Update contrib.
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".
8 // Initial Contributors:
9 // Nokia Corporation - initial contribution.
14 // e32test\mmu\t_nandpaging.cpp
15 // Suite of tests specifically to test the demand paging subsystem when
17 // 002 Read/Write and Page test
22 //! @SYMTestCaseID KBASE-T_NANDPAGING-0332
25 //! @SYMTestCaseDesc Demand Paging Nand Paging tests.
26 //! @SYMTestActions 001 Check that the rom is paged
27 //! @SYMTestExpectedResults All tests should pass.
28 //! @SYMTestPriority High
29 //! @SYMTestStatus Implemented
32 RTest test(_L("T_NANDPAGING"));
43 TInt DriveNumber=-1; // Parameter - Which drive? -1 = autodetect.
46 TInt MaxDeferLoops=40; // Parameter - Defer test, for how long?
47 TInt Maxloops=400; // Parameter - RW Soak, for how long?
48 TBool Forever=EFalse; // Parameter - RW Soak forever?
50 TBool Testing=ETrue; // Used to communicate when testing has finished between threads.
54 TLocalDriveCapsV4 DriveCaps;
56 TInt PagedTrashCount=0; // Incremented by threads, is used to detect preemption.
57 TInt GlobError=KErrNone; // To communicate an error between threads.
58 TBool CtrlIoCollectGarbageSupported = ETrue;
59 TBool CtrlIoGetDeferStatsSupported = ETrue;
62 const TInt KDiskSectorShift=9;
63 const TInt KBufSizeInSectors=8;
64 const TInt KBufSizeInBytes=(KBufSizeInSectors<<KDiskSectorShift)*40;
66 LOCAL_D TBuf8<KBufSizeInBytes> Buffer;
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.
75 void CreateFile(RFile &aFile,const TDesC& aFileName)
78 fileName.Append((TChar)('A'+DriveNumber));
79 fileName+=_L(":\\f32-tst\\");
80 TInt r=TheFs.MkDirAll(fileName);
81 test(r==KErrNone || r== KErrAlreadyExists);
83 fileName += aFileName;
85 r=aFile.Replace(TheFs,fileName,EFileWrite);
87 test.Printf(_L("Error %d: file '%S' could not be created\n"),r,&fileName);
92 void CloseAndDestroy(RFile &aFile)
95 aFile.FullName(fileName);
97 TheFs.Delete(fileName);
100 TInt WriteNumber(RFile &aFile)
104 r = aFile.Write(0,Buffer);
106 return aFile.Flush();
113 // The r/w soaktest leaves the drive in a mess.
114 // Formatting is needed afterwards.
116 void silentFormat(TInt driveNo)
118 TBuf<4> driveBuf=_L("?:\\");
122 driveBuf[0] = (TText)(driveNo + 'A');
124 TInt r = format.Open(TheFs, driveBuf, EHighDensity, count);
129 r=format.Next(count);
136 // Finds the 1st r/w NAND drive, or checks the specified one fits requirements
138 static TInt FindFsNANDDrive()
140 TDriveList driveList;
141 TDriveInfo driveInfo;
142 TInt r=TheFs.DriveList(driveList);
145 for (TInt drvNum= (DriveNumber<0)?0:DriveNumber; drvNum<KMaxDrives; ++drvNum)
147 if(!driveList[drvNum])
148 continue; //-- skip unexisting drive
150 test(TheFs.Drive(driveInfo, drvNum) == KErrNone);
152 if ((driveInfo.iMediaAtt&KMediaAttPageable) &&
153 (driveInfo.iType == EMediaNANDFlash) &&
154 (driveInfo.iDriveAtt & KDriveAttInternal))
156 TBool readOnly = driveInfo.iMediaAtt & KMediaAttWriteProtected; // skip ROFS partitions
159 if ((drvNum==DriveNumber) || (DriveNumber<0)) // only test if running on this drive
171 // Writes to main area for the entire disk and reads back to verify.
172 // The function is called from TestNandAccuratcy, which will have also
173 // started the background RepeatedPagingThread
180 TInt totChangeCount=0;
183 TInt oldPagedTrashCount=0;
191 TInt readSize = (64*1024);
192 TInt64 size = DriveCaps.iSize - (DriveCaps.iSize % readSize);
194 // print position every 128K
195 TInt64 printBlockPos = 128 * 1024;
196 test (size > printBlockPos);
198 // check for paging activity every 1MB
199 TInt64 checkChangePos = 1024*1024;
200 while (checkChangePos > size)
205 TInt pageGarbageCount=0;
206 TInt pageOtherCount=0;
207 TInt normalGarbageCount=0;
208 TInt normalOtherCount=0;
211 Buffer.SetLength(2*readSize);
213 TPtr8 subBuf1(&Buffer[0],readSize);
214 TPtrC8 subBuf2(&Buffer[readSize], readSize);
216 test.Printf(_L("Page size = %d\n"), DriveCaps.iNumBytesMain);
217 test.Printf(_L("Erase block size = %d\n"), DriveCaps.iEraseBlockSize);
218 test.Printf(_L("Media size (rounded down) = %ld\n"), size);
220 for(i = 0; i<readSize; i++)
221 Buffer[readSize+i] = (char)(i%100);
224 if(CtrlIoGetDeferStatsSupported)
226 TPtr8 statsBuf((TUint8*) &stats, sizeof(stats));
227 test(Drive.ControlIO(KNandGetDeferStats,statsBuf,0)==KErrNone);
231 while (((totChangeCount<Maxloops) || Forever) && (GlobError==KErrNone))
234 (pos<size) && ((totChangeCount<Maxloops) || Forever) && (GlobError==KErrNone);
235 pos+=(TUint)(readSize))
237 blockNo=I64LOW(pos / DriveCaps.iEraseBlockSize);
238 if ((pos % printBlockPos) == 0)
239 test.Printf(_L("Block %d at pos %lu \r"), blockNo, pos);
242 r = Drive.Write(pos,subBuf2);
245 //read back and verify
246 r = Drive.Read(pos,readSize,subBuf1);
249 for(i=0;i<readSize;i++)
250 if(Buffer[i]!=Buffer[readSize+i])
255 delta = PagedTrashCount-oldPagedTrashCount;
263 oldPagedTrashCount=PagedTrashCount;
267 if ((pos > 0) && (pos % checkChangePos) == 0)
269 totChangeCount+=changeCount;
270 if(CtrlIoGetDeferStatsSupported)
272 test.Printf(_L("\nHigh%4d Avg%2d %d%% CC=%4d \n"), high, (TInt) (tot/cCount), (TInt)(changeCount*100)/cCount, totChangeCount);
274 TPtr8 statsBuf((TUint8*) &stats, sizeof(stats));
275 Drive.ControlIO(KNandGetDeferStats,statsBuf,0);
276 test.Printf(_L("PG %d PO %d(%d%%) NG %d NO %d\n"),stats.iPageGarbage, stats.iPageOther, (TInt) ((stats.iPageOther*100)/cCount), stats.iNormalGarbage, stats.iNormalOther);
278 test(stats.iPageOther>0);
279 pageGarbageCount+=stats.iPageGarbage;
280 pageOtherCount+=stats.iPageOther;
281 normalGarbageCount+=stats.iNormalGarbage;
282 normalOtherCount+=stats.iNormalOther;
298 if (CtrlIoGetDeferStatsSupported)
300 test.Printf(_L("\nTotals: Avg %2d %d%% CC=%4d \n"), fullTot/fullcCount, (TInt)(totChangeCount*100)/fullcCount, totChangeCount);
301 test.Printf(_L("PG %d PO %d(%d%%) NG %d NO %d\n"),pageGarbageCount, pageOtherCount,(TInt) (pageOtherCount*100/fullcCount), normalGarbageCount, normalOtherCount );
304 // If totChangeCount does not change, nand maybe busy waiting.
305 test(totChangeCount>0);
308 if (GlobError!=KErrNone)
310 test.Printf(_L("\nPaging failed with %x\n"), GlobError);
314 test.Printf(_L("\ndone\n"));
318 TUint8 ReadByte(volatile TUint8* aPtr)
323 #define READ(a) ReadByte((volatile TUint8*)(a))
329 RandomNo = RandomNo*69069+1;
334 // Many instances of this run while testWriteMain runs,
335 // to cause random background paging.
337 LOCAL_C TInt RepeatedPagingThread(TAny* aUseTb)
339 TBool trashBurst = EFalse;
340 // This makes the paging system continually page stuff.
341 // get info about a paged ROM...
343 TRomHeader* romHeader = (TRomHeader*)UserSvr::RomHeaderAddress();
344 TUint8* start = (TUint8*)romHeader+romHeader->iPageableRomStart;
345 TUint size = romHeader->iPageableRomSize;
349 UserSvr::HalFunction(EHalGroupKernel,EKernelHalPageSizeInBytes,&pageSize,0);
355 TInt r=UserSvr::HalFunction(EHalGroupVM,EVMHalFlushCache,0,0);
357 User::AfterHighRes(500+Random() & 2047);
367 if ((Random() & 0xf) == 0xf)
374 for(TInt i=size/(pageSize); (i>0) && !trashBurst; --i)
376 READ(start+((TInt64(Random())*TInt64(size))>>32));
377 if ((RandomNo & 0x3f) == 0x3f)
379 trashBurst= (TBool) aUseTb;
383 User::AfterHighRes(500+Random() & 2047);
392 // This starts up multiple instances of repeatedPagingThread, and runs testWriteMain.
393 // After its done, it calls format, to clean up the drive.
395 void TestNandAccuratcy()
398 const TInt KNoThreads=10;
400 test.Printf(_L("Reset concurrency stats\n"));
402 i=UserSvr::HalFunction(EHalGroupMedia,EMediaHalResetConcurrencyInfo,(TAny*)locDriveNumber,(TAny*)EMediaPagingStatsRom);
403 test(i==KErrNone || i==KErrNotSupported);
404 if(i==KErrNotSupported)
405 test.Printf(_L("Concurrency stats not supported on this build\n"));
406 i=UserSvr::HalFunction(EHalGroupMedia,EMediaHalResetPagingBenchmark,(TAny*)locDriveNumber,(TAny*)EMediaPagingStatsRom);
407 test(i==KErrNone || i==KErrNotSupported);
408 if(i==KErrNotSupported)
409 test.Printf(_L("Benchmark stats not supported on this build\n"));
413 TRequestStatus stat[KNoThreads];
415 RThread repeatedPagingThread[KNoThreads];
417 test.Next(_L("Read/Write and Page test"));
420 for (i=0; i<KNoThreads; i++)
423 test(repeatedPagingThread[i].Create(_L(""),RepeatedPagingThread,KDefaultStackSize,NULL,(TAny*) ETrue)==KErrNone);
424 repeatedPagingThread[i].Logon(stat[i]);
425 test(stat[i]==KRequestPending);
426 repeatedPagingThread[i].Resume();
428 // Start repeated paging.
429 thisThread.SetPriority(EPriorityMore);
432 thisThread.SetPriority(EPriorityNormal);
433 for (i=0; i<KNoThreads; i++)
434 User::WaitForRequest(stat[i]);
436 test.Printf(_L("Collect concurrency stats\n"));
437 SMediaROMPagingConcurrencyInfo info;
438 SPagingBenchmarkInfo infoBench;
439 i=UserSvr::HalFunction(EHalGroupMedia,EMediaHalGetROMConcurrencyInfo,(TAny*)locDriveNumber,&info);
440 test(i==KErrNone || i==KErrNotSupported);
441 TInt r=UserSvr::HalFunction(EHalGroupMedia,EMediaHalGetROMPagingBenchmark,(TAny*)locDriveNumber,&infoBench);
442 test(r==KErrNone || r==KErrNotSupported);
445 test.Printf(_L("Media concurrency stats:\n\n"));
446 test.Printf(_L("The total number of page in requests issued whilst processing other page in requests: %d\n"),info.iTotalConcurrentReqs);
447 test.Printf(_L("The total number of page in requests issued with at least one queue not empty: %d\n"),info.iTotalReqIssuedNonEmptyQ);
448 test.Printf(_L("The maximum number of pending page in requests in the main queue any time during this session: %d\n"),info.iMaxReqsInPending);
449 test.Printf(_L("The maximum number of pending page in requests in the deferred queue any time during this session: %d\n"),info.iMaxReqsInDeferred);
450 test.Printf(_L("The total number of page in requests first-time deferred during this session: %d\n"),info.iTotalFirstTimeDeferrals);
451 test.Printf(_L("The total number of page in requests re-deferred during this session: %d\n"),info.iTotalReDeferrals);
452 test.Printf(_L("The maximum number of deferrals of any single page in request during this session: %d\n"),info.iMaxDeferrals);
453 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);
454 test.Printf(_L("The total number of page in requests serviced from main queue when completing an asynchronous request: %d\n"),info.iTotalSynchServicedFromMainQ);
455 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);
456 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);
457 test.Printf(_L("The total number of dry runs of paging DFC avoided during this session: %d\n"),info.iTotalDryRunsAvoided);
463 r = HAL::Get(HAL::EFastCounterFrequency, freq);
466 TReal mult = 1000000.0 / freq;
470 if (infoBench.iCount != 0)
472 min = infoBench.iMinTime * mult;
473 max = infoBench.iMaxTime * mult;
474 avg = (infoBench.iTotalTime * mult) / infoBench.iCount;
476 test.Printf(_L("Media benchmarks:\n\n"));
477 test.Printf(_L("The total number of page in requests issued: %d\n"),infoBench.iCount);
478 test.Printf(_L("The average latency of any page in request in the Media subsystem: %9.1f(us)\n"),avg);
479 test.Printf(_L("The maximum latency of any page in request in the Media subsystem: %9.1f(us)\n"),max);
480 test.Printf(_L("The minimum latency of any page in request in the Media subsystem: %9.1f(us)\n"),min);
484 test.Printf(_L("Formatting...\n"));
485 silentFormat(DriveNumber);
488 test.Next(_L("Read/Write test - Skipped!"));
493 // ************************************************************************************
496 // This code causes a flush
497 // It is done in a second thread to see if you really do get just
498 // one deferral, with the other page requests just waiting in line.
499 // (Paging is not re-entrant)
501 TInt PagesBeingPaged=0;
503 RSemaphore PageSemaphore;
504 RSemaphore PageDoneSemaphore;
506 LOCAL_C TInt CausePage(TAny*)
508 TRomHeader* romHeader = (TRomHeader*)UserSvr::RomHeaderAddress();
509 TUint8* start = (TUint8*)romHeader+romHeader->iPageableRomStart;
510 TUint size = romHeader->iPageableRomSize;
515 PageSemaphore.Wait(); // wait for main thread to want paging.
516 flush = (PagesBeingPaged==0);
517 addr=start+((TInt64(Random())*TInt64(size))>>32);
518 PageDoneSemaphore.Signal(); // Acknolage request.
525 UserSvr::HalFunction(EHalGroupVM,EVMHalFlushCache,0,0);
536 // TestDefered causes garbage collection, and then triggers paging to happen, which should be defered.
537 // One would only expect one defered request, as the paging system is not reentrant, but this is checked.
541 if (MaxDeferLoops==0)
543 test.Next(_L("Defering test - Skipped!"));
548 TInt writesNeeded=100;
556 TInt pageGarbageCount=0;
557 TInt pageOtherCount=0;
558 TInt normalGarbageCount=0;
559 TInt normalOtherCount=0;
562 // Set up thread sync
563 test(PageMutex.CreateLocal()==KErrNone);
564 test(PageSemaphore.CreateLocal(0)==KErrNone);
565 test(PageDoneSemaphore.CreateLocal(0)==KErrNone);
569 const TInt KMaxPageThreads = 2;
570 UserSvr::HalFunction(EHalGroupVM,EVMHalFlushCache,0,0);
572 RThread pageThread[KMaxPageThreads];
573 TRequestStatus stat[KMaxPageThreads];
575 for (i=0; i<KMaxPageThreads; i++)
577 test(pageThread[i].Create(_L(""),CausePage,KDefaultStackSize,NULL,NULL)==KErrNone);
578 pageThread[i].Logon(stat[i]);
579 test(stat[i]==KRequestPending);
580 pageThread[i].SetPriority(EPriorityMore);
581 pageThread[i].Resume();
585 test.Next(_L("Defering test"));
588 TPtr8 statsBuf((TUint8*) &stats, sizeof(stats));
589 test(Drive.ControlIO(KNandGetDeferStats,statsBuf,0)==KErrNone);
591 CreateFile(tempFile,_L("nandpage.txt"));
594 for (ii=0; ii<MaxDeferLoops; ii++) // Repeat the test, 'MaxDeferLoops' number of times. This can be set on cammand line.
597 do // while ((pageGarbageCount==0) && (timeout>0));
598 // ie, while garbage collection hasn't happened, or timed out
603 normalGarbageCount=0;
606 // Give somethng for garbage collector to collect
607 for (i=0; i<writesNeeded; i++)
608 test(WriteNumber(tempFile)==KErrNone);
610 // Force Collection. (Normally only happens in Idle)
611 r = Drive.ControlIO(KNandCollectGarbage,NULL,NULL);
614 // Since garbage Colleciton should be going now, watch it, until its finished.
617 runs = PagesBeingPaged;
618 for (i=runs; i<KMaxPageThreads; i++)
619 PageSemaphore.Signal(); // Trigger Paging.
621 for (i=runs; i<KMaxPageThreads; i++)
622 PageDoneSemaphore.Wait();
625 do { // If we get zero hits, maybe the page hasnt hit yet.
627 User::AfterHighRes(1000+Random() & 2047); // Throw some uncertainly into things
629 TPtr8 statsBuf((TUint8*) &stats, sizeof(stats));
630 r = Drive.ControlIO(KNandGetDeferStats,statsBuf,0);
631 test (r == KErrNone);
632 pageGarbageCount+=stats.iPageGarbage;
633 pageOtherCount+=stats.iPageOther;
634 normalGarbageCount+=stats.iNormalGarbage;
635 normalOtherCount+=stats.iNormalOther;
636 } while ((pageGarbageCount==0) && (tries>0)); // If we get zero hits, maybe the page hasnt hit yet
638 while (stats.iPageGarbage>0); // Keep going until collection seems to have finished.
640 // The paging system is not reentrant, so should never get more then one.
641 test(stats.iPageGarbage<2);
643 test.Printf(_L("%d: PG %d PO %d NG %d NO %d\n"),ii,pageGarbageCount, pageOtherCount, normalGarbageCount, normalOtherCount );
644 // if no collection, probebly didnt write enough to trigger it, so up the quantity.
645 if (pageGarbageCount==0)
647 writesNeeded+=writesNeeded/2;
648 test.Printf(_L("Writes needed = %d\n"),writesNeeded);
652 while ((pageGarbageCount==0) && (timeout>0));
655 } // end for MaxDeferLoops.
659 Testing=EFalse; // Setting this causes the CausePage threads to exit.
661 // Wait for threads to exit, signaling the semaphore in case they where waiting on it.
662 for (i=0; i<KMaxPageThreads; i++)
663 PageSemaphore.Signal();
664 for (i=0; i<KMaxPageThreads; i++)
665 User::WaitForRequest(stat[i]);
668 PageSemaphore.Close();
669 PageDoneSemaphore.Close();
670 CloseAndDestroy(tempFile);
674 // ************************************************************************************
677 // The gubbins that starts all the tests
679 // ParseCommandLine reads the arguments and sets globals accordingly.
682 void ParseCommandLine()
685 User::CommandLine(args);
691 TPtrC token=lex.NextToken();
692 if(token.Length()!=0)
694 if ((token.Length()==2) && (token[1]==':'))
695 DriveNumber=User::UpperCase(token[0])-'A';
696 else if (token.Length()==1)
698 TChar driveLetter = User::UpperCase(token[0]);
699 if ((driveLetter>='A') && (driveLetter<='Z'))
700 DriveNumber=driveLetter - (TChar) 'A';
702 test.Printf(_L("Unknown argument '%S' was ignored.\n"), &token);
704 else if ((token==_L("help")) || (token==_L("-h")) || (token==_L("-?")))
706 test.Printf(_L("\nUsage: t_nandpaging <driveletter> [rwsoak <cc>] [defer <c>]\n'-' indicated infinity.\n\n"));
710 else if (token==_L("rwsoak"))
712 TPtrC val=lex.NextToken();
719 if (lexv.Val(v)==KErrNone)
722 test.Printf(_L("Bad value for rwsoak '%S' was ignored.\n"), &val);
724 else if (token==_L("defer"))
726 TPtrC val=lex.NextToken();
731 MaxDeferLoops=KMaxTInt;
733 if (lexv.Val(v)==KErrNone)
736 test.Printf(_L("Bad value for defer '%S' was ignored.\n"), &val);
739 test.Printf(_L("Unknown argument '%S' was ignored.\n"), &token);
756 test.Printf(_L("key\n---\n"));
757 test.Printf(_L("PG: Paging requests defered due to Garbage\n"));
758 test.Printf(_L("PO: Paging requests defered due to other operations\n"));
759 test.Printf(_L("NG: Normal requests defered due to Garbage\n"));
760 test.Printf(_L("NO: Normal requests defered due to other operations\n\n"));
763 test.Start(_L("Check that the rom is paged"));
764 TRomHeader* romHeader = (TRomHeader*)UserSvr::RomHeaderAddress();
766 if (romHeader->iPageableRomStart==NULL)
767 test.Printf(_L("Test ROM is not paged - test skipped!\r\n"));
771 test(TheFs.Connect()==KErrNone);
773 r=UserSvr::HalFunction(EHalGroupVM,EVMHalFlushCache,0,0);
776 test.Printf(_L("DemandPagingFlushPages Error = %d\n"),r);
780 DriveNumber = FindFsNANDDrive();
783 test.Printf(_L("NAND Flash not found - test skipped!\r\n"));
788 fileName.Append((TChar)('A'+DriveNumber));
789 fileName+=_L(":\\f32-tst\\");
790 TInt r=TheFs.MkDirAll(fileName);
791 test(r==KErrNone || r== KErrAlreadyExists);
792 fileName += _L("annoyingflies.txt");
793 r=file.Replace(TheFs,fileName,EFileWrite);
795 test.Printf(_L("Error %d: file '%S' could not be created\n"),r,&fileName);
797 r=file.Write(_L8("Flies as big as sparrows indoletly buzzing in the warm air, heavy with the stench of rotting carcasses"));
799 test.Printf(_L("Error %d: could not write to file\n"),r);
802 test(file.Flush() == KErrNone);
806 r=file.BlockMap(info,start, -1,ETestDebug);
807 if (r!=KErrNone && r!=KErrCompletion)
808 test.Printf(_L("Error %d: could not obtain block map\n"),r);
809 test(r==KErrNone || r==KErrCompletion);
810 locDriveNumber=info.iLocalDriveNumber;
811 test.Printf(_L("Found drive: %c (NAND drive %d)\r\n"), DriveNumber+'A',locDriveNumber);
814 // Connect to device driver
815 TBool changeFlag = EFalse;
816 r = Drive.Connect(locDriveNumber,changeFlag);
817 TPckg<TLocalDriveCapsV4> capsPack(DriveCaps);
818 Drive.Caps(capsPack);
821 r = Drive.ControlIO(KNandCollectGarbage,NULL,NULL);
824 test.Printf(_L("LocalDrive does not support the KNandCollectGarbage ControlIO request\n"));
825 CtrlIoCollectGarbageSupported = EFalse;
829 TPtr8 statsBuf((TUint8*) &stats, sizeof(stats));
830 r = Drive.ControlIO(KNandGetDeferStats,statsBuf,0);
833 if (stats.iSynchronousMediaDriver)
835 test.Printf(_L("Media drive is synchronous - test skipped!\r\n"));
842 test.Printf(_L("LocalDrive does not support the KNandGetDeferStats ControlIO request\n"));
843 CtrlIoGetDeferStatsSupported = EFalse;
847 test.Printf(_L("LocalDrive Connected\n"));
852 if(CtrlIoCollectGarbageSupported && CtrlIoGetDeferStatsSupported)
855 // Free device and end test program