naviengine/navienginebsp/ne1_tb/test/pci/t_pci.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Wed, 15 Sep 2010 13:26:16 +0300
branchRCL_3
changeset 49 a4ff6126ec76
permissions -rw-r--r--
Revision: 201036 Kit: 201036

// 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 <e32std.h>
#define __E32TEST_EXTENSION__
#include <e32test.h>
#include "t_pci.h"
#include <assp/naviengine/pci.h>

#define __E32TEST_EXTENSION__
#include <e32test.h>
#include <e32svr.h>
#include <e32des8.h>
#include <e32des8_private.h>
#include <e32cmn.h>
#include <e32cmn_private.h>
#include <e32std.h>
#include <e32std_private.h>
    

_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<iIterations; i++)
			{
			test.Next(iName);
			RunTest();
			}
		test.End();
		}

	virtual void RunTest() = 0; 

	virtual CTest* Clone() const = 0;

	const TDesC& Name() const
		{
		return iName;
		}

protected:
	CTest(const TDesC& aName, TInt aIterations)
		:iIterations(aIterations)
		{
		iName.CreateL(aName);
		}


	
	CTest(const CTest& aOther)
		:iIterations(aOther.iIterations)
		{
		iName.CreateL(aOther.iName);
		}

	//It would be useful to have an RTest member, but this can't be
	//initialised untill the new thread is running as it will refer to
	//the creating thread
	RBuf iName;
	const TInt iIterations; 
	};

/**
Make aNumberOfThreads copies of aTest and run
each in its own thread

@param test Reference to test object
@param aTest Referance
*/
void MultipleTestRun(RTest& test, const CTest& aTest, TInt aNumberOfThreads)
	{
	RPointerArray<CTest> testArray;
	RPointerArray<TTestThread> threadArray;

	for(TInt i=0; i<aNumberOfThreads; i++)
		{		
		test.Printf(_L("Create test thread"));
		CTest* newTest = aTest.Clone();
		test_NotNull(newTest);

		TTestThread* thread = new TTestThread(aTest.Name(), *newTest);
		test_NotNull(thread);

		threadArray.AppendL(thread);
		testArray.AppendL(newTest);
		}

	const TInt count = threadArray.Count();
	for(TInt j=0; j<count; j++)
		{
		TTestThread* thread = threadArray[j];
		
		TInt r = KErrNone;
		TRAPD(leaveCode, r = thread->WaitForExitL());
		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<TPciDevice> devicePkg(aDevice);
	return DoCreate(KPciLddFactory, TVersion(), KNullUnit, NULL, &devicePkg);
	}

inline TInt RPci::GetTestInfo(TPciTestInfo& aTestInfo)
	{
	TPckg<TPciTestInfo> info(aTestInfo);
	return DoControl(EGetTestInfo, &info);
	}

inline TInt RPci::RunUnitTests()
	{
	return DoControl(ERunUnitTests);
	}

TUint RPci::AccessConfigSpace(const TUserConfigSpace& aCs)
	{
	TPckgC<TUserConfigSpace> pkg(aCs);
	return DoControl(EAccessConfigSpace, &pkg);
	}

TUint RPci::AccessMemorySpace(const TUserMemorySpace& aMs)
	{
	TPckgC<TUserMemorySpace> 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<TPciChunkCreateInfo> 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<ChunkOpenFn FUNC>
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<ChunkOpenFn FUNC>
TInt CPciOpenChunkTest<FUNC>::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<ChunkOpenFn OPEN_FUNC>
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;
	}