kerneltest/e32test/lffs/t_lfsdrv2.cpp
changeset 0 a41df078684a
child 33 0173bcd7697c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/e32test/lffs/t_lfsdrv2.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,1177 @@
+// Copyright (c) 1996-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of the License "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+// e32test\lffs\t_lfsdrv2.cpp
+// Test the LFFS Flash media driver
+// 
+//
+
+#include <e32test.h>
+#include <e32svr.h>
+#include <e32hal.h>
+#include <e32uid.h>
+#include <hal.h>
+#include "u32std.h"
+#include "..\misc\prbs.h"
+
+_LIT(KTestName,"T_LFSDRV");
+_LIT(KMediaDriverName,"MEDLFS");
+_LIT(KDot,".");
+_LIT(KSemiColon,";");
+
+RTest test(KTestName);
+TBusLocalDrive Drive;
+TInt DriveNumber;
+TLocalDriveCapsV7 DriveCaps;	// Required for M18 devices
+TBool ChangedFlag;
+TUint32 EbSz;
+TUint32 Size;
+
+const TInt KBufferSize=4096;
+const TInt KBigBufferSize=4096*4;
+TUint8 Buffer[KBigBufferSize];
+
+#ifdef _DEBUG
+/***************************************************
+ * ControlIO command types - for debug builds, only
+ ***************************************************/
+enum TCtrlIoTypes
+	{
+	ECtrlIoRww=0,
+	ECtrlIoTimeout=1
+	};
+// Used only for the ControlIO tests
+#define TYAX_PARTITION_SIZE	0x00200000 	// Partition size for TYAX is 1MB; 2 devices in parallel
+#endif
+
+
+/******************************************************************************
+ * Extra thread for background erase
+ ******************************************************************************/
+struct SEraseInfo
+	{
+	TInt iFirstBlock;
+	TInt iNumBlocks;
+	};
+
+volatile TInt Block;
+TInt EraseThreadFn(TAny* aPtr)
+	{
+	SEraseInfo& e=*(SEraseInfo*)aPtr;
+	TInt r=KErrNone;
+	for (Block=e.iFirstBlock; Block<e.iFirstBlock+e.iNumBlocks; ++Block)
+		{
+		TInt64 pos64 = MAKE_TINT64(0, Block*EbSz);
+		r=Drive.Format(pos64,EbSz);
+		if (r!=KErrNone)
+			return r;
+		}
+	return KErrNone;
+	}
+
+SEraseInfo EraseInfo;
+RThread EraseThread;
+TRequestStatus EraseStatus;
+const TInt KHeapSize=0x4000;
+
+_LIT(KEraseThreadName,"Eraser");
+TInt StartAsyncErase(TInt aFirstBlock, TInt aNumBlocks)
+	{
+	EraseInfo.iFirstBlock=aFirstBlock;
+	EraseInfo.iNumBlocks=aNumBlocks;
+	TInt r=EraseThread.Create(KEraseThreadName,EraseThreadFn,0x4000,KHeapSize,KHeapSize,&EraseInfo,EOwnerThread);
+	if (r!=KErrNone)
+		return r;
+	EraseThread.Logon(EraseStatus);
+	EraseThread.Resume();
+	return KErrNone;
+	}
+
+TInt WaitForAsyncErase()
+	{
+	User::WaitForRequest(EraseStatus);
+	TInt exitType=EraseThread.ExitType();
+	TInt exitReason=EraseThread.ExitReason();
+	TBuf<16> exitCat=EraseThread.ExitCategory();
+	if((exitType!= EExitKill)||(exitReason!=KErrNone))
+		{
+		test.Printf(_L("Async erase error: %d, block %d\n"),EraseStatus.Int(),Block);
+		test.Printf(_L("Thread exit reason: %d,%d,%S\n"),exitType,exitReason,&exitCat);
+		test(0);		
+		}
+	EraseThread.Close();
+
+	TUint32 pos=EraseInfo.iFirstBlock*EbSz;
+	TUint32 endpos=pos+EraseInfo.iNumBlocks*EbSz;
+	test.Printf(_L("\nAsync erase completed; verifying...\n"));
+	for (; pos<endpos; pos+=KBufferSize)
+		{
+		TInt64 pos64 = MAKE_TINT64(0, pos);
+		TPtr8 ptr(Buffer,0,KBufferSize);
+		Mem::FillZ(Buffer,KBufferSize);
+		TInt r=Drive.Read(pos64,KBufferSize,ptr);
+		test(r==KErrNone);
+		test(ptr.Length()==KBufferSize);
+		const TUint32* pB=(const TUint32*)Buffer;
+		const TUint32* pE=(const TUint32*)(Buffer+KBufferSize);
+		while (pB<pE && *pB==0xffffffff) ++pB;
+		if (pB<pE)
+			{
+			test.Printf(_L("ERROR: pos %08x data %08x\n"),((TUint32)pB)-((TUint32)Buffer)+pos,*pB);
+			test(0);
+			}
+		test.Printf(KDot);
+		}
+	test.Printf(_L("\n"));
+	return KErrNone;
+	}
+	
+/******************************************************************************
+ * Extra thread for background write, for use in the read-while-write tests
+ ******************************************************************************/
+TUint seed[2];
+
+TInt WriteThreadFn(TAny* aPtr)
+	{
+	// re-use the struct created for the erase thread
+	SEraseInfo& e=*(SEraseInfo*)aPtr;
+	TInt r=KErrNone;
+	
+	TPtrC8 wptr(Buffer,KBufferSize);
+	TUint32* pB=(TUint32*)Buffer;
+	TUint32* pE=(TUint32*)(Buffer+KBufferSize);
+	while (pB<pE)
+		*pB++=Random(seed);
+
+	for (Block=e.iFirstBlock; Block<e.iFirstBlock+e.iNumBlocks; ++Block)
+		{
+		TInt64 pos64 = MAKE_TINT64(0, Block*EbSz);
+		r=Drive.Write(pos64,wptr);
+		if (r!=KErrNone)
+			return r;
+		}
+	return KErrNone;
+	}
+
+RThread WriteThread;
+TRequestStatus WriteStatus;
+
+_LIT(KWriteThreadName,"Writer");
+TInt StartAsyncWrite(TInt aFirstBlock, TInt aNumBlocks)
+	{
+	// re-use the struct created for the erase thread
+	EraseInfo.iFirstBlock=aFirstBlock;
+	EraseInfo.iNumBlocks=aNumBlocks;
+	TInt r=WriteThread.Create(KWriteThreadName,WriteThreadFn,0x4000,KHeapSize,KHeapSize,&EraseInfo,EOwnerThread);
+	if (r!=KErrNone)
+		return r;
+	WriteThread.Logon(WriteStatus);
+	WriteThread.Resume();
+	return KErrNone;
+	}
+
+TInt WaitForAsyncWrite()
+	{
+	User::WaitForRequest(WriteStatus);
+	TInt exitType=WriteThread.ExitType();
+	TInt exitReason=WriteThread.ExitReason();
+	TBuf<16> exitCat=WriteThread.ExitCategory();
+	if((exitType!= EExitKill)||(exitReason!=KErrNone))
+		{
+		test.Printf(_L("Async Write error: %d, block %d\n"),WriteStatus.Int(),Block);
+		test.Printf(_L("Thread exit reason: %d,%d,%S\n"),exitType,exitReason,&exitCat);
+		test(0);
+		}
+	WriteThread.Close();
+	// No verification performed
+	test.Printf(_L("\n"));
+	return KErrNone;
+	}
+
+/******************************************************************************
+ * Control mode and Object mode test functions
+ ******************************************************************************/
+TInt DoControlModeWriteAndVerify(TUint32 aPattern, TUint32 aStartOffset)
+	{
+	// Writes 4K bytes of a given pattern to the "A" half of programming regions, 
+	// starting at the specified offset, then reads the data back to verify it
+
+		TUint32* pB=(TUint32*)(Buffer);
+		TUint32* pE=(TUint32*)(Buffer + KBufferSize);
+		TInt r=KErrNone;
+
+		// Fill the entire buffer with an initial value
+		while (pB<pE)
+			*pB++= aPattern;
+
+		// In this mode, half the device is available for writing, the other half is reserved;
+		// the available half appears as the first DriveCaps.iControlModeSize bytes, the reserved 
+		// half as the following DriveCaps.iControlModeSize, and this alternating continues.
+		// To perform this discrete-write test, therefore, the data held in Buffer that corresponds
+		// to the reserved area is overwritten with 0xFF; 'writing' this value to the reserved area
+		// has no detrimental effect.
+		TInt i;
+		TUint32 b;
+		pB=(TUint32*)Buffer;
+		for(i=0; i< KBufferSize; i+=(DriveCaps.iControlModeSize*2))
+		{
+			pB = (TUint32 *)((TUint32)pB + DriveCaps.iControlModeSize);
+			for (b=0; b < DriveCaps.iControlModeSize; b+=4)
+			{
+				*pB = 0xFFFFFFFF;
+				pB++;	
+			}
+		}
+		// Write the data
+		for (i=0; i<KBufferSize; i+=(4*DriveCaps.iControlModeSize))
+			{
+			TInt64 pos64(i + aStartOffset);
+			TPtrC8 ptr(Buffer+i,(4*DriveCaps.iControlModeSize));
+			r=Drive.Write(pos64,ptr);
+			test(r==KErrNone);
+			}
+		// Check what has been written
+		Mem::FillZ(Buffer,KBigBufferSize);
+		TPtr8 buf(Buffer,0,KBufferSize);
+		r=Drive.Read(aStartOffset,KBufferSize,buf);
+		test(r==KErrNone);
+		pB=(TUint32*)Buffer;
+		for(i=0; i< KBufferSize; i+=(DriveCaps.iControlModeSize*2))
+			{
+			for (b=0; b< DriveCaps.iControlModeSize; b+=4)
+				{
+				if(*pB++ != aPattern)
+					{ 
+					test.Printf(_L("ERROR: addr %08x data %08x expected %08x\n"),pB,*pB,aPattern);
+					r=KErrCorrupt;
+					break;
+					}
+				}
+			for (b=0; b< DriveCaps.iControlModeSize; b+=4)
+				{
+				if(*pB++ != 0xFFFFFFFF)
+					{ 
+					test.Printf(_L("ERROR: addr %08x data %08x expected 0xFFFFFFFF\n"),pB,*pB);
+					r=KErrCorrupt;
+					break;
+					}
+				}
+			}
+		return r;
+	}
+
+TInt DoObjectModeWriteAndVerify(TUint32 aOffset, TUint32 aSize)
+	{
+	// Writes 'aSize' bytes of a 'random' pattern to the specified offset
+	// then read back and verify
+	TInt r=KErrNone;
+
+	// Check that aSize is valid
+	if(aSize>DriveCaps.iObjectModeSize)
+		{
+		test.Printf(_L("ERROR: DoObjectModeWriteAndVerify - aSize=%x is greater than max (%x)\n"),aSize,DriveCaps.iObjectModeSize);
+		return KErrArgument;
+		}
+	// write the data
+	TUint seed[2];
+	seed[0]=0xb17217f8;
+	seed[1]=0;
+	TInt64 pos64 = MAKE_TINT64(0, aOffset);
+	TPtrC8 ptr(Buffer,aSize);
+	TUint32* pB=(TUint32*)Buffer;
+	TUint32* pE=(TUint32*)(Buffer+aSize);
+	while (pB<pE)
+		*pB++=Random(seed);
+	r=Drive.Write(pos64,ptr);
+	if(r!=KErrNone)
+		{
+		return r;
+		}
+	
+	// Read the data back
+	seed[0]=0xb17217f8;
+	seed[1]=0;
+	TPtr8 rptr(Buffer,0,aSize);
+	Mem::FillZ(Buffer,aSize);
+	r=Drive.Read(pos64,aSize,rptr);
+	if(r!=KErrNone)
+		{
+		test.Printf(_L("ERROR: DoObjectModeWriteAndVerify - Read returned %d\n"),r);
+		return r;
+		}
+	test((TUint32)(rptr.Length())==aSize);
+
+	// Verify the content
+	pB=(TUint32*)Buffer;
+	pE=(TUint32*)(Buffer+aSize);
+	TUint32 ex=0;
+	while (pB<pE && (ex=Random(seed),*pB==ex)) ++pB;
+	if (pB<pE)
+		{
+		test.Printf(_L("ERROR: DoObjectModeWriteAndVerify - addr %08x data %08x expected %08x\n"),pB,*pB,ex);
+		r=KErrCorrupt;
+		}
+	return r;
+	}
+
+
+TInt DoControlModeBoundaryWriteAndVerify()
+	{
+	// 
+	
+	TInt r=KErrNone;
+	//test.Printf(_L("Entering: DoControlModeBoundaryWriteAndVerify - Start Test\n"));
+
+	r=Drive.Format(0,DriveCaps.iEraseBlockSize);
+	test(r==KErrNone);
+	
+	// Program into the last Control mode region in the programming region.
+	TInt64 pos64 = MAKE_TINT64(0, (DriveCaps.iObjectModeSize - (DriveCaps.iControlModeSize*2)));
+	TPtrC8 ptr(Buffer,DriveCaps.iControlModeSize);
+	TUint32* pB=(TUint32*)Buffer;
+	TUint32* pE=(TUint32*)(Buffer+DriveCaps.iControlModeSize);
+	while (pB<pE)
+		*pB++=0xb4b4a5a5;
+	r=Drive.Write(pos64,ptr);
+	if(r!=KErrNone)
+		{
+		test.Printf(_L("ERROR: DoControlModeBoundaryWriteAndVerify - Write 1\n"));
+		return r;
+		}
+
+	// Program into the next programming region starting at the first byte up to the size of the Control Mode Size.
+	pos64 = MAKE_TINT64(0, DriveCaps.iObjectModeSize);
+	r=Drive.Write(pos64,ptr);
+	if(r!=KErrNone)
+		{
+		test.Printf(_L("ERROR: DoControlModeBoundaryWriteAndVerify - Write 2\n"));
+		return r;
+		}
+	
+	// Read the data back from the first program
+	pos64 = MAKE_TINT64(0, (DriveCaps.iObjectModeSize - (DriveCaps.iControlModeSize*2)));
+	TPtr8 rptr(Buffer,0,(TInt)DriveCaps.iControlModeSize);
+	Mem::FillZ(Buffer,DriveCaps.iControlModeSize);
+	r=Drive.Read(pos64,DriveCaps.iControlModeSize,rptr);
+	if(r!=KErrNone)
+		{
+		test.Printf(_L("ERROR: DoObjectModeWriteAndVerify - Read returned %d\n"),r);
+		return r;
+		}
+	test((TUint32)(rptr.Length())==DriveCaps.iControlModeSize);
+
+	// Verify the content
+	pB=(TUint32*)Buffer;
+	pE=(TUint32*)(Buffer+DriveCaps.iControlModeSize);
+	TUint32 ex=0xb4b4a5a5;
+	while (pB<pE && (*pB==ex)) ++pB;
+	if (pB<pE)
+		{
+		test.Printf(_L("ERROR: DoObjectModeWriteAndVerify - addr %08x data %08x expected %08x\n"),pB,*pB,ex);
+		r=KErrCorrupt;
+		}
+
+   // Read the data back from the second program
+   	pos64 = MAKE_TINT64(0, DriveCaps.iObjectModeSize);
+	TPtr8 rptr2(Buffer,0,((TInt)DriveCaps.iControlModeSize));
+	Mem::FillZ(Buffer,DriveCaps.iControlModeSize);
+	r=Drive.Read(pos64,DriveCaps.iControlModeSize,rptr2);
+	if(r!=KErrNone)
+		{
+		test.Printf(_L("ERROR: DoObjectModeWriteAndVerify - Read returned %d\n"),r);
+		return r;
+		}
+	test((TUint32)(rptr2.Length())==DriveCaps.iControlModeSize);
+
+	// Verify the content
+	pB=(TUint32*)Buffer;
+	pE=(TUint32*)(Buffer+DriveCaps.iControlModeSize);
+	ex=0xb4b4a5a5;
+	while (pB<pE && (*pB==ex)) ++pB;
+	if (pB<pE)
+		{
+		test.Printf(_L("ERROR: DoObjectModeWriteAndVerify - addr %08x data %08x expected %08x\n"),pB,*pB,ex);
+		r=KErrCorrupt;
+		}
+
+	// Bit Twiddle the last bit of the last Control Mode Region 
+	// Then bit twiddle the first bit of the first control Mode region.
+
+	// Program into the last Control mode region in the programming region.
+	pos64 = MAKE_TINT64(0, (DriveCaps.iObjectModeSize - DriveCaps.iControlModeSize - 4));
+	TPtrC8 ptr2(Buffer,4);
+	TUint32* pC=(TUint32*)Buffer;
+	*pC = 0xFFFFFFFE;
+	r=Drive.Write(pos64,ptr2);
+	if(r!=KErrNone)
+		{
+				test.Printf(_L("ERROR: DoControlModeBoundaryWriteAndVerify - Write 3\n"));
+
+		return r;
+		}
+	
+	// Read the data back from the first program
+	pos64 = MAKE_TINT64(0, (DriveCaps.iObjectModeSize - DriveCaps.iControlModeSize - 4));
+	TPtr8 rptr3(Buffer,0,4);
+	Mem::FillZ(Buffer,4);
+	r=Drive.Read(pos64,4,rptr3);
+	if(r!=KErrNone)
+		{
+		test.Printf(_L("ERROR: DoObjectModeWriteAndVerify - Read returned %d\n"),r);
+		return r;
+		}
+	test(rptr3.Length()==4);
+
+	// Verify the content
+	pB=(TUint32*)Buffer;
+	if (*pB != 0xb4b4a5a4)
+		{
+		test.Printf(_L("ERROR: DoObjectModeWriteAndVerify - addr %08x data %08x expected 0xb4b4a5a4\n"),pB,*pB);
+		r=KErrCorrupt;
+		}
+
+	// Program into the last Control mode region in the programming region.
+	pos64 = MAKE_TINT64(0, DriveCaps.iObjectModeSize);
+	TPtrC8 ptr3(Buffer,4);
+	pC=(TUint32*)Buffer;
+	*pC = 0x7FFFFFFF;
+	r=Drive.Write(pos64,ptr3);
+	if(r!=KErrNone)
+		{
+				test.Printf(_L("ERROR: DoControlModeBoundaryWriteAndVerify - Write 4\n"));
+
+		return r;
+		}
+	
+	// Read the data back from the first program
+	pos64 = MAKE_TINT64(0, DriveCaps.iObjectModeSize);
+	TPtr8 rptr4(Buffer,0,4);
+	Mem::FillZ(Buffer,4);
+	r=Drive.Read(pos64,4,rptr4);
+	if(r!=KErrNone)
+		{
+		test.Printf(_L("ERROR: DoObjectModeWriteAndVerify - Read returned %d\n"),r);
+		return r;
+		}
+	test(rptr4.Length()==4);
+
+	// Verify the content
+	pB=(TUint32*)Buffer;
+	if (*pB != 0x34b4a5a5)
+		{
+		test.Printf(_L("ERROR: DoObjectModeWriteAndVerify - addr %08x data %08x expected 0x34b4a5a5\n"),pB,*pB);
+		r=KErrCorrupt;
+		}
+		
+	return r;
+	}
+
+
+
+
+/******************************************************************************
+ * Main test program
+ ******************************************************************************/
+GLDEF_C TInt E32Main()
+	{
+	test.Title();
+
+/******************************************************************************
+ * Initialisation
+ ******************************************************************************/
+	TDriveInfoV1Buf diBuf;
+	UserHal::DriveInfo(diBuf);
+	TDriveInfoV1 &di=diBuf();
+	test.Start(_L("Test the LFFS media driver"));
+	test.Printf(_L("DRIVES PRESENT  :%d\r\n"),di.iTotalSupportedDrives);
+	test.Printf(_L("C:(1ST) DRIVE NAME  :%- 16S\r\n"),&di.iDriveName[0]);
+	test.Printf(_L("D:(2ND) DRIVE NAME  :%- 16S\r\n"),&di.iDriveName[1]);
+	test.Printf(_L("E:(3RD) DRIVE NAME  :%- 16S\r\n"),&di.iDriveName[2]);
+	test.Printf(_L("F:(4TH) DRIVE NAME  :%- 16S\r\n"),&di.iDriveName[3]);
+	test.Printf(_L("G:(5TH) DRIVE NAME  :%- 16S\r\n"),&di.iDriveName[4]);
+	test.Printf(_L("H:(6TH) DRIVE NAME  :%- 16S\r\n"),&di.iDriveName[5]);
+	test.Printf(_L("I:(7TH) DRIVE NAME  :%- 16S\r\n"),&di.iDriveName[6]);
+	test.Printf(_L("J:(8TH) DRIVE NAME  :%- 16S\r\n"),&di.iDriveName[7]);
+	test.Printf(_L("K:(9TH) DRIVE NAME  :%- 16S\r\n"),&di.iDriveName[8]);
+
+	test.Printf(_L("\r\nWarning - all data on LFFS drive will be lost.\r\n"));
+	test.Printf(_L("<<<Select drive to continue>>>\r\n"));
+	FOREVER
+		{
+		TChar c=(TUint)test.Getch();
+		c.UpperCase();
+		DriveNumber=((TUint)c)-'C';
+		if (DriveNumber>=0&&DriveNumber<='C'+ 8)
+			break;
+		}
+
+	test.Next(_L("Load media driver"));
+	TInt r=User::LoadPhysicalDevice(KMediaDriverName);
+	test(r==KErrNone || r==KErrAlreadyExists);
+
+	test.Next(_L("Connect to drive"));
+	r=Drive.Connect(DriveNumber,ChangedFlag);
+	test(r==KErrNone);
+	test.Next(_L("Get capabilities"));
+
+	DriveCaps.iControlModeSize=0;	// If test invoked for a chip other than Sibley then this element will not be updated
+	DriveCaps.iObjectModeSize=0;	// If test invoked for a chip other than Sibley then this element will not be updated 
+	TPckg<TLocalDriveCapsV7> capsPckg(DriveCaps);
+  	r=Drive.Caps(capsPckg);
+
+	test(r==KErrNone);
+	test.Printf(_L("Size            : %08x\n"),I64LOW(DriveCaps.iSize));
+	test.Printf(_L("Type            : %d\n"),DriveCaps.iType);
+	test.Printf(_L("BatState        : %d\n"),DriveCaps.iBattery);
+	test.Printf(_L("DriveAtt        : %02x\n"),DriveCaps.iDriveAtt);
+	test.Printf(_L("MediaAtt        : %02x\n"),DriveCaps.iMediaAtt);
+	test.Printf(_L("BaseAddress     : %08x\n"),DriveCaps.iBaseAddress);
+	test.Printf(_L("FileSysID       : %d\n"),DriveCaps.iFileSystemId);
+	test.Printf(_L("Hidden sectors  : %d\n"),DriveCaps.iHiddenSectors);
+	test.Printf(_L("Erase block size: %d\n"),DriveCaps.iEraseBlockSize);
+
+	test.Printf(_L("Partition size: %d\n"),DriveCaps.iPartitionSize);
+	test.Printf(_L("Control Mode size: %d\n"),DriveCaps.iControlModeSize);
+	test.Printf(_L("Object Mode size: %d\n"),DriveCaps.iObjectModeSize);
+	test.Printf(_L("Press any key...\n\n"));
+	test.Getch();
+
+	test(DriveCaps.iDriveAtt==(KDriveAttLocal|KDriveAttInternal));
+	test((DriveCaps.iMediaAtt&KMediaAttFormattable)==(KMediaAttFormattable)); // Apply mask since other flags may be set
+
+#if defined(_DEBUG) && defined(_WINS)
+/******************************************************************************
+ * Simulate device timeout
+ ******************************************************************************/
+	test.Next(_L("Timeout"));
+	EbSz=DriveCaps.iEraseBlockSize;
+	r=Drive.Format(0,EbSz);
+	test(r==KErrNone);
+	r=Drive.ControlIO(ECtrlIoTimeout, NULL, NULL);
+
+	if(r!=KErrNotSupported)
+		{
+		if(r==KErrNone)
+			{
+			// Test timeout behaviour for Write operation
+			TPtrC8 ptr(Buffer,1);
+			r=Drive.Write(0,ptr);
+			test(r==KErrNotReady);
+			// Test condition now cleared, ensure normal operation is OK
+			r=Drive.Write(0,ptr);
+			test(r==KErrNone);
+			// Test timeout behaviour for Format operation
+			r=Drive.ControlIO(ECtrlIoTimeout, NULL, NULL);
+			test(r==KErrNone);
+			r=Drive.Format(0,EbSz);
+			test(r==KErrNotReady);
+			// Cleanup
+			r=Drive.Format(0,EbSz);
+			test(r==KErrNone);
+			}
+		else
+			{
+			test.Printf(_L("Timeout ControlIO failed initialisation\n"));
+			test(0);	// Cannot proceed with this test
+			}
+		}
+	else 
+		{
+		test.Printf(_L("Timeout ControlIO not supported\n"));
+		}
+
+	test.Printf(_L("Press any key...\n"));
+	test.Getch();
+#endif
+
+ /******************************************************************************
+ * Formatting
+ ******************************************************************************/
+	test.Next(_L("Format"));
+	TUint32 pos;
+	EbSz=DriveCaps.iEraseBlockSize;
+	Size=I64LOW(DriveCaps.iSize);
+// Reduce size so test doesn't take forever
+	if (Size>8*EbSz)
+		Size=8*EbSz;
+
+	for (pos=0; pos<Size; pos+=EbSz)
+		{
+		TInt64 pos64 = MAKE_TINT64(0, pos);
+		r=Drive.Format(pos64,EbSz);
+		test(r==KErrNone);
+		test.Printf(KDot);
+		}
+	test.Next(_L("\nVerify"));
+	for (pos=0; pos<Size; pos+=KBufferSize)
+		{
+		TInt64 pos64 = MAKE_TINT64(0, pos);
+		TPtr8 ptr(Buffer,0,KBufferSize);
+		Mem::FillZ(Buffer,KBigBufferSize);
+		r=Drive.Read(pos64,KBufferSize,ptr);
+		test(r==KErrNone);
+		test(ptr.Length()==KBufferSize);
+		const TUint32* pB=(const TUint32*)Buffer;
+		const TUint32* pE=(const TUint32*)(Buffer+KBufferSize);
+		while (pB<pE && *pB==0xffffffff) ++pB;
+		if (pB<pE)
+			{
+			test.Printf(_L("ERROR: addr %08x data %08x\n"),pB,*pB);
+			test(0);
+			}
+		test.Printf(KDot);
+		}
+	test.Printf(_L("\nPress any key...\n\n"));
+	test.Getch();
+
+/******************************************************************************
+ * Large block writes
+ ******************************************************************************/
+	test.Next(_L("Write"));
+	TUint seed[2];
+	seed[0]=0xb17217f8;
+	seed[1]=0;
+	for (pos=0; pos<Size; pos+=KBufferSize)
+		{
+		TInt64 pos64 = MAKE_TINT64(0, pos);
+		TPtrC8 ptr(Buffer,KBufferSize);
+		TUint32* pB=(TUint32*)Buffer;
+		TUint32* pE=(TUint32*)(Buffer+KBufferSize);
+		while (pB<pE)
+			*pB++=Random(seed);
+		r=Drive.Write(pos64,ptr);
+		test(r==KErrNone);
+		test.Printf(KDot);
+		}
+	test.Printf(_L("\n"));
+	test.Next(_L("Verify"));
+	seed[0]=0xb17217f8;
+	seed[1]=0;
+	for (pos=0; pos<Size; pos+=KBufferSize)
+		{
+		TInt64 pos64 = MAKE_TINT64(0, pos);
+		TPtr8 ptr(Buffer,0,KBufferSize);
+		Mem::FillZ(Buffer,KBigBufferSize);
+		r=Drive.Read(pos64,KBufferSize,ptr);
+		test(r==KErrNone);
+		test(ptr.Length()==KBufferSize);
+		const TUint32* pB=(const TUint32*)Buffer;
+		const TUint32* pE=(const TUint32*)(Buffer+KBufferSize);
+		TUint32 ex=0;
+		while (pB<pE && (ex=Random(seed),*pB==ex)) ++pB;
+		if (pB<pE)
+			{
+			test.Printf(_L("ERROR: addr %08x data %08x expected %08x\n"),pB,*pB,ex);
+			test(0);
+			}
+		test.Printf(KDot);
+		}
+
+	test.Printf(_L("\nPress any key...\n\n"));
+	test.Getch();
+
+/******************************************************************************
+ * Single byte writes
+ ******************************************************************************/
+	test.Next(_L("Format first block"));
+	r=Drive.Format(0,EbSz);
+	test(r==KErrNone);
+	test.Next(_L("Single byte writes"));
+	seed[0]=0x317b106f;
+	seed[1]=0;
+	TUint32* pB=(TUint32*)Buffer;
+	TUint32* pE=(TUint32*)(Buffer+KBufferSize);
+	while (pB<pE)
+		*pB++= Random(seed);
+	
+	// For M18 devices, this test requires control mode operation.
+	// In this mode, half the device is available for writing, the other half is reserved;
+	// the available half appears as the first DriveCaps.iControlModeSize bytes, the reserved 
+	// half as the following DriveCaps.iControlModeSize, and this alternating continues.
+	// To perform this discrete-write test, therefore, the data held in Buffer that corresponds
+	// to the reserved area is overwritten with 0xFF; 'writing' this value to the reserved area
+	// has no detrimental effect.
+	TInt i;
+	TUint32 b;
+	if (DriveCaps.iControlModeSize > 0)
+	{
+		pB=(TUint32*)Buffer;
+		for(i=0; i< KBufferSize; i+=(DriveCaps.iControlModeSize*2))
+		{
+			pB = (TUint32 *)((TUint32)pB + DriveCaps.iControlModeSize);
+			for (b=0; b < DriveCaps.iControlModeSize; b+=4)
+			{
+				*pB = 0xFFFFFFFF;
+				pB++;	
+			}
+		}
+	} 
+	
+#if 0
+	// Debug - print content of buffer
+	test.Printf(_L("Content of buffer after inserting 0xFFFFFFFFs follows\n"));
+	i=0;
+	TUint32* verifyPtr=(TUint32*)Buffer;
+	while(i<KBufferSize)
+		{
+		test.Printf(_L("%8x %8X %8X\n"),i+=8,*verifyPtr++,*verifyPtr++);
+		}
+#endif
+	
+	for (i=0; i<KBufferSize; ++i)
+		{
+		TInt64 pos64(i);
+		TPtrC8 ptr(Buffer+i,1);
+		r=Drive.Write(pos64,ptr);
+		test(r==KErrNone);
+		if (!(i%16))
+			test.Printf(KDot);
+		}
+	test.Printf(_L("\n"));
+	test.Next(_L("Verify"));
+	Mem::FillZ(Buffer,KBigBufferSize);
+	TPtr8 buf(Buffer,0,KBufferSize);
+	r=Drive.Read(0,KBufferSize,buf);
+	test(r==KErrNone);
+	seed[0]=0x317b106f;
+	seed[1]=0;
+	pB=(TUint32*)Buffer;
+	TUint32 ex=0;
+	if (DriveCaps.iControlModeSize > 0)
+		{
+		pB=(TUint32*)Buffer;
+		for(i=0; i< KBufferSize; i+=(DriveCaps.iControlModeSize*2))
+			{
+			for (b=0; b< DriveCaps.iControlModeSize; b+=4)
+				{
+				ex=Random(seed);
+				if(*pB++ != ex)
+					{ 
+					test.Printf(_L("ERROR: addr %08x data %08x expected %08x\n"),pB,*pB,ex);
+					break;
+					}
+				}
+			for (b=0; b< DriveCaps.iControlModeSize; b+=4)
+				{
+				ex=Random(seed);
+				if(*pB++ != 0xFFFFFFFF)
+					{ 
+					test.Printf(_L("ERROR: addr %08x data %08x expected 0xFF\n"),pB,*pB);
+					break;
+					}
+				}
+			if (!((i+1)%64))
+				test.Printf(KDot);
+
+			}
+		}
+	else
+		{	
+		while (pB<pE && (ex=Random(seed),*pB==ex)) ++pB;
+		}
+	if (pB<pE)
+		{
+		test.Printf(_L("ERROR: addr %08x data %08x expected %08x\n"),pB,*pB,ex);
+		test(0);
+		}
+
+	test.Printf(_L("Single byte writes OK\n"));
+	
+	test.Printf(_L("Press any key...\n\n"));
+	test.Getch();
+
+/******************************************************************************
+ * Random length writes
+ ******************************************************************************/
+	test.Next(_L("Random length writes"));
+	// Prepare the device (required if control mode is used for M18 devices)
+	// assume that a maximum of 2 blocks is required
+	r=Drive.Format(0,EbSz);
+	r=Drive.Format(DriveCaps.iEraseBlockSize,EbSz);
+
+	seed[0]=0xdeadbeef;
+	seed[1]=0;
+	pB=(TUint32*)Buffer;
+	pE=(TUint32*)(Buffer+KBigBufferSize);
+	while (pB<pE)
+		*pB++=Random(seed);
+	TInt remain=KBigBufferSize;
+	TInt objectModeOffset=0;
+	TUint32 writeCount=0;
+	seed[0]=0xdeadbeef;
+	seed[1]=0;
+	for(writeCount=0; remain && (writeCount<512); writeCount++)
+		{
+		TInt l=1+(Random(seed)&255);	 // random length between 1 and 256
+		if (l>remain)
+			l=remain;
+		TInt pos=0;
+		if(DriveCaps.iObjectModeSize == 0)
+			{
+			pos=KBigBufferSize-remain;
+			}
+		
+		TPtrC8 ptr(Buffer+(KBigBufferSize-remain),l);
+		TInt64 pos64(pos+objectModeOffset);  // Start writes in a new programming region if object mode supported
+		r=Drive.Write(pos64,ptr);
+		test(r==KErrNone);
+		objectModeOffset+=DriveCaps.iObjectModeSize;
+		remain-=l;
+		test.Printf(KDot);
+		}
+	test.Printf(_L("\n"));
+	test.Next(_L("Verify"));
+	Mem::FillZ(Buffer,KBigBufferSize);
+	new (&buf) TPtr8(Buffer,0,KBigBufferSize);
+	if(DriveCaps.iObjectModeSize==0)
+	{
+		r=Drive.Read(0,KBigBufferSize,buf);
+		test(r==KErrNone);
+
+	}
+	else
+	{
+		remain=KBigBufferSize;
+		objectModeOffset=0;
+		
+		while(remain && writeCount)
+			{
+			TInt totalLength=0;
+			TInt l=1+(Random(seed)&255);	 // random length between 1 and 256
+			if (l>remain)
+				l=remain;
+			TPtr8 ptr(Buffer+(totalLength),l);
+			r=Drive.Read(objectModeOffset,l,ptr);
+			test(r==KErrNone);
+			totalLength +=l;
+			remain-=l;
+			writeCount--;
+			test.Printf(KDot);
+			}
+	}
+
+	seed[0]=0xdeadbeef;
+	seed[1]=0;
+	pB=(TUint32*)Buffer;
+	ex=0;
+	if(DriveCaps.iObjectModeSize==0)
+	{
+		while (pB<pE && (ex=Random(seed),*pB==ex)) ++pB;
+		if (pB<pE)
+			{
+			test.Printf(_L("ERROR: addr %08x data %08x expected %08x\n"),pB,*pB,ex);
+	//		test.Getch();
+			test(0);
+			}
+	}
+	
+	r=Drive.Format(0,EbSz);
+	r=Drive.Format(DriveCaps.iEraseBlockSize,EbSz);
+	test.Printf(_L("\nPress any key...\n\n"));
+	test.Getch();
+
+/******************************************************************************
+ * Concurrent read/write/erase
+ ******************************************************************************/
+	test.Printf(_L("Foreground R/W\n"));
+	r=StartAsyncErase(1,Size/EbSz-1);
+	test(r==KErrNone);
+
+	seed[0]=0xb17217f8;
+	seed[1]=0;
+	for (pos=KBufferSize+KBigBufferSize; pos<EbSz; pos+=KBufferSize)
+		{
+		TInt64 pos64 = MAKE_TINT64(0, pos);
+		TPtrC8 wptr(Buffer,KBufferSize);
+		TUint32* pB=(TUint32*)Buffer;
+		TUint32* pE=(TUint32*)(Buffer+KBufferSize);
+		while (pB<pE)
+			*pB++=Random(seed);
+		r=Drive.Write(pos64,wptr);
+		test(r==KErrNone);
+		test.Printf(KDot);
+		Mem::FillZ(Buffer+KBufferSize,KBufferSize);
+		TPtr8 rptr(Buffer+KBufferSize,0,KBufferSize);
+		r=Drive.Read(pos64,KBufferSize,rptr);
+		test(r==KErrNone);
+		test(rptr.Length()==KBufferSize);
+		//test(Mem::Compare(Buffer,KBufferSize,Buffer+KBufferSize,KBufferSize)==0);
+		r = Mem::Compare(Buffer,KBufferSize,Buffer+KBufferSize,KBufferSize);
+#if 0
+		if (r!=KErrNone)
+		{
+			pB=(TUint32*)Buffer;
+			pE=(TUint32*)(Buffer+KBufferSize);
+			for(TInt i=0; i < (KBufferSize>>2); i++)
+			{
+			  test.Printf(_L("%d Buffer Content %08x   %08x Flash Content\n"),i, pB[i], pE[i]);			
+			} 
+		}
+#endif
+		test (r==KErrNone);
+		test.Printf(KSemiColon);
+		}
+
+	r=WaitForAsyncErase();
+	test(r==KErrNone);
+
+    r=Drive.Format(0,EbSz);
+	r=Drive.Format(DriveCaps.iEraseBlockSize,EbSz);
+	test.Printf(_L("Press any key...\n\n"));
+	test.Getch();
+
+// Perform the following tests for debug builds, only
+
+#ifdef _DEBUG
+
+/******************************************************************************
+ * Concurrent operations to exercise TYAX Read-While-Write capability
+ * First, show read while write denied when attempting to read from a partition 
+ * that is being written to
+ * Second, show read while write proceeding when reading from a partition other
+ * than that which is being written to
+ ******************************************************************************/
+
+	// Do not perform these tests unless read-while-write is supported
+	if(DriveCaps.iMediaAtt&KMediaAttReadWhileWrite)
+		{	
+		test.Next(_L("Denied read while write"));
+		r=Drive.ControlIO(ECtrlIoRww, NULL, NULL);
+		if(r!=KErrNone)
+			{
+			test.Printf(_L("ControlIO not ready, returned %d\n"), r);
+			test(0);	// Cannot proceed with this test
+			}
+		test.Printf(_L("Press any key...\n"));
+		test.Getch();
+
+		test.Printf(_L("Starting async write for the first RWE/RWW test"));
+		r=StartAsyncWrite(1,3); // Write to the first three blocks, only, to limit duration
+		test(r==KErrNone);
+	
+		// Allow the write thread to be created and ready to run
+		// This will ensure that the driver will have received a write request before the second of the read
+		// requests, below. Following the issue of the ControlIO command, above, the driver will not instigate
+		// the write request until the next (second) read request is received. This is done so that the high priority
+		// driver thread recognises the existence of a read request (from a lower priority test / user thread)
+		// before it executes a sequence of writes to the FLASH device. This is necessary because, although
+		// each write takes a finite amount of time, the poll timer expires so quickly that the driver thread
+		// would not be blocked for a sufficiently long period to allow the read request to be processed. Adopting
+		// the contrived, and artificial, approach of using ControlIO to 'stage' the write allows the read-while-write
+		// capability of the device to be execrised.
+		User::After(1000);	
+
+		test.Printf(_L("Starting concurrent loop for background write\n"));
+		{
+		// First read - this will be performed before the write thread is run, so does
+		// not exercise read while write.
+		TInt64 pos64 = MAKE_TINT64(0,0);
+		TPtr8 rptr(Buffer+KBufferSize,0,KBufferSize);
+		test.Printf(_L("Issuing Drive.Read 1\n"));
+		r=Drive.Read(pos64,KBufferSize,rptr); 
+		test(r==KErrNone);
+		test.Printf(KSemiColon);		
+		}
+		{
+		// Second read - to same partition (and block) as the active write
+		// This read should be deferred by the driver
+		TInt64 pos64 = MAKE_TINT64(0, 2*EbSz);
+		TPtr8 rptr(Buffer+KBufferSize,0,KBufferSize);
+		test.Printf(_L("Issuing Drive.Read 2\n"));
+		r=Drive.Read(pos64,KBufferSize,rptr); // Should collide with second write
+		test(r==KErrNone);
+		test.Printf(KSemiColon);		
+		}
+		{
+		// Third read - due to the tight poll timer period, this will not be scheduled 
+		// until the write request has completed - so does not exercise read while write.
+		TInt64 pos64 = MAKE_TINT64(0, DriveCaps.iPartitionSize);
+		TPtr8 rptr(Buffer+KBufferSize,0,KBufferSize);
+		test.Printf(_L("Issuing Drive.Read 3\n"));
+		r=Drive.Read(pos64,KBufferSize,rptr);
+		test(r==KErrNone);
+		test.Printf(KSemiColon);		
+		}
+
+		r=WaitForAsyncWrite();
+		test(r==KErrNone);
+	
+	///////////////////////////////////////////////////////////////////////////////
+		r=Drive.Format(0,EbSz);
+		r=Drive.Format(DriveCaps.iEraseBlockSize,EbSz);
+		r=Drive.Format((DriveCaps.iEraseBlockSize*2),EbSz);
+		r=Drive.Format((DriveCaps.iEraseBlockSize*3),EbSz);
+		test.Printf(_L("Press any key...\n"));
+		test.Getch();
+		test.Next(_L("Supported read while write"));
+		r=Drive.ControlIO(ECtrlIoRww, NULL, NULL);
+		if(r!=KErrNone)
+			{
+			test.Printf(_L("ControlIO not ready\n"));
+			return r;
+			}
+		test.Printf(_L("Press any key...\n"));
+		test.Getch();
+
+		test.Printf(_L("Starting async write for the second RWE/RWW test"));
+		r=StartAsyncWrite(1,3); // Write to the first three blocks, only, to limit duration
+		test(r==KErrNone);
+
+		// Allow the write thread to be created and ready to run
+		User::After(1000);	
+
+		test.Printf(_L("Starting concurrent loop for background write\n"));
+		{
+		// First read - this will be performed before the write thread is run, so does
+		// not exercise read while write.
+		TInt64 pos64 = MAKE_TINT64(0, DriveCaps.iPartitionSize);
+		TPtr8 rptr(Buffer+KBufferSize,0,KBufferSize);
+		test.Printf(_L("Issuing Drive.Read 1\n"));
+		r=Drive.Read(pos64,KBufferSize,rptr); 
+		test(r==KErrNone);
+		test.Printf(KSemiColon);		
+		}
+		{
+		// Second read - to different partition than that targeted by the active write
+		// This read should check the overlap and proceed without being deferred
+		TInt64 pos64 = MAKE_TINT64(0, DriveCaps.iPartitionSize);
+		TPtr8 rptr(Buffer+KBufferSize,0,KBufferSize);
+		test.Printf(_L("Issuing Drive.Read 2\n"));
+		r=Drive.Read(pos64,KBufferSize,rptr); // Should collide with second write
+		test(r==KErrNone);
+		test.Printf(KSemiColon);		
+		}
+		{
+		// Third read - due to the tight poll timer period, this will not be scheduled 
+		// until the write request has completed - so does not exercise read while write.
+		TInt64 pos64 = MAKE_TINT64(0, DriveCaps.iPartitionSize);
+		TPtr8 rptr(Buffer+KBufferSize,0,KBufferSize);
+		test.Printf(_L("Issuing Drive.Read 3\n"));
+		r=Drive.Read(pos64,KBufferSize,rptr);
+		test(r==KErrNone);
+		test.Printf(KSemiColon);		
+		}
+
+		test.Printf(_L("\nForeground Read OK\n"));
+		r=WaitForAsyncWrite();
+		test(r==KErrNone);
+		}
+#endif		
+
+	// Clean up
+	r=Drive.Format(0,EbSz);
+	r=Drive.Format(DriveCaps.iEraseBlockSize,EbSz);
+	r=Drive.Format((DriveCaps.iEraseBlockSize*2),EbSz);
+	r=Drive.Format((DriveCaps.iEraseBlockSize*3),EbSz);
+
+/*****************************************************************************************************
+	Tests for M18 NOR Flash devices
+
+	These tests assume that object mode and control mode is supported
+ *****************************************************************************************************/
+	if((DriveCaps.iControlModeSize !=0) && (DriveCaps.iObjectModeSize != 0))
+		{
+		// Control mode writes
+		// Prove that control mode writes are supported
+		// This requires that data is formatted such that areas coinciding with the "B" Half of a
+		// programming region are set to all 0xFFs
+		// Write to programming region zero
+		test.Next(_L("\nControl mode writes"));
+
+		r=DoControlModeWriteAndVerify(0xa5a5a5a5, 0);
+		test(r==KErrNone);
+		// Now verify that data written in control mode can be further modified
+		// Do this by ANDing the read-back pattern with a mask that clears particular bits
+		// then write the resulting pattern back to the region
+		r=DoControlModeWriteAndVerify(0x84848484, 0);
+		test(r==KErrNone);
+		// Now verify that data written in control mode can be further modified to all 0x00s
+		// Do this by ANDing the read-back pattern with a mask that clears the remaining bits
+		// then write the resulting pattern back to the region
+		r=DoControlModeWriteAndVerify(0x00000000, 0);
+		test(r==KErrNone);
+		// Erase the block before attempting to re-use the programming region for object mode writing
+		test.Printf(_L("\nErase block 0 before object mode write"));
+		r=Drive.Format(0,EbSz);
+		test(r==KErrNone);
+
+		test.Next(_L("\n(Subsequent) Object mode writes"));
+
+		// Control mode writes
+		// Prove that object mode writes are allowd to an erased block that was previously
+		// used in control mode
+		// Use offset zero and length equal to one-quarter of the allowed object mode size (i.e. one-
+		// quarter of the lengh of the programming region) (The write test, above, wrote an entire region
+		// in object mode)
+		test.Printf(_L("\nObject mode write, object mode size=%d"),DriveCaps.iObjectModeSize);
+		r=DoObjectModeWriteAndVerify(0, (DriveCaps.iObjectModeSize>>2));
+		test(r==KErrNone);
+		// Prove that an attempt to append data to an object mode region fails
+		test.Printf(_L("\nAttempt append to object mode region"));
+		r=DoObjectModeWriteAndVerify((DriveCaps.iObjectModeSize>>2),(DriveCaps.iObjectModeSize>>2));
+		test(r==KErrGeneral);
+		// Erase the block after a failed write and before attempting to re-use for programming
+		test.Printf(_L("\nErase block 0 after failed object mode write"));
+		r=Drive.Format(0,EbSz);
+		test(r==KErrNone);
+
+		test.Next(_L("\n(Subsequent) Object mode writes following an error"));
+
+		// write to a new object mode region after a failed write and before attempting to erase the block
+		// Prove that erase block can be re-written to
+		test.Printf(_L("\nObject mode write following failed write and erase"));
+		r=DoObjectModeWriteAndVerify(0, (DriveCaps.iObjectModeSize>>2));
+		test(r==KErrNone);
+		// Cause a failed object mode write
+		r=DoObjectModeWriteAndVerify(0, (DriveCaps.iObjectModeSize>>2));
+		test(r==KErrGeneral);
+		// the status register has an error.  Attempt to write in a new region and ensure that it succeeds
+		r=DoObjectModeWriteAndVerify(DriveCaps.iObjectModeSize, DriveCaps.iObjectModeSize);
+		test(r==KErrNone);
+
+		test.Next(_L("\n(Subsequent) Control mode writes following previous use in object mode"));
+
+		// Re-use a former object mode region for control mode writes
+		// Erase the block after a failed write and before attempting to re-use for programming
+		r=Drive.Format(0,EbSz);
+		test(r==KErrNone);
+		r=DoControlModeWriteAndVerify(0xa5a5a5a5, 0);
+		test(r==KErrNone);
+		// Verify that data written in control mode can be further modified
+		r=DoControlModeWriteAndVerify(0x84848484, 0);
+		test(r==KErrNone);
+
+		test.Next(_L("\n(Subsequent) Control mode writes following an error"));
+
+		// Test that a control mode write can succeed after a previous error
+		// Use a failed object mode write attempt to the "B" half of a control mode region
+		// to cause the error
+		r=DoObjectModeWriteAndVerify(DriveCaps.iControlModeSize,(DriveCaps.iObjectModeSize>>2));
+		test(r==KErrGeneral);
+		r=DoControlModeWriteAndVerify(0x00000000, 0);
+		test(r==KErrNone);
+
+		test.Next(_L("\nControl mode boundary write test"));
+
+		r=DoControlModeBoundaryWriteAndVerify();
+		test(r==KErrNone);
+
+	}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+	test.Printf(_L("Press any key...\n"));
+	test.Getch();
+	test.End();
+	return KErrNone;
+	}