diff -r be079f63985a -r a4ff6126ec76 naviengine/navienginebsp/ne1_tb/test/pci/t_pci.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/naviengine/navienginebsp/ne1_tb/test/pci/t_pci.cpp Wed Sep 15 13:26:16 2010 +0300 @@ -0,0 +1,1087 @@ +// Copyright (c) 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: +// This is a test for the PCI driver, so far implemented only on the +// Naviengine platform. It aims to test: +// -That known values of data in config and memory space, on a given +// device can be read as expected. +// -That data can be written and modified in config and memory space +// -PCI memory buffers mapped or allocated by the PCI driver work as +// expected. These are +// -DChunk created by PCI driver and accessible from PCI +// -DPlatHwChunk created by PCI driver and accessible from PCI +// -DChunk created externally, then mapped in to PCI memory space +// There are tests to: +// - Create and close each buffer. Heap checking ensures proper +// cleanup +// - Create and close multiple buffers from multiple threads. +// This is an SMP focused test to check that the implementation +// of the chunk manager and allocator in the driver are thread +// safe. The tests should pass without triggering any assertions in +// the driver's invariance checks. +// - Write to buffers from software, and read back via the +// system to PCI window, and vice-versa -- a loop-back test. +// This checks that PCI buffers are indeed accessible to PCI devices. +// +// The tests require several pieces of PSL specific information: +// - A TPciDevice containing the vendor and device IDs of a PCI device +// to use for testing. +// - TAddrSpaceTests which identify regions of a device's config and +// memory space with known values, or which are known to be writable. +// +// The test driver grants access to the PCI API with the following +// constructs: +// - TUserConfigSpace and TUserMemorySpace, derived from TUserPciSpace, +// which are user side equivalents of kernel-side objects allowing +// accesses of different sizes to a PCI device's config space or +// memory space. +// - RPciChunk which is derived from and RChunk and corresponds to +// a kernel-side DChunk, which in turn corresponds to a PCI chunk or +// buffer. The test driver uses these for all PCI chunk types (a +// "wrapper" DChunk is used to map the memory of a PCI DPlatHwChunk +// to user side). +// +// Known Issues: +// The test driver d_pci is intended to be platform independent but +// for now still contains some PSL specific information .eg the test +// info structure (which should really be passed up from the PSL) and +// the address and size of the system to pci window. For now the +// test driver code will remain in the Naviengine baseport directory. +// If the PCI driver is ever ported to a new platform this can be +// rectified. +// +// +// +#include +#define __E32TEST_EXTENSION__ +#include +#include "t_pci.h" +#include + +#define __E32TEST_EXTENSION__ +#include +#include +#include +#include +#include +#include +#include +#include + + +_LIT(KPciPanicCat, "test_thread.h"); + +static const TInt KPciHeapSize=0x2000; + +enum TPciPanicCode + { + EThreadCreateFailed + }; + +/** +A utility class for running functions in other threads/processes +*/ +class TTestRemote + { +public: + virtual TInt WaitForExitL() = 0; + virtual ~TTestRemote() + {} + + virtual void Rendezvous(TRequestStatus& aStatus) = 0; + +protected: + TTestRemote() + {} + + static TInt RunFunctor(TAny* aFunctor) + { + TFunctor& functor = *(TFunctor*)aFunctor; + functor(); + return KErrNone; + } + + TRequestStatus iLogonStatus; + static TInt iCount; + }; +TInt TTestRemote::iCount=0; + +class TTestThread : public TTestRemote + { +public: + TTestThread(const TDesC& aName, TThreadFunction aFn, TAny* aData, TBool aAutoResume=ETrue) + { + Init(aName, aFn, aData, aAutoResume); + } + + /** + Run aFunctor in another thread + */ + TTestThread(const TDesC& aName, TFunctor& aFunctor, TBool aAutoResume=ETrue) + { + Init(aName, RunFunctor, &aFunctor, aAutoResume); + } + + ~TTestThread() + { + //RTest::CloseHandleAndWaitForDestruction(iThread); + iThread.Close(); + } + + void Resume() + { + iThread.Resume(); + } + + /** + If thread exited normally, return its return code + Otherwise, leave with exit reason + */ + virtual TInt WaitForExitL() + { + User::WaitForRequest(iLogonStatus); + const TInt exitType = iThread.ExitType(); + const TInt exitReason = iThread.ExitReason(); + + __ASSERT_ALWAYS(exitType != EExitPending, User::Panic(_L("TTestThread"),0)); + + if(exitType != EExitKill) + User::Leave(exitReason); + + return exitReason; + } + + virtual void Rendezvous(TRequestStatus& aStatus) + { + iThread.Rendezvous(aStatus); + } + +private: + void Init(const TDesC& aName, TThreadFunction aFn, TAny* aData, TBool aAutoResume) + { + TKName name(aName); + name.AppendFormat(_L("-%d"), iCount++); + TInt r=iThread.Create(name, aFn, KDefaultStackSize, KPciHeapSize, KPciHeapSize, aData); + if(r!=KErrNone) + { + RDebug::Printf("RThread::Create failed, code=%d", r); + User::Panic(KPciPanicCat, EThreadCreateFailed); + } + + iThread.Logon(iLogonStatus); + __ASSERT_ALWAYS(iLogonStatus == KRequestPending, User::Panic(_L("TTestThread"),0)); + + if(aAutoResume) + iThread.Resume(); + } + + RThread iThread; + }; + +class CTest : public CBase, public TFunctor + { +public: + ~CTest() + { + iName.Close(); + } + + virtual void operator()() + { + RTest test(iName); + test.Start(iName); + for(TInt i=0; i testArray; + RPointerArray threadArray; + + for(TInt i=0; iWaitForExitL()); + if(leaveCode != KErrNone) + { + test.Printf(_L("Thread %d: Panic code:%d\n"), j, leaveCode); + test_KErrNone(leaveCode); + } + + if(r!=KErrNone) + { + test.Printf(_L("Thread Number %d\n"), j); + test_KErrNone(r); + } + } + + threadArray.ResetAndDestroy(); + threadArray.Close(); + + testArray.ResetAndDestroy(); + testArray.Close(); + } + +class RPci; +/** +Extends RChunk to hold the PCI address +associated with a chunk. +*/ +class RPciChunk: public RChunk + { +public: + TUint PciBase() + { + return iPciBaseAddr; + } + + /** + Return the PCI accessible size + */ + TInt Size() const + { + return iPciSize; + } + +private: + friend class RPci; + TUint iPciBaseAddr; + TInt iPciSize; //size of the region mapped into PCI + }; + +typedef TInt (RPci::*ChunkOpenFn)(RPciChunk&, TInt, TRequestStatus*); + +class RPci : public RBusLogicalChannel + { +public: + TInt Open(); + TInt GetTestInfo(TPciTestInfo& aTestInfo); + + TInt Open(const TPciDevice&); + + TUint AccessConfigSpace(const TUserConfigSpace& aCs); + TUint AccessMemorySpace(const TUserMemorySpace& aMs); + TInt OpenPciDChunk(RPciChunk& aPciChunk,TInt aPciChunkSize, TRequestStatus* aStatus=0); + TInt OpenPciPlatHwChunk(RPciChunk& aPciHwChunk,TInt aPciChunkSize, TRequestStatus* aStatus=0); + TInt OpenPciMappedChunk(RPciChunk& aPciMappedChunk,TInt aPciChunkSize, TRequestStatus* aStatus=0); + TInt OpenPciWindowChunk(RChunk& aPciWindowChunk); + TInt RunUnitTests(); +private: + TInt DoOpenPciChunk(RPciChunk& aPciChunk, TInt aPciChunkSize, TPciTestCmd aCmd, TRequestStatus* aStatus); + }; + +inline TInt RPci::Open() + { + return DoCreate(KPciLddFactory, TVersion(), KNullUnit, NULL, NULL); + } + +inline TInt RPci::Open(const TPciDevice& aDevice) + { + TPckgC devicePkg(aDevice); + return DoCreate(KPciLddFactory, TVersion(), KNullUnit, NULL, &devicePkg); + } + +inline TInt RPci::GetTestInfo(TPciTestInfo& aTestInfo) + { + TPckg info(aTestInfo); + return DoControl(EGetTestInfo, &info); + } + +inline TInt RPci::RunUnitTests() + { + return DoControl(ERunUnitTests); + } + +TUint RPci::AccessConfigSpace(const TUserConfigSpace& aCs) + { + TPckgC pkg(aCs); + return DoControl(EAccessConfigSpace, &pkg); + } + +TUint RPci::AccessMemorySpace(const TUserMemorySpace& aMs) + { + TPckgC pkg(aMs); + return DoControl(EAccessMemorySpace, &pkg); + } + +TInt RPci::OpenPciDChunk(RPciChunk& aPciChunk,TInt aPciChunkSize, TRequestStatus* aStatus) + { + return DoOpenPciChunk(aPciChunk, aPciChunkSize, EOpenPciDChunk, aStatus); + } + +TInt RPci::OpenPciPlatHwChunk(RPciChunk& aPciHwChunk,TInt aPciChunkSize, TRequestStatus* aStatus) + { + return DoOpenPciChunk(aPciHwChunk, aPciChunkSize, EOpenPciPlatHwChunk, aStatus); + } + +TInt RPci::OpenPciMappedChunk(RPciChunk& aPciMappedChunk,TInt aPciChunkSize, TRequestStatus* aStatus) + { + return DoOpenPciChunk(aPciMappedChunk, aPciChunkSize, EOpenPciMappedChunk, aStatus); + } + +TInt RPci::OpenPciWindowChunk(RChunk& aPciWindowChunk) + { + TUint chunkHandle = DoControl(EOpenPciWindowChunk); + return aPciWindowChunk.SetReturnedHandle(chunkHandle); + } + +TInt RPci::DoOpenPciChunk(RPciChunk& aPciChunk, TInt aPciChunkSize, TPciTestCmd aCmd, TRequestStatus* aStatus) + { + const TInt constPciChunkSize = aPciChunkSize; + TPciChunkCreateInfo info(constPciChunkSize, aPciChunk.iPciBaseAddr, aStatus); + TPckgC pkg(info); + + TUint chunkHandle = DoControl(aCmd, &pkg); + + const TInt r = aPciChunk.SetReturnedHandle(chunkHandle); + if(r == KErrNone) + { + aPciChunk.iPciSize = constPciChunkSize; + } + return r; + } + +TUserPciSpace::TUserPciSpace(RPci& aPci) + :iPci(&aPci) + {} + +TUserConfigSpace::TUserConfigSpace(RPci& aPci) + :TUserPciSpace(aPci) + {} + +TUint TUserConfigSpace::Call() + { + return iPci->AccessConfigSpace(*this); + } + +TUserPciSpace* TUserConfigSpace::Clone() const + { + return new TUserConfigSpace(*this); + } + +TUserMemorySpace::TUserMemorySpace(RPci& aPci, TInt aBarIndex) + :TUserPciSpace(aPci), iBarIndex(aBarIndex) + {} + +TUint TUserMemorySpace::Call() + { + return iPci->AccessMemorySpace(*this); + } + +TUserPciSpace* TUserMemorySpace::Clone() const + { + return new TUserMemorySpace(*this); + } + +/** +Test address allocator +*/ +TInt TestRunPciUnitTest(RPci& pci) + { + return pci.RunUnitTests(); + } + + +/** +Read from a defined address in memory or config space, compare against expected values. +8,16, and 32 bit accesses performed. + +@param aSpace Object gving access to either the config or memory space of a PCI device +@param aInfo Contains the address and expected value of a dword +*/ +void TestReadAddressSpace(TUserPciSpace& aSpace, const TPciTestInfo::TAddrSpaceTest& aInfo, RTest& test, TBool aVerbose=EFalse) + { + const TUint os = aInfo.iOffset; + //Iterate over different widths, and possible + //subfields of 32 bit word + for(TInt bitWidth=32; bitWidth>=8; bitWidth>>=1) + { + const TInt numberOfFields = (32/bitWidth); + for(TInt i=0; i< numberOfFields; i++) + { + const TInt extraByteOffset = i * (bitWidth >> 3); + const TInt byteOffset = os + extraByteOffset; + if(aVerbose) + test.Printf(_L("Access bitWidth=%d byte offset=%d\n"), bitWidth, byteOffset); + + const TUint expected = aInfo.Expected(bitWidth, byteOffset); + const TUint read = aSpace.Read(bitWidth, byteOffset); + if(aVerbose) + test.Printf(_L("expect 0x%08x, read 0x%08x\n"), expected, read); + test_Equal(expected, read); + } + } + } + +/** +Verify writes and modifications to a defined address in memory or config space. 8,16, and 32 bit +accesses performed. + +@param aSpace Object gving access to either the config or memory space of a PCI device +@param aInfo Contains the address of a (at least partially) writable dword +*/ +void TestWriteAddressSpace(TUserPciSpace& aSpace, TPciTestInfo::TAddrSpaceTest& aInfo, RTest& test, TBool aVerbose=EFalse) + { + const TUint original = aSpace.Read(32, aInfo.iOffset); + const TUint os = aInfo.iOffset; + TUint mask = ~aInfo.iReadOnlyMask; + + //The pattern will be truncated when used with bit widths + //less than 32. + const TUint initPattern = 0xFFFFFFFF; + + for(TInt bitWidth=32; bitWidth>=8; bitWidth>>=1) + { + const TUint pattern = initPattern >> (32-bitWidth); + const TInt numberOfFields = (32/bitWidth); + for(TInt i=0; i< numberOfFields; i++) + { + const TInt extraByteOffset = i * (bitWidth >> 3); + const TInt byteOffset = os + extraByteOffset; + if(aVerbose) + test.Printf(_L("Access bitWidth=%d byte offset=%d\n"), bitWidth, byteOffset); + //the full dword we expect + //currently assume that the unwritable bits will be 0 + const TUint writeExpect = (pattern << (bitWidth * i) ) & mask; + const TUint clearExpect = 0; + + //do write followed by clear + const TUint expect[] = {writeExpect, clearExpect}; + const TUint write[] = {pattern, 0}; + for(TInt n = 0; n < 2; n++) + { + aSpace.Write(bitWidth, byteOffset, write[n]); + TUint result = aSpace.Read(32, os); + + if(aVerbose) + test.Printf(_L("wrote 0x%08x, expect 0x%08x, read 0x%08x\n"), + write[n], expect[n], result); + test_Equal(expect[n], result); + } + + //test Modify calls. Set then clear pattern + TUint set[] = {pattern, 0}; + TUint clear[] = {0, pattern}; + + for(TInt m = 0; m < 2; m++) + { + aSpace.Modify(bitWidth, byteOffset, clear[m], set[m]); + TUint result = aSpace.Read(32, os); + + if(aVerbose) + test.Printf(_L("clear 0x%08x, set 0x%08x, expect 0x%08x, read 0x%08x\n"), clear[m], set[m], expect[m], result); + test_Equal(expect[m], result); + } + } + } + + //restore orginal value or we will not be able to access device + aSpace.Write(32, os, original); + } + + +/** +Verify that a PCI DChunk can be opened and closed from user side + +@param pci The RPci object to use +@param test The RTest object to use +@param aPciChunkSize The size of the DChunk which would be created +*/ +void TestOpenAndCloseDChunk(RPci& pci,RTest& test,TInt aPciChunkSize) + { + RPciChunk testPciDChunk; + + // Create and open Chunk + TRequestStatus status; + TInt r = pci.OpenPciDChunk(testPciDChunk,aPciChunkSize, &status); + test_KErrNone(r); + + test(testPciDChunk.IsWritable()); + test(testPciDChunk.IsReadable()); + + test.Printf(_L("PCI Chunk base = 0x%08x\n"), testPciDChunk.Base()); + test.Printf(_L("PCI Chunk size = %d\n"), testPciDChunk.Size()); + test.Printf(_L("PCI Address = 0x%08x\n"), testPciDChunk.PciBase()); + + //Close Chunk + test.Next(_L("Close PCI Chunk handle")); + + RTest::CloseHandleAndWaitForDestruction(testPciDChunk); + User::WaitForRequest(status); + } + +/** +Verify that a PCI PlatHwChunk can be opened and closed from user side + + +@param pci The RPci object to use +@param test The RTest object to use +@param aPciChunkSize The size of the PlatHwChunk which would be created +*/ +void TestOpenAndClosePciPlatHwChunk(RPci& pci,RTest& test,TInt aPciChunkSize) + { + RPciChunk testPciPlatHwChunk; + + // Create and open Chunk + TRequestStatus status; + TInt r = pci.OpenPciPlatHwChunk(testPciPlatHwChunk,aPciChunkSize, &status); + test_KErrNone(r); + + test(testPciPlatHwChunk.IsWritable()); + test(testPciPlatHwChunk.IsReadable()); + + test.Printf(_L("PCI Chunk base = 0x%08x\n"), testPciPlatHwChunk.Base()); + test.Printf(_L("PCI Chunk size = %d\n"), testPciPlatHwChunk.Size()); + test.Printf(_L("PCI Address = 0x%08x\n"), testPciPlatHwChunk.PciBase()); + + //Close Chunk + testPciPlatHwChunk.Close(); + User::WaitForRequest(status); + test.Next(_L("Closed PCI PlatHwChunk handle")); + } + +/** +Verify that pci-mapped DChunk can be opended and closed form user side + +@param pci The RPci object to use +@param test The RTest object to use +@param aPciChunkSize The size of the pci-mapped DChunk which would be created +*/ +void TestPciMapppedChunk(RPci& pci,RTest& test,TInt aPciChunkSize) + { + RPciChunk testPciMappedChunk; + + // Create and open Chunk + TRequestStatus status; + TInt r = pci.OpenPciMappedChunk(testPciMappedChunk,aPciChunkSize, &status); + test_KErrNone(r); + + test(testPciMappedChunk.IsWritable()); + test(testPciMappedChunk.IsReadable()); + + test.Printf(_L("PCI Chunk base = 0x%08x\n"), testPciMappedChunk.Base()); + test.Printf(_L("PCI Chunk size = %d\n"), testPciMappedChunk.Size()); + test.Printf(_L("PCI Address = 0x%08x\n"), testPciMappedChunk.PciBase()); + + //Close Chunk + testPciMappedChunk.Close(); + User::WaitForRequest(status); + test.Next(_L("Closed PCI Mapped Chunk handle")); + } + +/** +Verify that an RChunk can be open to grant access to the internal PCI window from the user side + +@param pci The RPci object to use +@param test The RTest object to use +*/ +void TestPciWindowChunk(RPci& pci,RTest& test) + { + RChunk testPciWindowChunk; + + // Create and open DChunk + TInt r = pci.OpenPciWindowChunk(testPciWindowChunk); + test_KErrNone(r); + + test(testPciWindowChunk.IsWritable()); + test(testPciWindowChunk.IsReadable()); + + test.Printf(_L("PCI Window Chunk base = 0x%08x\n"), testPciWindowChunk.Base()); + test.Printf(_L("PCI Window Chunk size = %d\n"), testPciWindowChunk.Size()); + + //Close Chunk + testPciWindowChunk.Close(); + test.Next(_L("Closed PCI Window Chunk handle")); + } + + +class CPciTest : public CTest + { +protected: + CPciTest(const TDesC& aName, TInt aIterations, RPci& aDevice) + : CTest(aName, aIterations), iDevice(aDevice) + {} + + RPci iDevice; + }; + +/** +Each instance of test will open a chunk, using the function specified in +the template argument, FUNC. + +The total number of chunks that can be opened by all instances is limited +by iMaxCount. + +All intances of the test will hold their chunk open until iMaxCount has +been reached. +*/ +template +class CPciOpenChunkTest : public CPciTest + { +public: + CPciOpenChunkTest(const TDesC& aName, TInt aIterations, RPci& aDevice, + RSemaphore aSemOpen, RSemaphore aSemClose, RFastLock aLock, TInt aMaxCount) + :CPciTest(aName, aIterations, aDevice), + iSemOpen(aSemOpen), iSemClose(aSemClose), iLock(aLock), iMaxCount(aMaxCount) + { + } + + virtual void RunTest() + { + RTest test(iName); + RPciChunk chunk; + + iSemOpen.Wait(); + TRequestStatus status; + const TInt chunkSize = 0x400; + //open chunk by calling FUNC + TInt r = ((iDevice).*(FUNC))(chunk, chunkSize, &status); + test_KErrNone(r); + + iLock.Wait(); + iOpenCount++; + test.Printf(_L("Opened chunk %d\n"), iOpenCount); + if(iOpenCount == iMaxCount) + { + test.Printf(_L("Opened=%d, max=%d: Allow chunks to close\n"), iOpenCount, iMaxCount); + //release all waiting threads + //plus 1 preincrement so this + //thread also passes + iSemClose.Signal(iOpenCount); + iOpenCount = 0; + } + iLock.Signal(); + + + iSemClose.Wait(); + chunk.Close(); + User::WaitForRequest(status); + + // permit another chunk to be opened + iSemOpen.Signal(); + test.Close(); + } + + virtual CTest* Clone() const + { + //make shallow copy + return new CPciOpenChunkTest(*this); + } + + +private: + RSemaphore& iSemOpen; ///!< Represents the number of available PCI mappings + RSemaphore& iSemClose; ///!< Represents the number of threads waiting to close their chunk + RFastLock& iLock; + static TInt iOpenCount; + const TInt iMaxCount; + }; + +template +TInt CPciOpenChunkTest::iOpenCount = 0; + + +/** +Test which will perform various reads from a PCI address +space (config or memory) and confirm that values are read +as expected +*/ +class CPciAddressSpaceRead : public CPciTest + { +public: + CPciAddressSpaceRead(const TDesC& aName, TInt aIterations, RPci& aDevice, + const TUserPciSpace& aSpace, const TPciTestInfo::TAddrSpaceTest& aInfo) + :CPciTest(aName, aIterations, aDevice), + iAddressSpace(aSpace.Clone()), iSpaceTestInfo(aInfo) + { + } + + CPciAddressSpaceRead(const CPciAddressSpaceRead& aOther) + :CPciTest(aOther)/* TODO-REVIEW have object-sliced aOther - is this ok?*/, + iAddressSpace(aOther.iAddressSpace->Clone()), iSpaceTestInfo(aOther.iSpaceTestInfo) + { + } + + virtual ~CPciAddressSpaceRead() + { + delete iAddressSpace; + } + + virtual void RunTest() + { + __UHEAP_MARK; + RTest test(iName); + TestReadAddressSpace(*iAddressSpace, iSpaceTestInfo, test); + test.Close(); + __UHEAP_MARKEND; + } + + virtual CTest* Clone() const + { + //make shallow copy + return new CPciAddressSpaceRead(*this); + } + +private: + TUserPciSpace* iAddressSpace; + const TPciTestInfo::TAddrSpaceTest& iSpaceTestInfo; + }; + +/** +For aBuffer, test writing to it then reading back from aWindow +then write via window and read back from chunk + +@param test The RTest object to use +@param aBuffer RChunk corresponding to a PCI accessible buffer +@param aWindow RChunk coressponding an appropriate System-to-PCI memory window +It is presumed to start at PCI address 0 +*/ +void DoLoopBackTest(RTest& test, RPciChunk aBuffer, RChunk aWindow) + { + test.Start(_L("Test accessing memory via PCI")); + + TUint8* const bufferBase = aBuffer.Base(); + const TUint bufferSize = aBuffer.Size(); + const TUint bufferPciBase = aBuffer.PciBase(); + + TUint8* const windowBase = aWindow.Base(); + const TUint windowSize = aWindow.Size(); + +#define PRINT(N) RDebug::Printf("%s = 0x%08x (%d)", #N, (N), (N)) + PRINT(bufferBase); + PRINT(bufferSize); + PRINT(bufferPciBase); + + PRINT(windowBase); + PRINT(windowSize); + +#undef PRINT + + //need to check that the end of the buffer + //is within the windowed region + test(bufferPciBase + bufferSize <= windowSize); + TUint8* const bufferBaseWithinWindow = windowBase + bufferPciBase; + + test.Next(_L("write chunk")); + for(TUint i = 0; i < bufferSize; ++i) + { + //each byte will hold its own offset modulo 256 + bufferBase[i] = (TUint8)i; + } + + test.Next(_L("read back via window")); + for(TUint j=0; j < bufferSize; ++j) + { + const TUint8 result = bufferBaseWithinWindow[j]; + test_Equal(j%256, result); + } + + //clear chunk + memclr(bufferBase, bufferSize); + test.Next(_L("write via window")); + for(TUint k=0; k < bufferSize; ++k) + { + //each byte will hold its own offset modulo 256 + bufferBaseWithinWindow[k] = (TUint8)k; + } + + test.Next(_L("read back from chunk")); + for(TUint l=0; l < bufferSize; ++l) + { + const TUint8 result = bufferBase[l]; + test_Equal(l%256, result); + } + + test.End(); + } + +/** +Take care of opening a chunk, running the test and closing +*/ +template +inline void LoopBackTest(RPci& aPci, RTest& test, RChunk& aWindow) + { + RPciChunk pciChunk; + const TInt chunkSize = 0x400; //1k + + //call the specified chunk opening function + TRequestStatus status; + TInt r = ((aPci).*(OPEN_FUNC))(pciChunk, chunkSize, &status); + test_KErrNone(r); + DoLoopBackTest(test, pciChunk, aWindow); + pciChunk.Close(); + User::WaitForRequest(status); + } + +/** +Run the loopback test for the 3 types of buffer supported by the PCI driver. +DChunk +DPlatChunk +Mapped In external memory +*/ +void TestLoopBack(RPci& aPci, RTest& test) + { + test.Next(_L("Open PCI window")); + RChunk window; + + TInt r = aPci.OpenPciWindowChunk(window); + test_KErrNone(r); + + test.Next(_L("DChunk")); + LoopBackTest<&RPci::OpenPciDChunk>(aPci, test, window); + + test.Next(_L("DPlatHwChunk")); + LoopBackTest<&RPci::OpenPciPlatHwChunk>(aPci, test, window); + + test.Next(_L("DChunk (mapped in)")); + LoopBackTest<&RPci::OpenPciMappedChunk>(aPci, test, window); + + window.Close(); + } +#ifndef __VC32__ //visual studio 6 doesn't approve of pointer to member function template parameters +/** +Run the CPciOpenChunkTest for each type of chunk. This function also creates (and destroys) the +necessary semaphores and locks. +CPciOpenChunkTest objects are run in multiple threads using MultipleTestRun(). + +@param aDevice Handle to the test driver +@param test RTest to use. +@param aBufferLimit The maximum number of buffers which can be opened simultaneously +*/ +void TestBufferOpenConcurrency(RPci& aDevice, RTest& test, TInt aBufferLimit) + { + RSemaphore semaphoreOpen; + RSemaphore semaphoreClose; + RFastLock lock; + + TInt r = semaphoreOpen.CreateLocal(aBufferLimit); + test_KErrNone(r); + + r = semaphoreClose.CreateLocal(0); + test_KErrNone(r); + + r = lock.CreateLocal(); + test_KErrNone(r); + + const TInt iterations = 3; + { + test.Printf(_L("Opening %d PCI DChunks in %d threads\n"), aBufferLimit, aBufferLimit); + CPciOpenChunkTest<&RPci::OpenPciDChunk> + dChunkTest(_L("Concurrent-DChunk"), iterations, aDevice, semaphoreOpen, semaphoreClose, lock, aBufferLimit); + + MultipleTestRun(test, dChunkTest, aBufferLimit); + } + + { + test.Printf(_L("Opening %d PCI DPlatHwChunks in %d threads\n"), aBufferLimit, aBufferLimit); + CPciOpenChunkTest<&RPci::OpenPciPlatHwChunk> + platChunkTest(_L("Concurrent-DPlatHwChunk"), iterations, aDevice, semaphoreOpen, semaphoreClose, lock, aBufferLimit); + + MultipleTestRun(test, platChunkTest, aBufferLimit); + } + + { + test.Printf(_L("Opening %d PCI Mapped chunks in %d threads\n"), aBufferLimit, aBufferLimit); + CPciOpenChunkTest<&RPci::OpenPciMappedChunk> + mappedChunkTest(_L("Concurrent-DChunk(mapped)"), iterations, aDevice, semaphoreOpen, semaphoreClose, lock, aBufferLimit); + + MultipleTestRun(test, mappedChunkTest, aBufferLimit); + } + + semaphoreOpen.Close(); + semaphoreClose.Close(); + lock.Close(); + } +#endif + +TInt E32Main() + { + __UHEAP_MARK; + + _LIT(KPci, "PCI"); + RTest test(KPci); + test.Start(_L("Running PCI tests\n")); + + TInt r = User::LoadLogicalDevice(KPciLdd); + + __KHEAP_MARK; + + if(r==KErrNotFound) + { + test.Printf(_L("No PCI system present - skipping test\n")); + return KErrNone; + } + if(r!=KErrNone && r!=KErrAlreadyExists) + { + test_KErrNone(r); + } + + test.Next(_L("Open non-existant device\n")); + RPci device; + TPciDevice unavailable; + r = device.Open(unavailable); + test_Equal(KErrNotFound, r); + + RPci pciInfo; + r = pciInfo.Open(); + test_KErrNone(r); + + test.Next(_L("Get test info from driver\n")); + TPciTestInfo info; + r = pciInfo.GetTestInfo(info); + test_KErrNone(r); + pciInfo.Close(); + + test.Next(_L("Open test device\n")); + r = device.Open(info.iDevice); + test_KErrNone(r); + + test.Next(_L("Run Device Unit Test\n")); + r=TestRunPciUnitTest(device); + test_KErrNone(r); + + test.Next(_L("Read config space\n")); + TUserConfigSpace cs(device); + TestReadAddressSpace(cs, info.iCfgSpaceRead, test); + + test.Next(_L("Write config space\n")); + TestWriteAddressSpace(cs, info.iCfgSpaceWrite, test); + + test.Next(_L("Read memory space\n")); + TUserMemorySpace ms(device, info.iMemSpaceIndex); + TestReadAddressSpace(ms, info.iMemSpaceRead, test); + + test.Next(_L("Modify memory space\n")); + TestWriteAddressSpace(ms, info.iMemSpaceWrite, test); + + { + const TInt addrSpaceThreadCount = 4; + const TInt iterations = 100; + test.Next(_L("Concurrent config space reads")); + CPciAddressSpaceRead cfgSpaceRead(_L("Cfg Space Read"), iterations, device, cs, info.iCfgSpaceRead); + MultipleTestRun(test, cfgSpaceRead, addrSpaceThreadCount); + + test.Next(_L("Concurrent memory space reads")); + CPciAddressSpaceRead memSpaceRead(_L("Memory Space Read"), iterations, device, ms, info.iMemSpaceRead); + MultipleTestRun(test, memSpaceRead, addrSpaceThreadCount); + } + + TInt testDChunkSize = 0x4000; + test.Next(_L("Open and Close DChunks\n")); + TestOpenAndCloseDChunk(device,test,testDChunkSize); + + TInt testDPlatChunkSize = 0x2000; + test.Next(_L("Open and Close PlatHwChunks\n")); + TestOpenAndClosePciPlatHwChunk(device,test,testDPlatChunkSize); + + //TestPciMapppedChunk() fails for sizes greater than 4K. + //The issue is that a block of externally mapped memory must be + //naturally alligned in order to be accessible to the PCI bus (ie + //an 8k buffer would have to start at an address which is a + //multiple of 8k. + // + //Now we could fix this for sure on the kernel side, by making + //sure we only commit correctly aligned memory into the chunk (as + //the pci driver itself does), + //However, by using a 4k chunk, we know this will be on a page + //boundary so the alignment is correct (assuming the page size + //isn't changed). + TInt testMapppedChunkSize = 0x1000; + test.Next(_L("Open and Close Pci Mappped Chunk\n")); + TestPciMapppedChunk(device,test,testMapppedChunkSize); + + test.Next(_L("Open and Close Pci Window Chunk\n")); + TestPciWindowChunk(device,test); + + const TInt numberOfThreads = info.iNumberOfBars; + test.Printf(_L("Open buffers concurrently, max supported = %d\n"), numberOfThreads); +#ifndef __VC32__ + TestBufferOpenConcurrency(device, test, numberOfThreads); +#else + test.Printf(_L("TestBufferOpenConcurrency not implemented for WINS"), numberOfThreads); +#endif + + test.Next(_L("Test loop back")); + TestLoopBack(device, test); + + device.Close(); + __KHEAP_MARKEND; + + r = User::FreeLogicalDevice(KPciLdd); + test_KErrNone(r); + + test.End(); + test.Close(); + + __UHEAP_MARKEND; + return KErrNone; + }