sl@0: // Copyright (c) 2003-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of the License "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // Tests erasing of Flash while forcing suspend-resume cycles sl@0: // This is a soak-test version that runs continously sl@0: // sl@0: // sl@0: sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include "randgen.h" sl@0: #include "user_config.h" sl@0: sl@0: RTest test( _L("TF_SUSPENDSOAK") ); sl@0: sl@0: sl@0: sl@0: sl@0: class CEraser sl@0: { sl@0: public: sl@0: enum TFunction sl@0: { sl@0: EIdle, sl@0: EEraseBlock sl@0: }; sl@0: sl@0: public: sl@0: ~CEraser(); sl@0: void CreateL(); sl@0: void Stop(); sl@0: void WaitForReady(); sl@0: inline TBool CheckDone() const sl@0: { sl@0: return (EIdle == iRequestedFunction); sl@0: } sl@0: sl@0: inline void WaitForDone() sl@0: { sl@0: WaitForReady(); sl@0: iWaitingSignal.Signal(); // resignal, ready for next Start() sl@0: }; sl@0: sl@0: void EraseBlock( TUint32 aOffset, TUint aLength ); sl@0: sl@0: private: sl@0: void Panic( TInt aPanicNum ); sl@0: void Start( TFunction aFunction ); sl@0: sl@0: static TInt EraserThread( TAny* aParam ); sl@0: sl@0: void DoEraseBlock(); sl@0: sl@0: private: sl@0: RThread iThread; sl@0: sl@0: // sl@0: // Shared between main & eraser thread sl@0: // sl@0: TFunction iRequestedFunction; sl@0: RSemaphore iGoSignal; sl@0: RSemaphore iWaitingSignal; sl@0: TBool iStop; sl@0: sl@0: // sl@0: // These are local to the eraser thread sl@0: // sl@0: TUint iOffset; sl@0: TUint iLength; sl@0: TBusLocalDrive iDrive; sl@0: }; sl@0: sl@0: sl@0: sl@0: CEraser::~CEraser() sl@0: { sl@0: iThread.Terminate( KErrNone ); sl@0: iThread.Close(); sl@0: iGoSignal.Close(); sl@0: iWaitingSignal.Close(); sl@0: } sl@0: sl@0: void CEraser::Panic( TInt aPanicNum ) sl@0: { sl@0: _LIT( KPanicCat, "ERASE-T" ); sl@0: User::Panic( KPanicCat, aPanicNum ); sl@0: RProcess().Panic( KPanicCat, aPanicNum ); sl@0: } sl@0: sl@0: sl@0: void CEraser::CreateL() sl@0: // sl@0: // Create new thread and wait for it to become ready sl@0: // sl@0: { sl@0: iGoSignal.CreateLocal( 0 ); // initially blocked sl@0: iWaitingSignal.CreateLocal( 0 ); // initially blocked sl@0: iStop = EFalse; sl@0: User::LeaveIfError( iThread.Create( _L("ERASER"), EraserThread, 2048, 2048, 65536, this ) ); sl@0: test.Printf( _L("Eraser thread created\n") ); sl@0: sl@0: iThread.Resume(); sl@0: sl@0: test.Printf( _L("Waiting for thread to become ready\n") ); sl@0: WaitForReady(); sl@0: iWaitingSignal.Signal(); sl@0: } sl@0: sl@0: void CEraser::Start( TFunction aFunction ) sl@0: // sl@0: // Start the suspender thread executing function aFunction sl@0: // sl@0: { sl@0: iStop = EFalse; sl@0: WaitForReady(); sl@0: iRequestedFunction = aFunction; sl@0: iGoSignal.Signal(); sl@0: } sl@0: sl@0: void CEraser::Stop() sl@0: // sl@0: // Stop the thread sl@0: // sl@0: { sl@0: iStop = ETrue; sl@0: } sl@0: sl@0: void CEraser::WaitForReady() sl@0: { sl@0: iWaitingSignal.Wait(); sl@0: } sl@0: sl@0: void CEraser::EraseBlock( TUint32 aOffset, TUint aLength ) sl@0: // sl@0: // Execute a single read immediately to cause a suspend sl@0: // sl@0: { sl@0: iOffset = aOffset; sl@0: iLength = aLength; sl@0: Start( EEraseBlock ); sl@0: } sl@0: sl@0: sl@0: TInt CEraser::EraserThread( TAny* aParam ) sl@0: // sl@0: // The thread which executes suspend functions sl@0: // sl@0: { sl@0: RDebug::Print( _L("Eraser thread starts") ); sl@0: sl@0: CEraser& self = *reinterpret_cast(aParam); sl@0: sl@0: // sl@0: // Open our own TBusLogicalDevice channel sl@0: // sl@0: TBool changedFlag; sl@0: if( KErrNone != self.iDrive.Connect( KDriveNumber, changedFlag ) ) sl@0: { sl@0: self.Panic( 1 ); sl@0: } sl@0: sl@0: RDebug::Print( _L("Eraser thread connected to drive") ); sl@0: sl@0: while( !self.iStop ) sl@0: { sl@0: // sl@0: // Signal that we are ready for a request sl@0: // sl@0: _LIT( KWaitMsg, "Eraser thread waiting..." ); sl@0: RDebug::Print( KWaitMsg ); sl@0: self.iWaitingSignal.Signal(); sl@0: sl@0: // sl@0: // Wait for a request sl@0: // sl@0: self.iGoSignal.Wait(); sl@0: _LIT( KGoMsg, "Eraser thread go (%d)" ); sl@0: RDebug::Print( KGoMsg, self.iRequestedFunction ); sl@0: sl@0: switch( self.iRequestedFunction ) sl@0: { sl@0: case EEraseBlock: sl@0: self.DoEraseBlock(); sl@0: break; sl@0: sl@0: case EIdle: sl@0: default: sl@0: self.Panic( 0 ); sl@0: } sl@0: sl@0: self.iRequestedFunction = EIdle; sl@0: } sl@0: sl@0: self.iDrive.Disconnect(); sl@0: return KErrNone; sl@0: } sl@0: sl@0: void CEraser::DoEraseBlock() sl@0: // sl@0: // Issue an erase sl@0: // sl@0: { sl@0: _LIT( KEraseStartMsg, "Eraser starting erase..." ); sl@0: RDebug::Print( KEraseStartMsg ); sl@0: sl@0: TInt r = iDrive.Format( TInt64(iOffset), iLength ); sl@0: sl@0: if( KErrNone != r ) sl@0: { sl@0: RDebug::Print( _L("Eraser: FAIL: erase request returns %d"), r ); sl@0: Panic( 2 ); sl@0: } sl@0: } sl@0: sl@0: sl@0: sl@0: class CSuspendTest : public CBase sl@0: { sl@0: public: sl@0: ~CSuspendTest(); sl@0: sl@0: void CreateL(); sl@0: sl@0: void DoTest(); sl@0: sl@0: private: sl@0: sl@0: TInt EraseOneBlock( TInt aBlockNumber ); sl@0: TInt ZeroFillBlock( TInt aBlockNumber ); sl@0: TBool ValidateBlock( TInt aBlockNumber, TUint32 aFillWord ); sl@0: TInt ZeroAllBlocks(); sl@0: TBool ValidateAllBlocks( TUint32 aFillWord ); sl@0: void TimeSinceStart() const; sl@0: sl@0: void DoRandomReadSoak(); sl@0: sl@0: private: sl@0: TBusLocalDrive iDrive; sl@0: TBool iDriveOpened; sl@0: sl@0: CEraser* iEraser; sl@0: sl@0: TInt iFlashSize; sl@0: TInt iBlockSize; sl@0: TInt iBlockCount; sl@0: sl@0: TTime iStartTime; sl@0: sl@0: TBuf8<512> iReadBuffer; sl@0: sl@0: }; sl@0: sl@0: sl@0: CSuspendTest::~CSuspendTest() sl@0: { sl@0: if( iDriveOpened ) sl@0: { sl@0: iDrive.Disconnect(); sl@0: } sl@0: sl@0: delete iEraser; sl@0: } sl@0: sl@0: sl@0: sl@0: void CSuspendTest::CreateL() sl@0: { sl@0: // sl@0: // Create the eraser thread sl@0: // sl@0: iEraser = new(ELeave) CEraser; sl@0: iEraser->CreateL(); sl@0: sl@0: // sl@0: // Load the device drivers sl@0: // sl@0: TInt r; sl@0: #ifndef SKIP_PDD_LOAD sl@0: test.Printf( _L("Loading %S\n"), &KLfsDriverName ); sl@0: r = User::LoadPhysicalDevice( KLfsDriverName ); sl@0: test( KErrNone == r || KErrAlreadyExists == r ); sl@0: #endif sl@0: sl@0: #ifdef UNMOUNT_DRIVE sl@0: RFs fs; sl@0: test( KErrNone == fs.Connect() ); sl@0: test( KErrNone == fs.SetSessionPath( _L("Z:\\") ) ); sl@0: TFullName name; sl@0: fs.FileSystemName( name, KLffsLogicalDriveNumber ); sl@0: if( name.Length() > 0 ) sl@0: { sl@0: test.Printf( _L("Unmounting drive") ); sl@0: test( KErrNone == fs.DismountFileSystem( _L("Lffs"), KLffsLogicalDriveNumber) ); sl@0: User::After( 2000000 ); sl@0: test.Printf( _L("Drive unmounted") ); sl@0: } sl@0: fs.Close(); sl@0: #endif sl@0: sl@0: // sl@0: // Open a TBusLogicalDevice to it sl@0: // sl@0: test.Printf( _L("Opening media channel\n") ); sl@0: TBool changedFlag = EFalse; sl@0: r = iDrive.Connect( KDriveNumber, changedFlag ); sl@0: User::LeaveIfError( r ); sl@0: iDriveOpened = ETrue; sl@0: sl@0: // sl@0: // Get size of Flash drive, block size, block count sl@0: // sl@0: TLocalDriveCapsV2Buf info; sl@0: iDrive.Caps(info); sl@0: iFlashSize = I64LOW(info().iSize); sl@0: iBlockSize = info().iEraseBlockSize; sl@0: iBlockCount = iFlashSize / iBlockSize; sl@0: sl@0: test.Printf( _L("Flash size is 0x%x bytes\n"), iFlashSize ); sl@0: test.Printf( _L("Block size is 0x%x bytes\n"), iBlockSize ); sl@0: test.Printf( _L("Block count is %d\n"), iBlockCount ); sl@0: sl@0: test.Printf( _L("CreateL complete\n") ); sl@0: } sl@0: sl@0: sl@0: void CSuspendTest::DoTest() sl@0: // sl@0: // Main test dispatcher sl@0: // sl@0: { sl@0: DoRandomReadSoak(); sl@0: } sl@0: sl@0: sl@0: TInt CSuspendTest::EraseOneBlock( TInt aBlockNumber ) sl@0: // sl@0: // Erases block aBlockNumber on Flash sl@0: // sl@0: { sl@0: TInt blockBaseOffset = aBlockNumber * iBlockSize; sl@0: sl@0: test.Printf( _L("Erasing block %d (offs=0x%x)\n"), aBlockNumber, blockBaseOffset ); sl@0: sl@0: TInt r = iDrive.Format( blockBaseOffset, iBlockSize ); sl@0: sl@0: test.Printf( _L("... block erased, rv=%d\n"), r ); sl@0: return r; sl@0: } sl@0: sl@0: sl@0: TBool CSuspendTest::ValidateBlock( TInt aBlockNumber, TUint32 aFillWord ) sl@0: // sl@0: // Checks that every word in block aBlockNumber has the value aFillWord sl@0: // sl@0: { sl@0: TUint offset = aBlockNumber * iBlockSize; sl@0: test.Printf( _L("Validating block %d (offs=0x%x)\n"), aBlockNumber, offset ); sl@0: sl@0: TBool failed = EFalse; sl@0: const TInt readBufLen = iReadBuffer.MaxLength(); sl@0: sl@0: for( TInt len = iBlockSize; len > 0 && !failed ;) sl@0: { sl@0: TInt r = iDrive.Read( offset, readBufLen, iReadBuffer ); sl@0: if( r != KErrNone ) sl@0: { sl@0: test.Printf( _L("... FAIL: read failed (%d) at offset 0x%x\n"), r, offset ); sl@0: test( KErrNone == r ); sl@0: } sl@0: test( iReadBuffer.Length() == readBufLen ); sl@0: sl@0: TUint32* p = (TUint32*)iReadBuffer.Ptr(); sl@0: for( TInt i = 0; i < readBufLen; i += 4 ) sl@0: { sl@0: if( aFillWord != *p ) sl@0: { sl@0: failed = ETrue; sl@0: test.Printf( _L("... FAILED: word @ offs=0x%x, read=0x%x, expected=0x%x\n"), sl@0: offset+i, p[0], aFillWord ); sl@0: break; sl@0: } sl@0: ++p; sl@0: } sl@0: offset += readBufLen; sl@0: len -= readBufLen; sl@0: } sl@0: sl@0: return !failed; sl@0: } sl@0: sl@0: sl@0: TInt CSuspendTest::ZeroFillBlock( TInt aBlockNumber ) sl@0: // sl@0: // Zero-fills and entire block sl@0: // The requires that writing works sl@0: // sl@0: { sl@0: test.Printf( _L("Zero-filling block %d\n"), aBlockNumber ); sl@0: sl@0: // sl@0: // Create a buffer full of zeros sl@0: // sl@0: const TInt KZeroBufSize = 512; sl@0: sl@0: TBuf8 buf; sl@0: buf.FillZ( buf.MaxLength() ); sl@0: sl@0: // sl@0: // Write the data out to the Flash sl@0: // sl@0: TInt writeCount = iBlockSize / KZeroBufSize; sl@0: TInt r = KErrNone; sl@0: TUint blockBaseOffset = aBlockNumber * iBlockSize; sl@0: TInt pos = blockBaseOffset; sl@0: for( ; (writeCount > 0) && (KErrNone == r); writeCount-- ) sl@0: { sl@0: r = iDrive.Write( pos, buf ); sl@0: if( r != KErrNone ) sl@0: { sl@0: test.Printf( _L("... FAIL: write failed (%d) at offset 0x%x\n"), pos ); sl@0: } sl@0: pos += KZeroBufSize; sl@0: } sl@0: sl@0: return r; sl@0: } sl@0: sl@0: sl@0: TInt CSuspendTest::ZeroAllBlocks() sl@0: // sl@0: // Writes zeros to all blocks sl@0: // sl@0: { sl@0: test.Printf( _L("Zeroing all blocks\n") ); sl@0: sl@0: TInt r = KErrNone; sl@0: for( TInt i = 0; (i < iBlockCount) && (KErrNone == r); i++ ) sl@0: { sl@0: r = ZeroFillBlock( i ); sl@0: } sl@0: sl@0: return r; sl@0: } sl@0: sl@0: TBool CSuspendTest::ValidateAllBlocks( TUint32 aFillWord ) sl@0: // sl@0: // Checks that all blocks contain aFillWord sl@0: // sl@0: { sl@0: test.Printf( _L("Validating all blocks\n") ); sl@0: sl@0: TBool failed = EFalse; sl@0: for( TInt i = 0; (i < iBlockCount) && (!failed); i++ ) sl@0: { sl@0: failed = !ValidateBlock( i, aFillWord ); sl@0: } sl@0: sl@0: return !failed; sl@0: } sl@0: sl@0: sl@0: void CSuspendTest::TimeSinceStart() const sl@0: { sl@0: TTimeIntervalSeconds timeTaken; sl@0: TTime time; sl@0: time.HomeTime(); sl@0: TInt r = time.SecondsFrom(iStartTime, timeTaken); sl@0: test(r == KErrNone); sl@0: TInt totalTime = timeTaken.Int(); sl@0: sl@0: TInt seconds = totalTime % 60; sl@0: TInt minutes = (totalTime / 60) % 60; sl@0: TInt hours = totalTime / 3600; sl@0: sl@0: test.Printf( _L("Time since test started = %d:%d:%d\n"), hours, minutes, seconds ); sl@0: } sl@0: sl@0: sl@0: sl@0: void CSuspendTest::DoRandomReadSoak() sl@0: // sl@0: // For each block issues an erase and then sl@0: // starts issuing read requests. The intervals sl@0: // between read requests are derived from the sl@0: // pseudo-random number generator. sl@0: // Each block is checked after is has been erased sl@0: // sl@0: { sl@0: sl@0: TRandomGenerator random; sl@0: random.SetSeed( MAKE_TINT64(0xA05BE111,0x00101111) ); sl@0: sl@0: test.Next( _L("Random read soak test") ); sl@0: sl@0: iStartTime.HomeTime(); sl@0: sl@0: // sl@0: // We repeat the test for each block, erasing block n and reading from sl@0: // block (n+1) modulo iBlockCount sl@0: // sl@0: for(;;) sl@0: { sl@0: TimeSinceStart(); sl@0: sl@0: for( TInt eraseBlock = 0; eraseBlock < iBlockCount; eraseBlock++ ) sl@0: { sl@0: TUint32 readBlock = (eraseBlock + 1) % iBlockCount; sl@0: TUint32 erasePos = eraseBlock * iBlockSize; sl@0: TInt readPos = readBlock * iBlockSize; sl@0: sl@0: // sl@0: // Zero the block we are about to erase sl@0: // sl@0: test( KErrNone == ZeroFillBlock( eraseBlock ) ); sl@0: test( ValidateBlock( eraseBlock, 0 ) ); sl@0: sl@0: TBuf8<32> buf; sl@0: sl@0: // sl@0: // Start the erase sl@0: // sl@0: _LIT( KEraseNotify, "Main thread starting erase\n" ); sl@0: test.Printf( KEraseNotify ); sl@0: iEraser->EraseBlock( erasePos, iBlockSize ); sl@0: sl@0: // sl@0: // Now we loop, waiting for random intervals, issuing sl@0: // reads, until the erase completes sl@0: // sl@0: sl@0: while( !iEraser->CheckDone() ) sl@0: { sl@0: // sl@0: // Get a pseudo-random interval between 0 and 524.288 milliseconds sl@0: // sl@0: TInt delayInMicroseconds = random.Next() % 0x80000; sl@0: User::After( delayInMicroseconds ); sl@0: sl@0: test( KErrNone == iDrive.Read( readPos, buf.MaxLength(), buf ) ); sl@0: _LIT( KReadNotify, "Done Read" ); sl@0: test.Printf( KReadNotify ); sl@0: } sl@0: sl@0: // sl@0: // Now check that the block was erased sl@0: // sl@0: test( ValidateBlock( eraseBlock, 0xFFFFFFFF ) ); sl@0: sl@0: } sl@0: } sl@0: sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: void E32Main() sl@0: { sl@0: test.Title(); sl@0: test.Start(_L("Testing media erase+suspend operations")); sl@0: sl@0: CSuspendTest suspendTest; sl@0: TRAPD( ret, suspendTest.CreateL() ); sl@0: if( KErrNone == ret ) sl@0: { sl@0: suspendTest.DoTest(); sl@0: } sl@0: sl@0: test.End(); sl@0: }