os/kernelhwsrv/kerneltest/f32test/demandpaging/t_mmcpaging.cpp
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
     1 // Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
     2 // All rights reserved.
     3 // This component and the accompanying materials are made available
     4 // under the terms of the License "Eclipse Public License v1.0"
     5 // which accompanies this distribution, and is available
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
     7 //
     8 // Initial Contributors:
     9 // Nokia Corporation - initial contribution.
    10 //
    11 // Contributors:
    12 //
    13 // Description:
    14 // e32test\mmu\t_mmcpaging.cpp
    15 // Suite of tests specifically to test the demand paging subsystem when
    16 // booted from MMC rather than NAND.
    17 // 002 Read/Write and Page test
    18 // 
    19 //
    20 
    21 //! @SYMTestCaseID			KBASE-T_MMCPAGING-0331
    22 //! @SYMTestType			UT
    23 //! @SYMPREQ				PREQ1110
    24 //! @SYMTestCaseDesc		Demand Paging MMC Paging tests.
    25 //! @SYMTestActions			001 Check that the rom is paged
    26 //! @SYMTestExpectedResults All tests should pass.
    27 //! @SYMTestPriority        High
    28 //! @SYMTestStatus          Implemented
    29 
    30 #include <e32test.h>
    31 RTest test(_L("T_MMCPAGING"));
    32 
    33 #include <e32rom.h>
    34 #include <e32svr.h>
    35 #include <u32hal.h>
    36 #include <f32file.h>
    37 #include <f32dbg.h>
    38 #include <d32locd.h>
    39 #include <hal.h>
    40 #define __TEST_PAGING_MEDIA_DRIVER__
    41 #include "mmcdp.h"
    42 
    43 
    44 
    45 TInt DriveNumber=-1;   // Parameter - Which drive?  -1 = autodetect.
    46 TInt locDriveNumber;
    47 
    48 TInt MaxDeferLoops=40; // Parameter - Defer test, for how long?
    49 TInt Maxloops=400;	   // Parameter - RW Soak, for how long?
    50 TBool Forever=EFalse;  // Parameter - RW Soak forever?
    51 
    52 TBool Testing=ETrue;	// Used to communicate when testing has finished between threads.
    53 
    54 RFs TheFs;
    55 TBusLocalDrive Drive;
    56 TLocalDriveCapsV4 DriveCaps;
    57 
    58 TInt PagedTrashCount=0; // Incremented by threads, is used to detect preemption.
    59 TInt GlobError=KErrNone; // To communicate an error between threads.
    60 TBool CtrlIOSupported=ETrue;
    61 
    62 
    63 //const TInt KDiskSectorShift = 9;
    64 const TInt KBufSizeInBytes = (32 * 1024);
    65 
    66 LOCAL_D TBuf8<KBufSizeInBytes> Buffer;
    67 
    68 
    69 
    70 // Three functions for the garbage test.
    71 // CreateFile creates a file, and sets up the buffer for WriteNumber.
    72 // After the code has finished writing numbers to the start,
    73 // CloseAndDestroy cleans up.
    74 
    75 void CreateFile(RFile &aFile,const TDesC& aFileName)
    76 	{
    77 	TBuf<256> fileName;	
    78 	fileName.Append((TChar)('A'+DriveNumber));
    79 	fileName+=_L(":\\f32-tst\\");
    80 	TInt r=TheFs.MkDirAll(fileName);
    81 	test(r==KErrNone || r== KErrAlreadyExists);
    82 
    83 	fileName += aFileName;	
    84 
    85 	r=aFile.Replace(TheFs,fileName,EFileWrite);
    86 	if (r!=KErrNone)
    87 		test.Printf(_L("Error %d: file '%S' could not be created\n"),r,&fileName);
    88 	test(r==KErrNone);	
    89 	Buffer.SetLength(4);
    90 	}
    91 	
    92 void CloseAndDestroy(RFile &aFile)
    93 	{
    94 	TBuf<256> fileName;	
    95 	aFile.FullName(fileName);
    96 	aFile.Close();
    97 	TheFs.Delete(fileName);
    98 	}
    99 
   100 TInt WriteNumber(RFile &aFile)
   101 	{
   102 	TInt r;
   103 	Buffer[0]++;
   104 	r = aFile.Write(0,Buffer);
   105 	if (r==KErrNone)
   106 		return aFile.Flush();
   107 	else
   108 		return r; 
   109 	}
   110 
   111 
   112 
   113 // Finds the 1st MMC drive, or checks the specified one fits requirements  
   114 
   115 static TInt FindFsMMCDrive()
   116 	{
   117 	TDriveList driveList;
   118 	TDriveInfo driveInfo;
   119 	TInt r=TheFs.DriveList(driveList);
   120     test(r == KErrNone);
   121 
   122 	TInt drvNum = DriveNumber;
   123 	if (drvNum<0)
   124 		drvNum = 0;
   125 	do
   126 		{
   127 	    if(!driveList[drvNum])
   128 	        continue;   //-- skip unexisting drive
   129 
   130 	    test(TheFs.Drive(driveInfo, drvNum) == KErrNone);
   131 
   132 		if(driveInfo.iMediaAtt&KMediaAttPageable)
   133 			{
   134 			// Internal MMC ?
   135 			if (driveInfo.iType == EMediaHardDisk && 
   136 				(driveInfo.iDriveAtt & KDriveAttInternal) &&
   137 				(!(driveInfo.iDriveAtt & KDriveAttRemovable)))
   138 				return (drvNum);
   139 			}
   140 		}
   141 	while(DriveNumber<0 && ++drvNum<KMaxDrives);
   142 
   143 	return (-1);
   144 	}
   145 
   146 
   147 //
   148 // Writes to main area for the entire disk and reads back to verify.
   149 // The function is called from TestMmcAccuratcy, which will have also
   150 // started the background RepeatedPagingThread
   151 //
   152 void testWriteMain()
   153 	{
   154 	TInt i;
   155 	TInt r;
   156 	TInt changeCount=0;
   157 	TInt totChangeCount=0;
   158 	TInt cCount=0;
   159 	TInt fullcCount=0;
   160 	TInt oldPagedTrashCount=0;
   161 	TInt delta=0;
   162 	TInt high=0;
   163 	TInt tot=0;
   164 	TInt fullTot=0;
   165 	TInt blockNo;
   166 	
   167 	SMmcStats stats;
   168 	TInt reqPageCount=0;	
   169 	TInt reqNormalCount=0;
   170 
   171 	
   172 	TInt readSize = KBufSizeInBytes/2;
   173 	TInt writeSize = KBufSizeInBytes/2;
   174 
   175 	Buffer.SetLength(2*readSize);
   176 
   177 	TPtr8 subBuf1(&Buffer[0],readSize);
   178 	TPtrC8 subBuf2(&Buffer[readSize], readSize);
   179 	
   180 	test.Printf(_L("writeSize = %d\n"), writeSize);
   181 
   182 //	TInt64 size = DriveCaps.iSize - (DriveCaps.iSize % readSize);
   183 	
   184 	for(i = 0; i<readSize; i++)
   185 		Buffer[readSize+i] = (char)(i%100);
   186 
   187 	// Zero Stats
   188 	if(CtrlIOSupported)
   189 		{
   190 		TPtr8 statsBuf((TUint8*) &stats, sizeof(stats));
   191 	 	test(Drive.ControlIO(KMmcGetStats,statsBuf,0) == KErrNone);
   192 		}
   193 
   194 
   195 	TFileName fileName = _L("?:\\f32-tst\\mmcpage.txt");
   196 	fileName[0] = (TText) ('A'+DriveNumber);
   197 
   198 
   199 	r = TheFs.MkDirAll(fileName);
   200 	test(r==KErrNone || r== KErrAlreadyExists);
   201 //	fileName += KTempFileName;	
   202 	RFile tempFile;
   203 	r=tempFile.Replace(TheFs,fileName,EFileWrite);
   204 	if (r!=KErrNone)
   205 		test.Printf(_L("Error %d: file '%S' could not be created\n"),r,&fileName);
   206 	test(r==KErrNone);	
   207 
   208 	TVolumeInfo volInfo;
   209 	r = TheFs.Volume(volInfo, DriveNumber);
   210 	test (r == KErrNone);
   211 
   212 	
   213 	TInt64 size = volInfo.iFree - (volInfo.iFree % readSize);
   214 	TInt maxFileSize = (size > KMaxTInt) ? KMaxTInt : (TInt) size;
   215 	
   216 	test.Printf(_L("Volume size %ld, free %ld maxFileSize %d file '%S'\n"), volInfo.iSize, volInfo.iFree, maxFileSize, &fileName);
   217 	
   218 	while (((totChangeCount<Maxloops) || Forever) && (GlobError==KErrNone))
   219 		{
   220 		
   221 		for(TInt pos=0;
   222 			((pos+writeSize) < maxFileSize) && ((totChangeCount<Maxloops) || Forever) && (GlobError==KErrNone);
   223 			pos+=(TUint)(readSize))
   224 			{
   225 			blockNo=I64LOW(pos / writeSize);
   226 			if (pos % (writeSize) == 0)
   227 				test.Printf(_L("Block %d at %u \r"), blockNo, I64LOW(pos));
   228 
   229 			//write the pattern
   230 			r = tempFile.Write(pos,subBuf2);
   231 			if (r != KErrNone)
   232 				test.Printf(_L("Write failed %d"), r);
   233 			test(r==KErrNone);
   234 
   235 			//read back and verify
   236 			r = tempFile.Read(pos,subBuf1,readSize);
   237 			test(r==KErrNone);
   238 
   239 			for(i=0;i<readSize;i++)
   240 				if(Buffer[i]!=Buffer[readSize+i])
   241 					{
   242 					r = KErrCorrupt;
   243 					break;
   244 					}
   245 			delta = PagedTrashCount-oldPagedTrashCount;
   246 			cCount++;
   247 			if (delta)
   248 				{	
   249 				if (delta>high)
   250 					high=delta;
   251 				tot+=delta;
   252 				
   253 				oldPagedTrashCount=PagedTrashCount;
   254 				changeCount++;
   255 				}
   256 			if (pos % (writeSize) == 0)
   257 				{				
   258 
   259 				if ((blockNo%80==0) && (blockNo!=0))
   260 					{
   261 					totChangeCount+=changeCount;
   262 					if(CtrlIOSupported)
   263 						{
   264 						test.Printf(_L("High%4d Avg%2d %d%% CC=%4d \n"), high, (TInt) (tot/cCount), (TInt)(changeCount*100)/cCount, totChangeCount);
   265 						
   266 						TPtr8 statsBuf((TUint8*) &stats, sizeof(stats));
   267 						Drive.ControlIO(KMmcGetStats,statsBuf,0);
   268 						test.Printf(_L("PR %d(%d%%) NR %d\n"), stats.iReqPage, (TInt) ((stats.iReqPage*100)/cCount), stats.iReqNormal);
   269 
   270 						test(stats.iReqPage>0);
   271 				 		reqPageCount+=stats.iReqPage;			 	
   272 				 		reqNormalCount+=stats.iReqNormal;			 	
   273 						}
   274 
   275 					high=0;
   276 					
   277 					fullTot+=tot;
   278 					tot=0;
   279 					
   280 					fullcCount+=cCount;
   281 					cCount=0;
   282 					changeCount=0;
   283 					}
   284 						
   285 				}
   286 			test(r==KErrNone);
   287 			}
   288 		if(CtrlIOSupported)
   289 			{
   290 			test.Printf(_L("Totals: Avg %2d %d%% CC=%4d \n"), fullTot/fullcCount, (TInt)(totChangeCount*100)/fullcCount, totChangeCount);
   291 			test.Printf(_L("PR %d(%d%%) NR %d\n"), reqPageCount,(TInt) (reqPageCount*100/fullcCount), reqNormalCount );
   292 			}
   293 
   294 		// If totChangeCount does not change, mmc maybe busy waiting.
   295 		test(totChangeCount>0);
   296 		}
   297 
   298 
   299 	tempFile.Close();
   300 	r = TheFs.Delete(fileName);
   301 	test (r == KErrNone);
   302 	
   303 	if (GlobError!=KErrNone)
   304 		{
   305 		test.Printf(_L("\nPageing failed with %x\n"), GlobError);
   306 		test(0);
   307 		}
   308 	else
   309 		test.Printf(_L("\ndone\n"));
   310 	}
   311 
   312 
   313 TUint8 ReadByte(volatile TUint8* aPtr)
   314 	{
   315 	return *aPtr;
   316 	}
   317 
   318 #define READ(a) ReadByte((volatile TUint8*)(a))
   319 
   320 TUint32 RandomNo =0;
   321 
   322 TUint32 Random()
   323 	{
   324 	RandomNo = RandomNo*69069+1;
   325 	return RandomNo;
   326 	}
   327 
   328 
   329 // Many instances of this run while testWriteMain runs,
   330 // to cause random background paging.
   331 
   332 LOCAL_C TInt RepeatedPagingThread(TAny* aUseTb)
   333 	{
   334 //	RTest test(_L("RepeatedPagingThread"));
   335 	TBool trashBurst = EFalse;
   336 	// This makes the paging system continually page stuff.
   337 	// get info about a paged ROM...
   338 	
   339 	TRomHeader* romHeader = (TRomHeader*)UserSvr::RomHeaderAddress();
   340 	TUint8* start = (TUint8*)romHeader+romHeader->iPageableRomStart;
   341 	TUint size = romHeader->iPageableRomSize;
   342 	TInt pageSize = 0;
   343 	PagedTrashCount=1;
   344 
   345 	UserSvr::HalFunction(EHalGroupKernel,EKernelHalPageSizeInBytes,&pageSize,0);
   346 	RandomNo=123;
   347 	PagedTrashCount++;
   348 
   349 	while (Testing)
   350 		{
   351 		TInt r=UserSvr::HalFunction(EHalGroupVM,EVMHalFlushCache,0,0);
   352 		if (Random() & 1)
   353 			User::AfterHighRes(500+Random() & 2047);
   354 
   355 		if (r<0)
   356 			{
   357 			GlobError=r;
   358 			PagedTrashCount=99;
   359 			return (KErrNone);
   360 			}
   361 		if (trashBurst)
   362 			{
   363 			if ((Random() & 0xf) == 0xf)
   364 				trashBurst=EFalse;
   365 			PagedTrashCount++;
   366 			}
   367 		else 
   368 			{
   369 			
   370 			for(TInt i=size/(pageSize); (i>0) && !trashBurst; --i)
   371 				{
   372 				READ(start+((TInt64(Random())*TInt64(size))>>32));
   373 				if ((RandomNo & 0x3f) == 0x3f)
   374 					{
   375 					trashBurst= (TBool) aUseTb;
   376 					}
   377 				PagedTrashCount++;
   378 				if (RandomNo & 1)
   379 					User::AfterHighRes(500+Random() & 2047);
   380 				}
   381 			}
   382 	
   383 		}
   384 	return(KErrNone);
   385 	}
   386 	
   387 
   388 // This starts up multiple instances of repeatedPagingThread, and runs testWriteMain.
   389 // After its done, it calls format, to clean up the drive.
   390 
   391 void TestMmcAccuratcy()
   392 	{
   393 	RThread thisThread;
   394 	const TInt KNoThreads=10;
   395 	TInt i;
   396 	test.Printf(_L("Reset stats\n"));
   397 
   398 	i=UserSvr::HalFunction(EHalGroupMedia,EMediaHalResetConcurrencyInfo,(TAny*)locDriveNumber,(TAny*)EMediaPagingStatsRom);
   399 	test(i==KErrNone || i==KErrNotSupported);
   400 	if(i==KErrNotSupported)
   401 		test.Printf(_L("Concurrency stats not supported on this build\n"));
   402 	i=UserSvr::HalFunction(EHalGroupMedia,EMediaHalResetPagingBenchmark,(TAny*)locDriveNumber,(TAny*)EMediaPagingStatsRom);
   403 	test(i==KErrNone || i==KErrNotSupported);
   404 	if(i==KErrNotSupported)
   405 		test.Printf(_L("Benchmark stats not supported on this build\n"));
   406 
   407 	if (Maxloops>0)
   408 		{
   409 		TRequestStatus stat[KNoThreads];
   410 		// Start Read Test
   411 		RThread repeatedPagingThread[KNoThreads];
   412 		
   413 		test.Next(_L("Read/Write and Page test"));
   414 
   415 		for (i=0; i<KNoThreads; i++)
   416 			{
   417 			test(repeatedPagingThread[i].Create(_L(""),RepeatedPagingThread,KDefaultStackSize,NULL,(TAny*) ETrue)==KErrNone);
   418 			repeatedPagingThread[i].Logon(stat[i]);
   419 			test(stat[i]==KRequestPending);	
   420 			repeatedPagingThread[i].Resume();
   421 			}
   422 		// Start repeated paging.
   423 		thisThread.SetPriority(EPriorityMore);
   424 		Testing=ETrue;
   425 		testWriteMain();
   426 		Testing = 0;
   427 		thisThread.SetPriority(EPriorityNormal);
   428 		for (i=0; i<KNoThreads; i++)
   429 	 		User::WaitForRequest(stat[i]);
   430 	 		
   431 		test.Printf(_L("Collect concurrency stats\n"));
   432 		SMediaROMPagingConcurrencyInfo info;
   433 		SPagingBenchmarkInfo infoBench;
   434 		i=UserSvr::HalFunction(EHalGroupMedia,EMediaHalGetROMConcurrencyInfo,(TAny*)locDriveNumber,&info);
   435 		test(i==KErrNone || i==KErrNotSupported);
   436 		TInt r=UserSvr::HalFunction(EHalGroupMedia,EMediaHalGetROMPagingBenchmark,(TAny*)locDriveNumber,&infoBench);
   437 		test(r==KErrNone || r==KErrNotSupported);
   438 		if(i==KErrNone)
   439 			{
   440 			test.Printf(_L("Media concurrency stats:\n\n"));
   441 			test.Printf(_L("The total number of page in requests issued whilst processing other page in requests: %d\n"),info.iTotalConcurrentReqs);
   442 			test.Printf(_L("The total number of page in requests issued with at least one queue not empty: %d\n"),info.iTotalReqIssuedNonEmptyQ);
   443 			test.Printf(_L("The maximum number of pending page in requests in the main queue any time during this session: %d\n"),info.iMaxReqsInPending);
   444 			test.Printf(_L("The maximum number of pending page in requests in the deferred queue any time during this session: %d\n"),info.iMaxReqsInDeferred);
   445 			test.Printf(_L("The total number of page in requests first-time deferred during this session: %d\n"),info.iTotalFirstTimeDeferrals);
   446 			test.Printf(_L("The total number of page in requests re-deferred during this session: %d\n"),info.iTotalReDeferrals);
   447 			test.Printf(_L("The maximum number of deferrals of any single page in request during this session: %d\n"),info.iMaxDeferrals);
   448 			test.Printf(_L("The total number of times the main queue was emptied when completing an asynchronous request during this session: %d\n"),info.iTotalSynchEmptiedMainQ);
   449 			test.Printf(_L("The total number of page in requests serviced from main queue when completing an asynchronous request: %d\n"),info.iTotalSynchServicedFromMainQ);
   450 			test.Printf(_L("The total number of page in requests deferred after being picked out of main queue when completing an asynchronous request: %d\n"),info.iTotalSynchDeferredFromMainQ);
   451 			test.Printf(_L("The total number of times the page in DFC run with an empty main queue during this session: %d\n"),info.iTotalRunDry);
   452 			test.Printf(_L("The total number of dry runs of paging DFC avoided during this session: %d\n"),info.iTotalDryRunsAvoided);
   453 			}
   454 
   455 		if(r==KErrNone)
   456 			{
   457 			TInt freq = 0;
   458 			r = HAL::Get(HAL::EFastCounterFrequency, freq);
   459 			if (r==KErrNone)
   460 				{
   461 				TReal mult = 1000000.0 / freq;
   462 				TReal min = 0.0;
   463 				TReal max = 0.0;
   464 				TReal avg = 0.0;
   465 				if (infoBench.iCount != 0)
   466 					{
   467 					min = infoBench.iMinTime * mult;
   468 					max = infoBench.iMaxTime * mult;
   469 					avg = (infoBench.iTotalTime * mult) / infoBench.iCount;
   470 					}
   471 				test.Printf(_L("Media benchmarks:\n\n"));
   472 				test.Printf(_L("The total number of page in requests issued: %d\n"),infoBench.iCount);
   473 				test.Printf(_L("The average latency of any page in request in the Media subsystem: %9.1f(us)\n"),avg);
   474 				test.Printf(_L("The maximum latency of any page in request in the Media subsystem: %9.1f(us)\n"),max);
   475 				test.Printf(_L("The minimum latency of any page in request in the Media subsystem: %9.1f(us)\n"),min);
   476 				}
   477 			}
   478 		}
   479 		else
   480 			test.Next(_L("Read/Write test - Skipped!"));
   481 
   482 	}
   483 	
   484 
   485 // ************************************************************************************
   486 	
   487 	
   488 /*
   489 // This code causes a flush
   490 // It is done in a second thread to see if you really do get just
   491 // one deferral, with the other page requests just waiting in line.
   492 // (Paging is not re-entrant)
   493 
   494 TInt PagesBeingPaged=0;
   495 RMutex PageMutex;
   496 RSemaphore PageSemaphore;
   497 RSemaphore PageDoneSemaphore;
   498  
   499 LOCAL_C TInt CausePage(TAny*)
   500 	{	
   501 	TRomHeader* romHeader = (TRomHeader*)UserSvr::RomHeaderAddress();
   502 	TUint8* start = (TUint8*)romHeader+romHeader->iPageableRomStart;
   503 	TUint size = romHeader->iPageableRomSize;
   504 	TUint8* addr=NULL;
   505 	TBool flush;
   506 	while (Testing)
   507 		{
   508 		// Wait on semaphore
   509 		PageSemaphore.Wait();
   510 		flush = (PagesBeingPaged==0);
   511 		PagesBeingPaged++;
   512 		addr=start+((TInt64(Random())*TInt64(size))>>32);	
   513 		PageDoneSemaphore.Signal();
   514 		if (flush)
   515 			UserSvr::HalFunction(EHalGroupVM,EVMHalFlushCache,0,0);
   516 		READ(addr);
   517 		PageMutex.Wait();
   518 		PagesBeingPaged--;
   519 		PageMutex.Signal();
   520 		}
   521 	return 0;
   522 	}
   523 */
   524 
   525 // ************************************************************************************
   526 
   527 //
   528 // The gubbins that starts all the tests
   529 //
   530 // ParseCommandLine reads the arguments and sets globals accordingly.
   531 //
   532 
   533 void ParseCommandLine()
   534 	{
   535 	TBuf<32> args;
   536 	User::CommandLine(args);
   537 	TLex lex(args);
   538 	
   539 	FOREVER
   540 		{
   541 		
   542 		TPtrC token=lex.NextToken();
   543 		if(token.Length()!=0)
   544 			{
   545 			if ((token.Length()==2) && (token[1]==':'))
   546 				DriveNumber=User::UpperCase(token[0])-'A';
   547 			else if (token.Length()==1)
   548 				{
   549 				TChar driveLetter = User::UpperCase(token[0]); 
   550 				if ((driveLetter>='A') && (driveLetter<='Z'))
   551 					DriveNumber=driveLetter - (TChar) 'A';
   552 				else 
   553 					test.Printf(_L("Unknown argument '%S' was ignored.\n"), &token);
   554 				}
   555 			else if ((token==_L("help")) || (token==_L("-h")) || (token==_L("-?")))
   556 				{
   557 				test.Printf(_L("\nUsage:  t_mmcpaging <driveletter> [rwsoak <cc>] [defer <c>]\n'-' indicated infinity.\n\n"));
   558 				test.Getch();
   559 				Maxloops=0;
   560 				}
   561 			else if (token==_L("rwsoak"))
   562 				{
   563 				TPtrC val=lex.NextToken();
   564 				TLex lexv(val);
   565 				TInt v;
   566 
   567 				if (val==_L("-"))
   568 					Forever=ETrue;
   569 				else
   570 					if (lexv.Val(v)==KErrNone)
   571 						Maxloops=v;
   572 					else
   573 						test.Printf(_L("Bad value for rwsoak '%S' was ignored.\n"), &val);
   574 				}
   575 			else if (token==_L("defer"))
   576 				{
   577 				TPtrC val=lex.NextToken();
   578 				TLex lexv(val);
   579 				TInt v;
   580 
   581 				if (val==_L("-"))
   582 					MaxDeferLoops=KMaxTInt;
   583 				else
   584 					if (lexv.Val(v)==KErrNone)
   585 						MaxDeferLoops=v;
   586 					else
   587 						test.Printf(_L("Bad value for defer '%S' was ignored.\n"), &val);
   588 				}	
   589 			else
   590 				test.Printf(_L("Unknown argument '%S' was ignored.\n"), &token);
   591 			}
   592 		else
   593 			break;
   594 		
   595 		}
   596 	}
   597 
   598 //
   599 // E32Main
   600 //
   601 
   602 TInt E32Main()
   603 	{
   604 	TInt r;
   605 	test.Title();
   606 
   607 	test.Printf(_L("key\n---\n"));	
   608 	test.Printf(_L("PR: Paging requests\n"));
   609 	test.Printf(_L("NR: Normal requests\n\n"));
   610 
   611 	
   612 	test.Start(_L("Check that the rom is paged"));
   613 	TRomHeader* romHeader = (TRomHeader*)UserSvr::RomHeaderAddress();
   614 	if (romHeader->iPageableRomStart==NULL)
   615 		test.Printf(_L("Test ROM is not paged - test skipped!\r\n"));
   616 	else
   617 		{
   618 		ParseCommandLine();	
   619 		test(TheFs.Connect()==KErrNone);
   620 
   621 		r=UserSvr::HalFunction(EHalGroupVM,EVMHalFlushCache,0,0);
   622 		if(r<0)
   623 			{
   624 			test.Printf(_L("DemandPagingFlushPages Error = %d\n"),r);
   625 			test(0);
   626 			}
   627 			
   628 		DriveNumber = FindFsMMCDrive();	
   629 		
   630 		if(DriveNumber<0)
   631 			test.Printf(_L("MMC Flash not found - test skipped!\r\n"));
   632 		else
   633 			{
   634 			RFile file;
   635 			TBuf<256> fileName;	
   636 			fileName.Append((TChar)('A'+DriveNumber));
   637 			fileName+=_L(":\\f32-tst\\");
   638 			TInt r=TheFs.MkDirAll(fileName);
   639 			test(r==KErrNone || r== KErrAlreadyExists);
   640 			fileName += _L("redglare.txt");
   641 			r=file.Replace(TheFs,fileName,EFileWrite);
   642 			if (r!=KErrNone)
   643 				test.Printf(_L("Error %d: file '%S' could not be created\n"),r,&fileName);
   644 			test(r==KErrNone);
   645 			r=file.Write(_L8("The red glare of an ancient sun reflecting on the leaden surface of a primeval soup of decomposing matter"));
   646 			if (r!=KErrNone)
   647 				test.Printf(_L("Error %d: could not write to file\n"),r);
   648 			test(r==KErrNone);
   649 
   650 			test(file.Flush() == KErrNone);
   651 
   652 			SBlockMapInfo info;
   653 			TInt64 start=0;
   654 			r=file.BlockMap(info,start, -1,ETestDebug);
   655 			if (r!=KErrNone && r!=KErrCompletion)
   656 				test.Printf(_L("Error %d: could not obtain block map\n"),r);
   657 			test(r==KErrNone || r==KErrCompletion);
   658 			locDriveNumber=info.iLocalDriveNumber;
   659 			test.Printf(_L("Found drive: %c (MMC drive %d)\r\n"), DriveNumber+'A',locDriveNumber);
   660 			file.Close();
   661 			
   662 			TDriveInfo driveInfo;
   663 			test(TheFs.Drive(driveInfo, DriveNumber) == KErrNone);
   664 
   665 			// Connect to device driver
   666 			TBool changeFlag = EFalse;
   667 			r = Drive.Connect(locDriveNumber,changeFlag);
   668 			TPckg<TLocalDriveCapsV4>	capsPack(DriveCaps);
   669 			Drive.Caps(capsPack);
   670 			test(r == KErrNone);
   671 
   672 			SMmcStats stats;
   673 			TPtr8 statsBuf((TUint8*) &stats, sizeof(stats));
   674 			r = Drive.ControlIO(KMmcGetStats,statsBuf,0);
   675 
   676 
   677 			if (r!=KErrNone)
   678 				{
   679 				test.Printf(_L("LocalDrive does not support testing IO Requests\n"));
   680 				CtrlIOSupported=EFalse;
   681 				}
   682 			test.Printf(_L("LocalDrive Connected\n"));
   683 			//
   684 			// Run tests	
   685 			//
   686 			TestMmcAccuratcy();
   687 			// 
   688 			// Free device and end test program
   689 			//
   690 			Drive.Disconnect();
   691 			}	
   692 		}
   693 		
   694 	test.End();
   695 	return 0;
   696 	}
   697