# HG changeset patch # User Dremov Kirill (Nokia-D-MSW/Tampere) # Date 1284546376 -10800 # Node ID a4ff6126ec76acc5696cee75d0c1ee4d273ccd6b # Parent be079f63985addec6de169a43d5c890dcd88f2a1 Revision: 201036 Kit: 201036 diff -r be079f63985a -r a4ff6126ec76 naviengine/navienginebsp/ne1_tb/test/pci/d_pci.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/naviengine/navienginebsp/ne1_tb/test/pci/d_pci.cpp Wed Sep 15 13:26:16 2010 +0300 @@ -0,0 +1,882 @@ +/* +* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This material, including documentation and any related computer +* programs, is protected by copyright controlled by Nokia. All +* rights are reserved. Copying, including reproducing, storing +* adapting or translating, any or all of this material requires the +* prior written consent of Nokia. This material also contains +* confidential information which may not be disclosed to others +* without the prior written consent of Nokia. +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: Device-driver for naviEngine PCI testing +* +*/ + +#include +#include +#include +#include +#include +#include "allocator.h" +#include "pci-ne.h" +#include "pci_priv.h" +#include +#include "t_pci.h" +#include "../../naviengine_assp/naviengine_pci.h" + +#define TEST(X) __NK_ASSERT_ALWAYS(X) +#define TEST_KERRNONE(X) if((X) !=KErrNone) {\ + Kern::Printf("Assertion Failed X=%d", (X)); FAULT();} + +#define FUNC_LOG() __KTRACE_OPT(KPCI, Kern::Printf(__PRETTY_FUNCTION__)) + + +void TestAllocator(); + +/** +So that the test app can get notification +when PCI DChunks' cleanup operation has run +we will replace their cleanup object with this +one, which will call the original object's destroy +function as well completing a notification +*/ +class TPciCleanupWrapper : public TChunkCleanup + { +public: + + ~TPciCleanupWrapper() + { + delete iOriginal; + Kern::DestroyClientRequest(iClientRequest); + iClient->Close(NULL); + } + + static TPciCleanupWrapper* Create(TRequestStatus* aRequestStatus) + { + __KTRACE_OPT(KPCI, Kern::Printf("TPciCleanupWrapper::Create aRequestStatus=0x%08x", aRequestStatus)); + TClientRequest* request = NULL; + TInt r = Kern::CreateClientRequest(request); + if(r != KErrNone) + { + __KTRACE_OPT(KPCI, Kern::Printf("TPciCleanupWrapper::Create Failed to create client request r=%d", r)); + return NULL; + } + + r = request->SetStatus(aRequestStatus); + if(r != KErrNone) + { + __KTRACE_OPT(KPCI, Kern::Printf("TPciCleanupWrapper::Create Failed to set status r=%d", r)); + Kern::DestroyClientRequest(request); + return NULL; + } + + return new TPciCleanupWrapper(request); + } + + /** + Insert the cleanup object into aChunk, remembering + the original one + + @param aChunk a chunk known to have TChunkCleanup derived cleanup object + */ + void Insert(DChunk* aChunk) + { + __KTRACE_OPT(KPCI, Kern::Printf("TPciCleanupWrapper::Insert aChunk=0x%08x", aChunk)); + __NK_ASSERT_DEBUG(aChunk); + __KTRACE_OPT(KPCI, Kern::Printf("TPciCleanupWrapper replace 0x%08x with 0x%08x", aChunk->iDestroyedDfc, this)); + iOriginal = static_cast(aChunk->iDestroyedDfc); + + __NK_ASSERT_DEBUG(iOriginal); + aChunk->iDestroyedDfc = this; + } + + /** + Run the original object's destroy method + then notify client + */ + void Destroy() + { + __KTRACE_OPT(KPCI, Kern::Printf("TPciCleanupWrapper::Destroy\n")); + iOriginal->Destroy(); + + __NK_ASSERT_ALWAYS(iClientRequest->IsReady()); + Kern::QueueRequestComplete(iClient, iClientRequest, KErrNone); + } + +private: + TPciCleanupWrapper(TClientRequest* aRequest) + :TChunkCleanup(), iOriginal(NULL), iClientRequest(aRequest), iClient(&Kern::CurrentThread()) + { + __ASSERT_CRITICAL; + iClient->Open(); //don't allow thread object to be destroyed before we signal + } + + + TChunkCleanup* iOriginal; + TClientRequest* iClientRequest; + DThread* const iClient; + }; + + + + +//This information will come from the pci driver +//if this code is ever made generic +TPciTestInfo KTestInfo = + { + TPciDevice(0x1033, 0x35, 0), + TPciTestInfo::TAddrSpaceTest(0, 0x00351033, 0), + TPciTestInfo::TAddrSpaceTest(KPciBar0, 0, 0xFFF), + 0, + TPciTestInfo::TAddrSpaceTest(0x34, 0x2EDF, 0), + TPciTestInfo::TAddrSpaceTest(0x20, 0, 0xF), + KNeBridgeNumberOfBars + }; + +/** +Class for a DChunk to remove a DPlatHwChunk chunk +*/ +class TPciPlatChunkCleanup : public TChunkCleanup + { +public: + TPciPlatChunkCleanup(TInt aPciFunction, DPlatChunkHw* aPciPlatChunk); + virtual void Destroy(); +public: + TInt iPciFunction; + DPlatChunkHw* iPciChunk; + }; + +TPciPlatChunkCleanup::TPciPlatChunkCleanup(TInt aPciFunction, DPlatChunkHw* aPciPlatChunk) + : TChunkCleanup(), iPciFunction(aPciFunction), iPciChunk(aPciPlatChunk) + { + } + +void TPciPlatChunkCleanup::Destroy() + { + __KTRACE_OPT(KPCI, Kern::Printf("SHAREDCHUNK ChunkDestroyed DFC\n")); + TInt r = Pci::RemoveChunk(iPciFunction, iPciChunk); + __NK_ASSERT_ALWAYS(r==KErrNone); + } + +/** +Cleanup class to remove the mapping for an externally +mapped chunk +*/ +class TPciMappedChunkCleanup : public TChunkCleanup + { +public: + TPciMappedChunkCleanup (TInt aPciFunction,TUint32 aPhysicalAddress); + virtual void Destroy(); +public: + TInt iPciFunction; + TUint32 iPhysicalAddress; + }; + +TPciMappedChunkCleanup::TPciMappedChunkCleanup(TInt aPciFunction,TUint32 aPhysicalAddress) + : TChunkCleanup(), iPciFunction(aPciFunction),iPhysicalAddress(aPhysicalAddress) + { + } + +void TPciMappedChunkCleanup::Destroy() + { + //remove mapping + TInt r = Pci::RemoveMapping(iPciFunction, iPhysicalAddress); + __NK_ASSERT_ALWAYS(r==KErrNone); + __KTRACE_OPT(KPCI, Kern::Printf("MAPPING REMOVED ChunkDestroyed DFC\n")); + } + +class DPciTestChannel : public DLogicalChannelBase + { +public: + DPciTestChannel(); + virtual ~DPciTestChannel(); + TInt DoCreate(TInt aUnit, const TDesC8* aInfo, const TVersion& aVer); + virtual TInt Request(TInt aFunction, TAny* a1, TAny* a2); + +private: + TInt OpenPciDChunk(TUint32& aPciAddr,TInt aPciChunkSize, TRequestStatus* aStatus); + TInt OpenPciPlatHwChunk(TUint32& aPciAddr,TInt aPciChunkSize, TRequestStatus* aStatus); + TInt OpenPciMappedChunk(TUint32& aPciAddr,TInt aPciChunkSize, TRequestStatus* aStatus); + TInt CreateSharedChunk(TInt aPciChunkSize, TUint32 aAttributes, DChunk*& aChunk, TLinAddr& aVirt, TPhysAddr& aPhysicalAddress); + TInt OpenPciWindowChunk(); + void RunUnitTests(); + +private: + const TPciTestInfo& iTestInfo; + TInt iFunction; ///< PCI function number this channel is associated with + }; + +DPciTestChannel::DPciTestChannel() + : iTestInfo(KTestInfo), iFunction(-1) + { + FUNC_LOG(); + } + +DPciTestChannel::~DPciTestChannel() + { + FUNC_LOG(); + } + +TInt DPciTestChannel::DoCreate(TInt aUnit, const TDesC8* aInfo, const TVersion& aVer) + { + if(aInfo == NULL) + return KErrNone; //Not a device specific channel + + TPciDevice dev; + TPckg devPckg(dev); + TInt r = Kern::ThreadDesRead(&Kern::CurrentThread(), aInfo, devPckg, 0, KChunkShiftBy0); + if(r != KErrNone) + return r; + + NKern::ThreadEnterCS(); + RArray indicies; + r = Pci::Probe(indicies, dev.iVendorId, dev.iDeviceId); + + if((KErrNone == r) && (dev.iInstance < indicies.Count())) + { + iFunction = indicies[dev.iInstance]; + } + else + { + r = KErrNotFound; + } + + indicies.Close(); + NKern::ThreadLeaveCS(); + return r; + } + +TInt DPciTestChannel::Request(TInt aFunction, TAny* a1, TAny* a2) + { + switch (aFunction) + { + case EGetTestInfo: + { + TDes8& dest(*(TDes8*)a1); + TPckgC info(iTestInfo); + Kern::KUDesPut(dest, info); + return KErrNone; + } + case EAccessConfigSpace: + { + TPckgBuf pckg; + Kern::KUDesGet(pckg, *reinterpret_cast(a1)); + + TAddrSpace* configSpace = Pci::GetConfigSpace(iFunction); + if(configSpace == NULL) + { + Kern::PanicCurrentThread(KPciTest, KErrGeneral); + return KErrGeneral; + } + return pckg().KRun(*configSpace); + } + case EAccessMemorySpace: + { + TPckgBuf pckg; + Kern::KUDesGet(pckg, *reinterpret_cast(a1)); + + TAddrSpace* memSpace = Pci::GetMemorySpace(iFunction, pckg().BarIndex()); + if(memSpace == NULL) + { + Kern::PanicCurrentThread(KPciTest, KErrGeneral); + return KErrGeneral; + } + return pckg().KRun(*memSpace); + } + case EOpenPciWindowChunk: + { + TInt rHandle = 0; + rHandle = OpenPciWindowChunk(); + return rHandle; + } + case EOpenPciDChunk: //Fall-through + case EOpenPciPlatHwChunk: + case EOpenPciMappedChunk: + { + TPckgBuf pckg; + Kern::KUDesGet(pckg, *reinterpret_cast(a1)); + + TUint32 pciAddr; + TInt rHandle = 0; + switch (aFunction) + { + case EOpenPciDChunk: + { + rHandle = OpenPciDChunk(pciAddr, pckg().iSize, pckg().iStatus); + break; + } + case EOpenPciPlatHwChunk: + { + rHandle = OpenPciPlatHwChunk(pciAddr, pckg().iSize, pckg().iStatus); + break; + } + case EOpenPciMappedChunk: + { + rHandle = OpenPciMappedChunk(pciAddr, pckg().iSize, pckg().iStatus); + break; + } + default: + { + FAULT(); + } + } + //write back PCI address to user + umemput(pckg().iPciAddress,&pciAddr,sizeof(pciAddr)); + return rHandle; + } + case ERunUnitTests: + { + RunUnitTests(); + return KErrNone; + } + default: + return KErrNotSupported; + } + } + +/** +This function runs tests for the address allocator +*/ +void DPciTestChannel::RunUnitTests() +{ + // Enter critical section + NKern::ThreadEnterCS(); + + TestAllocator(); + + // Finished + NKern::ThreadLeaveCS(); +} + + +/** +This function creates and opens a PCI DChunk and returns the PCI addresss +@param aPciAddr on return contains the pci address +@param aPciChunkSize contains the size of the PCI DChunk which is to be created +*/ +TInt DPciTestChannel::OpenPciDChunk(TUint32& aPciAddr,TInt aPciChunkSize, TRequestStatus* aStatus) +{ + //Chunk Attributes + TChunkCreateInfo aInfo; + aInfo.iType = TChunkCreateInfo::ESharedKernelMultiple; + aInfo.iMapAttr = EMapAttrSupRw|EMapAttrFullyBlocking; + aInfo.iOwnsMemory = EFalse; // We'll be using our own devices memory + + DChunk* pciChunk; + pciChunk=NULL; + + // Enter critical section + NKern::ThreadEnterCS(); + + //Create DChunk + TInt r = Pci::CreateChunk(iFunction, pciChunk, aInfo,0,aPciChunkSize,aPciAddr); + if(r!=KErrNone) + { + // Failed to create DChunk + __KTRACE_OPT(KPCI,Kern::Printf("Failed to create DChunk: Error code is=%d", r) ); + NKern::ThreadLeaveCS(); // Finished + return r; + } + else + { + TInt rHandle = KErrGeneral; + if(aStatus) + { + TPciCleanupWrapper* wrapper = TPciCleanupWrapper::Create(aStatus); + if(wrapper == NULL) + { + __KTRACE_OPT(KPCI,Kern::Printf("Creation of TPciCleanupWrapper failed")); + goto End; + } + wrapper->Insert(pciChunk); + } + + __KTRACE_OPT(KPCI,Kern::Printf("Created DChunk: PCI_ADDRESS=0x%08x",aPciAddr)); + rHandle = Kern::MakeHandleAndOpen(NULL, pciChunk);//Get DChunk handle + +End: + pciChunk->Close(NULL); // Close DChunk + NKern::ThreadLeaveCS(); // Finished + return rHandle; + } +} + +/** +This function creates and opens a PCI DPlatChunk and returns the PCI addresss. +A DPlatChunk is intially created and then a DChunk is set to point to the same +memory as the DPlatChunk.This is done so that it can be accessed on the user side. +@param aPciAddr on return contains the pci address +@param aPciChunkSize contains the size of the PCI PlatHwChunk which is to be created +*/ +TInt DPciTestChannel::OpenPciPlatHwChunk(TUint32& aPciAddr,TInt aPciChunkSize, TRequestStatus* aStatus) +{ + TUint32 pciPhysicalAddr; + TUint32 pciChunkMapAttr; + TLinAddr pciChunkKernelAddr; + + DPlatChunkHw* pciPlatChunk; + pciPlatChunk=NULL; + + // Enter critical section + NKern::ThreadEnterCS(); + + //Create DPlatChunkHw + TInt r = Pci::CreateChunk(iFunction,pciPlatChunk,aPciChunkSize,(EMapAttrSupRw|EMapAttrFullyBlocking),aPciAddr); + if(r!=KErrNone) + { + // Failed to create DPlatChunkHw + __KTRACE_OPT(KPCI,Kern::Printf("Failed to create DPlatChunkHw chunk: Error code is=%d", r)); + NKern::ThreadLeaveCS(); // Finished + return r; + } + + //Get physical addresss + pciPhysicalAddr = pciPlatChunk->PhysicalAddress(); + + // Create DChunk cleanup object + TPciPlatChunkCleanup* cleanup = new TPciPlatChunkCleanup(iFunction, pciPlatChunk); + if(!cleanup) + { + pciPlatChunk->Close(NULL); //close pciPlatChunk + NKern::ThreadLeaveCS(); + return KErrNoMemory; + } + + //Chunk Attributes for DChunk + TChunkCreateInfo chunkinfo; + chunkinfo.iType = TChunkCreateInfo::ESharedKernelMultiple; + chunkinfo.iMaxSize = 0x4000; + chunkinfo.iMapAttr = EMapAttrSupRw|EMapAttrFullyBlocking; // No caching + chunkinfo.iOwnsMemory = EFalse; // Use memory from system's free pool + chunkinfo.iDestroyedDfc = cleanup; + + DChunk* pciDChunk; + + //Create DChunk + r = Kern::ChunkCreate(chunkinfo, pciDChunk, pciChunkKernelAddr, pciChunkMapAttr); + if(r!=KErrNone) + { + pciPlatChunk->Close(NULL); //close pciPlatChunk + delete cleanup; + NKern::ThreadLeaveCS(); + return r; + } + + pciPlatChunk=NULL; // pciDChunk now owns chunk + + if(aStatus) + { + TPciCleanupWrapper* wrapper = TPciCleanupWrapper::Create(aStatus); + if(wrapper == NULL) + { + pciDChunk->Close(NULL); // Close pciDChunk + NKern::ThreadLeaveCS(); // Finished + return KErrGeneral; + } + wrapper->Insert(pciDChunk); + } + + //Commit memory to a DChunk using DPlatChunkHw physical address + r = Kern::ChunkCommitPhysical(pciDChunk,0,aPciChunkSize,pciPhysicalAddr); + if(r!=KErrNone) + { + // Failed to commit memory + Kern::Printf("Commit failed: Error code is=%d", r); + __KTRACE_OPT(KPCI,Kern::Printf("Commit failed: Error code is=%d", r)); + + // Close chunk, which will then get deleted at some point + Kern::ChunkClose(pciDChunk); + NKern::ThreadLeaveCS(); + return r; + } + + //Close pciPlatChunk using pciDChunk as pciDChunk now owns it + const TInt rHandle = Kern::MakeHandleAndOpen(NULL, pciDChunk); //Get DChunk handle + pciDChunk->Close(NULL); // Close pciDChunk + NKern::ThreadLeaveCS(); // Finished + return rHandle; +} + +/** +This function creates and opens a PCI mapped DChunk and returns the PCI addresss +@param aPciAddr on return contains the pci address +@param aPciChunkSize contains the size of the PCI DChunk which is to be created +*/ +TInt DPciTestChannel::OpenPciMappedChunk(TUint32& aPciAddr,TInt aPciChunkSize, TRequestStatus* aStatus) +{ + TLinAddr virt=NULL; + TPhysAddr physicalAddress=NULL; + DChunk* pciChunk=NULL; + TUint32 pciAttributes=EMapAttrSupRw|EMapAttrFullyBlocking; + + // Enter critical section + NKern::ThreadEnterCS(); + + //create DChunk + TInt r = CreateSharedChunk(aPciChunkSize, pciAttributes, pciChunk, virt, physicalAddress); + if(r!=KErrNone) + { + __KTRACE_OPT(KPCI,Kern::Printf("Create shared Chunk failed: Error code is=%d", r)); + return r; + } + + __NK_ASSERT_ALWAYS(pciChunk); + + //create mapping + r=Pci::CreateMapping(iFunction, physicalAddress, aPciChunkSize, aPciAddr); + if(r!=KErrNone) + { + pciChunk->Close(NULL); + __KTRACE_OPT(KPCI,Kern::Printf("Create mapping failed: Error code is=%d", r)); + return r; + } + + + // Create DChunk cleanup object + TPciMappedChunkCleanup* cleanup = new TPciMappedChunkCleanup(iFunction, physicalAddress); + if(!cleanup) + { + pciChunk->Close(NULL); + NKern::ThreadLeaveCS(); + return KErrNoMemory; + } + + //must add the cleanup dfc to the chunk after creation + //since the cleanup parameters aren't known + //till after creating it and allocating memory to it + pciChunk->iDestroyedDfc = cleanup; + + if(aStatus) + { + TPciCleanupWrapper* wrapper = TPciCleanupWrapper::Create(aStatus); + if(wrapper == NULL) + { + pciChunk->Close(NULL); // Close pciDChunk + NKern::ThreadLeaveCS(); + return KErrGeneral; + } + wrapper->Insert(pciChunk); + } + + //Get DChunk handle + const TInt rHandle = Kern::MakeHandleAndOpen(NULL, pciChunk); + + // Close DChunk + pciChunk->Close(NULL); + + // Finished + NKern::ThreadLeaveCS(); + return rHandle; +} + +/** +This function creates and opens a PCI Window Chunk and returns the PCI Window addresss +@param aPciChunkSize contains the size of the PCI Window DChunk which is to be created +*/ +TInt DPciTestChannel::OpenPciWindowChunk() +{ + TUint32 pciChunkMapAttr; + TLinAddr pciChunkKernelAddr=NULL; + DChunk* pciWindowChunk=NULL; + + //Chunk Attributes for DChunk + TChunkCreateInfo chunkinfo; + chunkinfo.iType = TChunkCreateInfo::ESharedKernelMultiple; + chunkinfo.iMaxSize = 0x2000; + chunkinfo.iMapAttr = EMapAttrSupRw|EMapAttrFullyBlocking; // No caching + chunkinfo.iOwnsMemory = EFalse; // Use memory from system's free pool + + // Enter critical section + NKern::ThreadEnterCS(); + + //Create shared chunk for PCI window + TInt r = Kern::ChunkCreate(chunkinfo, pciWindowChunk, pciChunkKernelAddr, pciChunkMapAttr); + if(r!=KErrNone) + { + // Failed to create DChunk + __KTRACE_OPT(KPCI,Kern::Printf("Failed to create DChunk: Error code is=%d", r) ); + NKern::ThreadLeaveCS(); + return r; + } + + //This address is PSL specific. This will have to be changed + //if d_pci.cpp is ever made generic + TUint32 pciPhysicalAddr = KHwUSBHPhys; // Internal PCI window address + + //Commit memory to a DChunk using Internal PCI window address + r = Kern::ChunkCommitPhysical(pciWindowChunk,0,KHwUSBHInternalPciWindowSize, pciPhysicalAddr); + if(r!=KErrNone) + { + // Failed to commit memory + Kern::Printf("Commit failed: Error code is=%d", r); + __KTRACE_OPT(KPCI,Kern::Printf("Commit failed: Error code is=%d", r)); + + // Close chunk, which will then get deleted at some point + Kern::ChunkClose(pciWindowChunk); + NKern::ThreadLeaveCS(); + return r; + } + + //Close pciPlatChunk using pciDChunk as pciDChunk now owns it + const TInt rHandle = Kern::MakeHandleAndOpen(NULL, pciWindowChunk); //Get PCI Window DChunk handle + pciWindowChunk->Close(NULL); // Close pci window chunk + NKern::ThreadLeaveCS(); // Finished + return rHandle; +} + +/** +This function creates and opens a shared chunk. The chunk is then commited to a contiguous memory +@param aPciChunkSize contains the size of the PCI DChunk which is to be created +@param aAttributes on return, this is set to the mmu mapping attributes used for the chunk +@param aChunk on return, a reference to the shared chunk +@param aVirt on return, this is set to the virtual address shared chunk +@param aPhysicalAddress on return, this is set to the physical address of the first page of memory + which was committed. +*/ +TInt DPciTestChannel::CreateSharedChunk(TInt aPciChunkSize, TUint32 aAttributes, DChunk*& aChunk, TLinAddr& aVirt, TPhysAddr& aPhysicalAddress) +{ + __NK_ASSERT_DEBUG(aChunk==NULL); + aPciChunkSize = Kern::RoundToPageSize(aPciChunkSize); + DChunk* pC=NULL; + + // Enter critical section + NKern::ThreadEnterCS(); + + //Chunk Attributes for DChunk + TChunkCreateInfo info; + info.iType=TChunkCreateInfo::ESharedKernelSingle; + info.iMaxSize=aPciChunkSize; + info.iMapAttr=aAttributes; + info.iOwnsMemory=ETrue; + + //Create DChunk + TInt r=Kern::ChunkCreate(info, pC, aVirt, aAttributes); + if(r!=KErrNone) + { + NKern::ThreadLeaveCS(); + return r; + } + //Commit DChunk to Contiguous memory + r = Kern::ChunkCommitContiguous(pC, 0, aPciChunkSize, aPhysicalAddress); + if(r==KErrNone) + { + aChunk=pC; + } + else + { + Kern::ChunkClose(pC); + __KTRACE_OPT(KPCI,Kern::Printf("Commit DChunk to Contiguous memory Failed : Error code is=%d",r)); + return r; + } + + NKern::ThreadLeaveCS(); // Finished + __KTRACE_OPT(KPCI, Kern::Printf("Created SC: size=0x%08x, virtual= 0x%08x, phys=0x%08x", aPciChunkSize, aVirt, aPhysicalAddress)); + return r; +} + +class DPciDevice : public DLogicalDevice + { +public: + DPciDevice(); + ~DPciDevice(); + TInt Install(); + void GetCaps(TDes8& aDes) const; + TInt Create(DLogicalChannelBase*& aChannel); + }; + +DPciDevice::DPciDevice() + { + FUNC_LOG(); + } + +DPciDevice::~DPciDevice() + { + FUNC_LOG(); + } + +TInt DPciDevice::Install() + { + return SetName(&KPciLddFactory); + } + +void DPciDevice::GetCaps(TDes8&) const + { + } + +TInt DPciDevice::Create(DLogicalChannelBase*& aChannel) + { + aChannel = new DPciTestChannel; + return aChannel ? KErrNone : KErrNoMemory; + } + +/**************************************** +TUserPciSpace +*/ + +/** +Decides what action to run based on contents of class +*/ +TUint TUserPciSpace::KRun(TAddrSpace& aAddrSp) + { + + //this could be reworked as a function pointer + //table, but this might be clearer + switch(iBitWidth) + { + case 8: + { + switch(iOperation) + { + case ERead: + { + return aAddrSp.Read8(iOffset); + } + case EWrite: + { + aAddrSp.Write8(iOffset, iWriteValue); + return KErrNone; + } + case EModify: + { + aAddrSp.Modify8(iOffset, iClearMask, iSetMask); + return KErrNone; + } + default: + { + Kern::PanicCurrentThread(KPciTest, KErrNotReady); + } + } + } + case 16: + { + switch(iOperation) + { + case ERead: + { + return aAddrSp.Read16(iOffset); + } + case EWrite: + { + aAddrSp.Write16(iOffset, iWriteValue); + return KErrNone; + } + case EModify: + { + aAddrSp.Modify16(iOffset, iClearMask, iSetMask); + return KErrNone; + } + default: + { + Kern::PanicCurrentThread(KPciTest, KErrNotReady); + } + } + } + case 32: + { + switch(iOperation) + { + case ERead: + { + return aAddrSp.Read32(iOffset); + } + case EWrite: + { + aAddrSp.Write32(iOffset, iWriteValue); + return KErrNone; + } + case EModify: + { + aAddrSp.Modify32(iOffset, iClearMask, iSetMask); + return KErrNone; + } + default: + { + Kern::PanicCurrentThread(KPciTest, KErrNotReady); + } + } + } + default: + { + Kern::PanicCurrentThread(KPciTest, KErrArgument); + } + + } + + //unreachable return + return KMaxTUint; + } + +//stub implementation for kernel side +TUint TUserConfigSpace::Call() + { + FAULT(); + return 0; + } + +TUserPciSpace* TUserConfigSpace::Clone() const + { + FAULT(); + return 0; + } + +//stub implementation for kernel side +TUint TUserMemorySpace::Call() + { + FAULT(); + return 0; + } + +TUserPciSpace* TUserMemorySpace::Clone() const + { + FAULT(); + return 0; + } + +void TestAllocator() + { + __KTRACE_OPT(KPCI, Kern::Printf("Testing address allocator")); + TAddressAllocator allocator(0x80000000); //2 GB + TLinAddr rcvdAddr=NULL; + TEST_KERRNONE(allocator.Allocate(rcvdAddr,0x10) ); + TEST(0x0 ==rcvdAddr); + TEST_KERRNONE(allocator.Allocate(rcvdAddr,0x100) ); + TEST(0x100 ==rcvdAddr); + TEST_KERRNONE(allocator.Allocate(rcvdAddr,0x10) ); + TEST(0x10 ==rcvdAddr); + TEST_KERRNONE(allocator.Allocate(rcvdAddr,0x10) ); + TEST(0x20 ==rcvdAddr); + //test deallocating + TEST_KERRNONE(allocator.DeAllocate(0x0)); + TEST_KERRNONE(allocator.Allocate(rcvdAddr,0x10) ); + TEST(0x000 ==rcvdAddr); + + TEST_KERRNONE(allocator.DeAllocate(0x100)); + TEST_KERRNONE(allocator.Allocate(rcvdAddr,0x100) ); + TEST(0x100 ==rcvdAddr); + + TEST_KERRNONE(allocator.DeAllocate(0x10)); + TEST_KERRNONE(allocator.Allocate(rcvdAddr,0x10) ); + TEST(0x10 ==rcvdAddr); + + TEST_KERRNONE(allocator.DeAllocate(0x20)); + TEST_KERRNONE(allocator.Allocate(rcvdAddr,0x20) ); + TEST(0x20 ==rcvdAddr); + + TEST(allocator.DeAllocate(0x40)==KErrNotFound); + TEST_KERRNONE(allocator.DeAllocate(0x100)); + TEST_KERRNONE(allocator.DeAllocate(0x20)); + TEST_KERRNONE(allocator.DeAllocate(0x0)); + TEST_KERRNONE(allocator.DeAllocate(0x10)); + } + + +DECLARE_STANDARD_LDD() + { + return new DPciDevice; + } 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; + } diff -r be079f63985a -r a4ff6126ec76 naviengine/navienginebsp/ne1_tb/test/pci/t_pci.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/naviengine/navienginebsp/ne1_tb/test/pci/t_pci.h Wed Sep 15 13:26:16 2010 +0300 @@ -0,0 +1,254 @@ +// 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 the header file for the PCI driver test , so far implemented +// only on the Naviengine platform + +#ifndef __TPCI_TEST_H +#define __TPCI_TEST_H + +#ifndef __KERNEL_MODE__ +#define __E32TEST_EXTENSION__ +#include + #include +#endif // __KERNEL_MODE__ + +_LIT(KPciLdd, "d_pci.ldd"); +_LIT(KPciLddFactory, "PCI_test_factory"); +_LIT(KPciTest, "PCI Test LDD"); + +/** +Test driver op-codes +*/ +enum TPciTestCmd + { + EGetTestInfo, + EAccessConfigSpace, + EAccessMemorySpace, + EOpenPciDChunk, + EOpenPciPlatHwChunk, + EOpenPciMappedChunk, + EOpenPciWindowChunk, + ERunUnitTests + }; + +/** +Identifies a PCI Function (device) on the system +*/ +struct TPciDevice + { + TPciDevice() + :iVendorId(0xFFFFFFFF), iDeviceId(0xFFFFFFFF), iInstance(0) {} + + TPciDevice(TUint aVendorId, TUint aDeviceId, TInt aInstance=0) + :iVendorId(aVendorId), iDeviceId(aDeviceId), iInstance(aInstance) {} + + TUint iVendorId; + TUint iDeviceId; + TInt iInstance; ///< Unit to open (there could be multiple devices on system) + }; + +/** +Used to send chunk size and recieve +PCI address +*/ +struct TPciChunkCreateInfo + { + TPciChunkCreateInfo() + :iSize(0), iPciAddress(NULL) + { + } + + TPciChunkCreateInfo(TInt aSize, TUint& aPciAddress, TRequestStatus* aStatus=NULL) + :iSize(aSize), iPciAddress(&aPciAddress), iStatus(aStatus) + { + } + TInt iSize; + TUint* iPciAddress; + TRequestStatus* iStatus; + }; + +/** +Information about the PSL required by the +user side test +*/ +struct TPciTestInfo + { + TPciDevice iDevice; ///< Probe for this + + /** + Supplies the necessary information to test Read, Write, and + Modify for a word of PCI memory or configuration space + */ + struct TAddrSpaceTest + { + TAddrSpaceTest() + :iOffset(0), iExpectedValue(0), iReadOnlyMask(0) + {} + + TAddrSpaceTest(TUint aOffset, TUint aExpectedValue, TUint aReadOnlyMask) + :iOffset(aOffset), iExpectedValue(aExpectedValue), iReadOnlyMask(aReadOnlyMask) + {} + + /** + Returns a specified sub byte, or word from the whole dword + */ + inline TUint Expected(TInt aBitWidth, TInt aExtraOffset) const + { + //the right shift required to get field to bit 0 + const TInt shift = 8 *((aExtraOffset + iOffset) % 4); + + const TUint mask = 0xFFFFFFFF >> (32-aBitWidth); + return (iExpectedValue >> shift) & mask; + } + + const TUint iOffset; + const TUint iExpectedValue; ///< The initial value of word + const TUint iReadOnlyMask; ///< Mask of unwritable bits + //Future work, memory spaces should state a bar index + }; + + + TAddrSpaceTest iCfgSpaceRead; + TAddrSpaceTest iCfgSpaceWrite; + + TUint iMemSpaceIndex; ///< Memory space to select + TAddrSpaceTest iMemSpaceRead; + TAddrSpaceTest iMemSpaceWrite; + + TInt iNumberOfBars; ///< Number of simultaneous mappings into PCI space + }; + +class RPci; +class TAddrSpace; +/** +This class encapsulates all the various read/write/and modify commands +that can be carried out on a PCI memory space. The command is stored user +side, and then executed on kernel side when KRun() is called. +*/ +class TUserPciSpace + { +public: + TUserPciSpace() + :iPci(NULL), iOperation(EInvalid), iBitWidth(0), iOffset(0), + iWriteValue(0), iClearMask(0), iSetMask(0) + {} + TUserPciSpace(RPci& aPci); + + /** + Perform the encapsulated read/write/or modify + @note Only run on kernel side + */ + TUint KRun(TAddrSpace& aAddrSpace); + + /** + Clone method is required so that multiple threads may + have their own copy of a TUserPciSpace (without knowing + its runtime type) + */ + virtual TUserPciSpace* Clone() const = 0; + + TUint Read(TInt aBitWidth, TUint aOffset) + { + iOffset = aOffset; + iOperation = ERead; + iBitWidth = aBitWidth; + + return Call(); + } + + void Write(TInt aBitWidth, TUint aOffset, TUint aValue) + { + iOffset = aOffset; + iOperation = EWrite; + iBitWidth = aBitWidth; + + iWriteValue = aValue; + Call(); + } + + void Modify(TInt aBitWidth, TUint aOffset, TUint aClearMask, TUint aSetMask) + { + iOffset = aOffset; + iOperation = EModify; + iBitWidth = aBitWidth; + + iClearMask = aClearMask; + iSetMask = aSetMask; + Call(); + } + +protected: + /** + Makes a request to iPci and passes a copy of this object to + the kernel side. + */ + virtual TUint Call() =0; + + enum TOperation {EInvalid, ERead, EWrite, EModify}; + + /** + Pointer to a PCI device handle + */ + RPci* iPci; + + TOperation iOperation; //!< Type of access to perform + TInt iBitWidth; + + TUint iOffset; + TUint32 iWriteValue; + TUint32 iClearMask; + TUint32 iSetMask; + }; + +/** +Grants access to a PCI device's (identified +by aPci) config space from user side +*/ +class TUserConfigSpace : public TUserPciSpace + { +public: + TUserConfigSpace() + :TUserPciSpace() + {} + TUserConfigSpace(RPci& aPci); + + virtual TUserPciSpace* Clone() const; +private: + TUint Call(); + }; + +/** +Grants access to some region of a PCI +device's memory space. A PCI device(or function) +may have up to 8 distinct memory spaces +*/ +class TUserMemorySpace : public TUserPciSpace + { +public: + TUserMemorySpace() + :TUserPciSpace(), iBarIndex(-1) + {} + + TUserMemorySpace(RPci& aPci, TInt aBarIndex); + + virtual TUserPciSpace* Clone() const; + + inline TInt BarIndex() {return iBarIndex;} + +private: + TUint Call(); + + TInt iBarIndex; ///< Each PCI function may have up to 8 memory spaces + }; + +#endif //__TPCI_TEST_H