diff -r 000000000000 -r 96e5fb8b040d kerneltest/e32test/pccd/t_mmcdrv.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kerneltest/e32test/pccd/t_mmcdrv.cpp Thu Dec 17 09:24:54 2009 +0200 @@ -0,0 +1,2297 @@ +// 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\pccd\t_mmcdrv.cpp +// Test the MultiMediaCard (MMC) media driver +// Spare Test case Numbers 0513-0519 +// +// + +#include "../mmu/d_sharedchunk.h" +#include +#include +#include +#include +#include +#include +#include + +const TInt KDiskSectorSize=512; +const TInt KDiskSectorShift=9; +const TUint KDiskSectorMask=0xFFFFFE00; +const TInt KSectBufSizeInSectors=8; +const TInt KSectBufSizeInBytes=(KSectBufSizeInSectors< than largest transfer + +const TInt KSingSectorNo=1; +const TInt64 KTwoGigbytes = 0x80000000; + +TBool mediaChangeSupported=EFalse; // ??? +TBool ManualMode=EFalse; + +// Wrappers for the test asserts +GLREF_C void TestIfError( TInt aValue, TInt aLine, const TText* aFile ); +GLREF_C void TestIfErrorMsg( TInt aValue, TInt aLine, const TText* aFile, const TDesC& aMessageOnError ); +GLREF_C void TestEqual( TInt aValue, TInt aExpected, TInt aLine, const TText* aFile ); +GLREF_C void TestEqualMsg( TInt aValue, TInt aExpected, TInt aLine, const TText* aFile, const TDesC& aMessageOnError ); +GLREF_C void TestEitherEqual( TInt aValue, TInt aExpected1, TInt aExpected2, TInt aLine, const TText* aFile ); +GLREF_C void TestRange( TInt aValue, TInt aMin, TInt Max, TInt aLine, const TText* aFile ); + +#define TEST_FOR_ERROR2( r, l, f ) TestIfError( r, l, _S(f) ) +#define TEST_FOR_ERROR_ERRMSG2( r, l, f, m ) TestIfErrorMsg( r, l, _S(f), m ) +#define TEST_FOR_VALUE2( r, e, l, f ) TestEqual( r, e, l, _S(f) ) +#define TEST_FOR_VALUE_ERRMSG2( r, e, l, f, m ) TestEqualMsg( r, e, l, _S(f), m ) +#define TEST_FOR_EITHER_VALUE2( r, e1, e2, l, f ) TestEitherEqual( r, e1, e2, l, _S(f) ) +#define TEST_FOR_RANGE2( r, min, max, l, f ) TestRange( r, min, max, l, _S(f) ) + +#define TEST_FOR_ERROR( r ) TEST_FOR_ERROR2( r, __LINE__, __FILE__ ) +#define TEST_FOR_ERROR_ERRMSG( r, m ) TEST_FOR_ERRORMSG2( r, __LINE__, __FILE__, m ) +#define TEST_FOR_VALUE( r, expected ) TEST_FOR_VALUE2( r, expected, __LINE__, __FILE__ ) +#define TEST_FOR_VALUE_ERRMSG( r, expected, m ) TEST_FOR_VALUE_ERRMSG2( r, expected, __LINE__, __FILE__, m ) +#define TEST_FOR_EITHER_VALUE( r, expected1, expected2 ) TEST_FOR_EITHER_VALUE2( r, expected1, expected2, __LINE__, __FILE__ ) +#define TEST_FOR_RANGE( r, min, max ) TEST_FOR_RANGE2( r, min, max, __LINE__, __FILE__ ) + +GLDEF_C void TestIfError( TInt aValue, TInt aLine, const TText* aFile ) + { + if( aValue < 0 ) + { + _LIT( KErrorTestFailMsg, "ERROR %d\n\r" ); + test.Printf( KErrorTestFailMsg, aValue ); + test.operator()( EFalse, aLine, (const TText*)(aFile) ); + } + } + +GLDEF_C void TestIfErrorMsg( TInt aValue, TInt aLine, const TText* aFile, const TDesC& aMessageOnError ) + { + if( aValue < 0 ) + { + _LIT( KErrorTestFailMsg, "ERROR %d %S\n\r" ); + test.Printf( KErrorTestFailMsg, aValue, &aMessageOnError ); + test.operator()( EFalse, aLine, (const TText*)(aFile) ); + } + } + + +GLDEF_C void TestEqual( TInt aValue, TInt aExpected, TInt aLine, const TText* aFile ) + { + if( aExpected != aValue ) + { + _LIT( KEqualTestFailMsg, "ERROR %d expected %d\n\r" ); + test.Printf( KEqualTestFailMsg, aValue, aExpected ); + test.operator()( EFalse, aLine, (const TText*)(aFile) ); + } + } + +GLDEF_C void TestEqualMsg( TInt aValue, TInt aExpected, TInt aLine, const TText* aFile, const TDesC& aMessageOnError ) + { + if( aExpected != aValue ) + { + _LIT( KEqualTestFailMsg, "ERROR %d expected %d %S\n\r" ); + test.Printf( KEqualTestFailMsg, aValue, aExpected, &aMessageOnError ); + test.operator()( EFalse, aLine, (const TText*)(aFile) ); + } + } + +GLDEF_C void TestEitherEqual( TInt aValue, TInt aExpected1, TInt aExpected2, TInt aLine, const TText* aFile ) + { + if( (aExpected1 != aValue) && (aExpected2 != aValue) ) + { + _LIT( KEqualTestFailMsg, "ERROR %d expected %d or %d\n\r" ); + test.Printf( KEqualTestFailMsg, aValue, aExpected1, aExpected2 ); + test.operator()( EFalse, aLine, (const TText*)(aFile) ); + } + } + +GLDEF_C void TestRange( TInt aValue, TInt aMin, TInt aMax, TInt aLine, const TText* aFile ) + { + if( (aValue < aMin) || (aValue > aMax) ) + { + _LIT( KRangeTestFailMsg, "ERROR 0x%x expected 0x%x..0x%x\n\r" ); + test.Printf( KRangeTestFailMsg, aValue, aMin, aMax ); + test.operator()( EFalse, aLine, (const TText*)(aFile) ); + } + } + +//// + +TMMCDrive::TMMCDrive() + : iTestMode(ETestPartition), + iDriveSize(0), + iMediaSize(0) + { + } + +TInt TMMCDrive::Read(TInt64 aPos,TInt aLength,TDes8& aTrg) + { + if(iTestMode == ETestWholeMedia) + { + return TBusLocalDrive::Read(aPos, aLength, &aTrg, KLocalMessageHandle, 0, RLocalDrive::ELocDrvWholeMedia); + } + else if(iTestMode != ETestPartition && aLength <= (TInt)ChunkSize) + { + TPtr8 wholeBufPtr(TheChunk.Base(),aLength); + + TInt r = TBusLocalDrive::Read(aPos, aLength, wholeBufPtr); + + aTrg.Copy(wholeBufPtr); + return r; + } + + return TBusLocalDrive::Read(aPos, aLength, aTrg); + } + +TInt TMMCDrive::Write(TInt64 aPos,const TDesC8& aSrc) + { + if(iTestMode == ETestWholeMedia) + { + return TBusLocalDrive::Write(aPos, aSrc.Length(), &aSrc, KLocalMessageHandle, 0, RLocalDrive::ELocDrvWholeMedia); + } + else if(iTestMode != ETestPartition && aSrc.Length() <= (TInt)ChunkSize) + { + TPtr8 wholeBufPtr(TheChunk.Base(),aSrc.Length()); + wholeBufPtr.Copy(aSrc); + + TInt r = TBusLocalDrive::Write(aPos, wholeBufPtr); + + return r; + } + + return TBusLocalDrive::Write(aPos, aSrc); + } + +TInt TMMCDrive::SetTestMode(TTestMode aTestMode) + { + switch (aTestMode) + { + case ETestWholeMedia : test.Printf(_L("\nTesting Whole Media\n")); break; + case ETestPartition : test.Printf(_L("\nTesting Partition\n")); break; + case ETestSharedMemory : test.Printf(_L("\nTesting Shared Memory\n")); break; + case ETestSharedMemoryCache : test.Printf(_L("\nTesting Shared Memory (Caching)\n")); break; + case ETestSharedMemoryFrag : test.Printf(_L("\nTesting Shared Memory (Fragmented)\n")); break; + default : test.Printf(_L("\nTesting Shared Memory (Fragmented/Caching)\n")); break; + } + + if(aTestMode == ETestWholeMedia && iMediaSize == 0) + { + test.Printf(_L("...not supported")); + return KErrNotSupported; + } + + iTestMode = aTestMode; + return KErrNone; + } + +TMMCDrive::TTestMode TMMCDrive::TestMode() + { + return iTestMode; + } + +void TMMCDrive::SetSize(TInt64 aDriveSize, TInt64 aMediaSize) + { + iDriveSize = aDriveSize; + iMediaSize = aMediaSize; + } + +TInt64 TMMCDrive::Size() + { + switch (iTestMode) + { + case ETestWholeMedia : return iMediaSize; + default : return iDriveSize; + } + } + +////// + +GLDEF_C void DumpBuffer( const TDesC8& aBuffer ) + /** + * Dump the content of aBuffer in hex + */ + { + static const TText hextab[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F' }; + const TInt KBytesPerLine = 32; + const TInt KCharsPerLine = KBytesPerLine * 2; + + TInt remaining = aBuffer.Length(); + TUint8* pSrc = const_cast(aBuffer.Ptr()); + + TBuf line; + line.SetLength( KCharsPerLine ); // don't need to print trailing space + TInt bytesPerLine = KBytesPerLine; + TInt lineOffs = 0; + while( remaining ) + { + if( remaining < KBytesPerLine ) + { + bytesPerLine = remaining; + line.SetLength( (bytesPerLine*2) ); + } + TUint16* pDest = const_cast(line.Ptr()); + remaining -= bytesPerLine; + for( TInt i = bytesPerLine; i > 0; --i ) + { + TUint8 c = *pSrc++; + *pDest++ = hextab[c >> 4]; + *pDest++ = hextab[c & 0xF]; + } + _LIT( KFmt, "%06x: %S\n\r" ); + test.Printf( KFmt, lineOffs, &line ); + lineOffs += bytesPerLine; + } + } + + +GLDEF_C TBool CompareBuffers( const TDesC8& aBuf1, const TDesC8& aBuf2 ) + { + TInt count = 32; + if (aBuf1.Length() < count) + count = aBuf1.Length(); + + + for (TInt i = 0; i < (aBuf1.Length()-count); i+= count) + { + if( aBuf1.Mid(i,count).Compare(aBuf2.Mid(i,count)) != 0) + { + // now need to find where mismatch ends + TInt j =i; + for (; j <= (aBuf1.Length()-count); j+= count) + { + if( aBuf1.Mid(j,count).Compare(aBuf2.Mid(j,count)) == 0) break; + } + test.Printf(_L("buf1 len: %d, buf2 len: %d\n"),aBuf1.Length(),aBuf2.Length()); + test.Printf( _L("Buffer mismatch @%d to %d (%d Bytes)\n\r"),i,j, (j-i) ); + test.Printf( _L("buffer 1 ------------------\n\r") ); + DumpBuffer( aBuf1.Mid(i,(j-i)) ); + test.Printf( _L("buffer 2 ------------------\n\r") ); + DumpBuffer( aBuf2.Mid(i,(j-i)) ); + test.Printf(_L("buf1 len: %d, buf2 len: %d\n"),aBuf1.Length(),aBuf2.Length()); + test.Printf( _L("Buffer mismatch @%d to %d (%d Bytes)\n\r"),i,j, (j-i) ); + return EFalse; + } + } + return ETrue; + } + + +void singleSectorRdWrTest(TInt aSectorOffset,TInt aLen) +// +// Perform a write / read test on a single sector (KSingSectorNo). Verify that the +// write / read back is successful and that the rest of the sector is unchanged. +// + { + + TBuf8 saveBuf; + test.Start(_L("Single sector write/read test")); + test(aSectorOffset+aLen<=KDiskSectorSize); + + // Now save state of sector before we write to it + TInt secStart=(KSingSectorNo< saveBuf1; + TBuf8 saveBuf2; + + test.Printf(_L(" MBW[%d] : MBR[%d]\n\r"), aWrMB, aRdMB); + + test(aFirstSectorOffset infoPckg(info); + nTest(anotherMmcDrive.Caps(infoPckg)==KErrNone); + nTest(info.iType==EMediaHardDisk); + + nTest.End(); + return(KErrNone); + } + +LOCAL_C void ProgressBar(TInt64 aPos,TInt64 anEndPos,TInt anXPos) +// +// Display progress of local drive operation on screen (1-16 dots) +// + { + static TInt64 prev; + TInt64 curr; + if ((curr=(aPos-1)/(anEndPos>>4))>prev) + { // Update progress bar + test.Console()->SetPos(anXPos); + for (TInt64 i=curr;i>=0;i--) + test.Printf(_L(".")); + } + prev=curr; + } + + +/** +@SYMTestCaseID PBASE-T_MMCDRV-0510 +@SYMTestCaseDesc Test Write/Read during media Change +@SYMTestPriority High + +@SYMTestActions + a.) Test Read during a Media Change + b.) Test Write during a Media Change + +@SYMTestExpectedResults All tests must pass +*/ +LOCAL_C void TestHugeReadWrite(TBool aIsRead, TInt aLen) +// +// Writes aLen bytes to the MMC drive. Gives user enough time to flip the media +// change switch. Request should abort with KErrNotReady on write command, but nothing +// on read command. +// Each read or write is started from sector KMultSectNo (2). +// The media change operation only works when the switch is moved from the closed position +// to the open position. +// + { + test.Start(_L("TestHugeReadWrite: media change during I/O test.")); + test.Printf(_L("aIsRead = %x, aLen = %x.\n"), aIsRead, aLen); + + HBufC8 *buf = HBufC8::New(aLen); + test(buf != NULL); + + TInt startSectPos = KMultSectorNo << KDiskSectorShift; + if (aIsRead) + { + test.Printf(_L("Launching %08x byte read at %08x.\n"), aLen, startSectPos); + test.Printf(_L("Move media change from closed to open position before finished.\n")); + TPtr8 ptr(buf->Des()); + TInt r = TheMmcDrive.Read(startSectPos, aLen, ptr); + test.Printf(_L("r = %d.\n"), r); + test(r == KErrNone); + } + else + { + buf->Des().Fill(0xff, aLen); + test.Printf(_L("Launching %08x byte write at %08x.\n"), aLen, startSectPos); + test.Printf(_L("Move media change from closed to open position before finished.\n")); + TInt r = TheMmcDrive.Write(startSectPos, *buf); + test.Printf(_L("r = %d.\n"), r); + test(r == KErrNotReady); + } + + test.Printf(_L("Pausing for 5 seconds to move media change switch back to closed.\n")); + User::After(5 * 1000 * 1000); + delete buf; + test.End(); + } + + +LOCAL_C void FillBufferWithPattern(TDes8 &aBuf) +// +// Fills aBuf with cycling hex digits up to aBuf.Length(). +// + { + TInt len = aBuf.Length() & ~3; + for (TInt i = 0; i < len; i+=4) + { + *((TUint32*) &aBuf[i]) = i; + } + } + + +LOCAL_C void WriteAndReadBack(TInt64 aStartPos, const TDesC8 &aWrBuf) +// +// This function tests the multiple block reads when aWrBuf is sufficiently large. +// + { + test.Start(_L("WriteAndReadBack")); + + TInt r; // general error values + + // Allocate a same size buffer to read back into and compare with. + HBufC8 *rdBuf = aWrBuf.Alloc(); + test(rdBuf != NULL); + TPtr8 rdPtr(rdBuf->Des()); + + test.Next(_L("wrb: writing")); + r = TheMmcDrive.Write(aStartPos, aWrBuf); + test.Printf(_L("\nwrb:r=%d"), r); + test(r == KErrNone); + + test.Printf(_L("\n")); + test.Next(_L("wrb: reading")); + r = TheMmcDrive.Read(aStartPos, rdPtr.Length(), rdPtr); + test.Printf(_L("rb:r=%d"), r); + test(r == KErrNone); + + // Compare the pattern that has just been read back with the original. + test.Printf(_L("\n")); + test.Next(_L("wrb: comparing")); + test.Printf( + _L("rdPtr.Length() = %04x, aWrBuf.Length() = %04x"), + rdPtr.Length(), aWrBuf.Length()); + test(rdPtr == aWrBuf); + +#if 0 // extra debug when buffers not compare. + for (TInt j = 0; j < rdPtr.Length(); j++) + { + test.Printf(_L("%d: w%02x r%02x"), j, aWrBuf[j], rdBuf[j]); + + if (rdPtr[j] != aWrBuf[j]) + { + test.Printf(_L("buffer mismatch at %04x: %02x v %02x"), j, rdPtr[j], aWrBuf[j]); + test(EFalse); + } + } +#endif + + test.Printf(_L("\n")); + delete rdBuf; + test.End(); + } + +/** +@SYMTestCaseID PBASE-T_MMCDRV-0169 +@SYMTestCaseDesc Test Multiple Block Reads +@SYMTestPriority High + +@SYMTestActions + a.) Test Multiple Block Reads at the internal buffer size + b.) Test Multiple Block Reads greater than the internal buffer size + +@SYMTestExpectedResults All tests must pass + +@TODO: increase Buffer size to match current reference platform (128KB) +*/ +LOCAL_C void TestMultipleBlockReads() + { + // Test multiple block reads. + static TBuf8<256 * 1024> rw_wrBuf; + + rw_wrBuf.SetLength(rw_wrBuf.MaxLength()); + FillBufferWithPattern(rw_wrBuf); + + test.Next(_L("Testing multiple block reads at internal buffer size")); + rw_wrBuf.SetLength(8 * KDiskSectorSize); + WriteAndReadBack(KMultSectorNo << KDiskSectorShift, rw_wrBuf); + + test.Next(_L("Testing multiple block reads at gt internal buffer size")); + rw_wrBuf.SetLength(10 * KDiskSectorSize); + WriteAndReadBack(KMultSectorNo << KDiskSectorShift, rw_wrBuf); + + test.Next(_L("Testing unaligned large block read ")); + rw_wrBuf.SetLength(rw_wrBuf.MaxLength()); + WriteAndReadBack((KMultSectorNo << KDiskSectorShift) + 128, rw_wrBuf); + } + + +/** +@SYMTestCaseID PBASE-T_MMCDRV-0558 +@SYMTestCaseDesc Test Long Read/Write Boundaries +@SYMTestPriority High + +@SYMTestActions + + Perform and Write/Read/Verify for the given length (L) of data across the following boundaries. + Depending on the length provided, this will also perform a partial write/read at the end sector. + + ------------------- + | Start | End | + |-------------------| + | 0 | L | + | 507 | L-507 | + | 10 | L | + | 0 | L-3 | + | 27 | L-512 | + | 0 | L-509 | + | 3 | L-3 | + ------------------- + + For each combination, the write/read/verify operations are performed in the following sequence: + + a: Write and Read in single 512-byte blocks. + b: Write in a single operation (multiple blocks), Read in 512-Byte blocks. + c: Write in 512-Byte blocks, Read in a single operation (multiple-blocks). + d: Write and Read in a single operation (multiple-blocks). + + In the cases where a partial read/write operation occurs (ie - the start and/or end position don't lie within + a sector boundary), the original contents of the start and/or end sectors are read and stored at the start of + the test, and compared with the contents of the sectors at the end of the test to ensure that unwritten data within + the sectors remain unaffected. + +@SYMTestExpectedResults All tests must pass + +@SYMPREQ1389 REQ6951 Double Buffering and SD Switch +*/ + +LOCAL_C void TestLongReadWriteBoundaries(TUint aLen, TBool aMBOnly = EFalse) + { + TBuf<64> b; + + b.Format(_L("MMC drive: Very long RdWr(1) (%dbytes at %d)"),aLen,0); + test.Next(b); + MultipleSectorRdWrTest(0, aLen, aMBOnly); // Exceeds driver's buffer, starts/ends on sector boundary + + b.Format(_L("MMC drive: Very long RdWr(2) (%dbytes at %d)"),(aLen-KDiskSectorSize+5),507); + test.Next(b); + MultipleSectorRdWrTest(507, (aLen-KDiskSectorSize+5), aMBOnly); // Exceeds driver's buffer, ends on sector boundary + + b.Format(_L("MMC drive: Very long RdWr(3) (%dbytes at %d)"),aLen,10); + test.Next(b); + MultipleSectorRdWrTest(10, aLen, aMBOnly); // Exceeds driver's buffer, starts/ends off sector boundary + + b.Format(_L("MMC drive: Very long RdWr(4) (%dbytes at %d)"),(aLen-3),0); + test.Next(b); + MultipleSectorRdWrTest(0, aLen-3, aMBOnly); // Exceeds driver's buffer, starts on sector boundary + + b.Format(_L("MMC drive: Very long RdWr(5) (%dbytes at %d)"),(aLen-KDiskSectorSize),27); + test.Next(b); + MultipleSectorRdWrTest(27, (aLen-KDiskSectorSize), aMBOnly); // Exceeds driver's buffer (due to start offset), starts/ends off sector boundary + + b.Format(_L("MMC drive: Very long RdWr(6) (%dbytes at %d)"),(aLen-KDiskSectorSize-3),0); + test.Next(b); + MultipleSectorRdWrTest(0, aLen-KDiskSectorSize-3, aMBOnly); // Equals driver's buffer, starts on sector boundary + + b.Format(_L("MMC drive: Very long RdWr(7) (%dbytes at %d)"),(aLen-3),3); + test.Next(b); + MultipleSectorRdWrTest(3, aLen-3, aMBOnly); // Equals driver's buffer, ends on sector boundary + } + + +/** +@SYMTestCaseID PBASE-T_MMCDRV-0509 +@SYMTestCaseDesc Test Sector Read/Writing +@SYMTestPriority High + +@SYMTestActions + a.) Test Writing blocks on sector boundaries + b.) Test Reading blocks on sector boundaries + c.) Test single sector Write/Read at: + i.) Sector Start + ii.) Mid Sector + iii.) Sector End + d.) Test Multiple Sector Write/Read: + i.) Start on Sector Boundary + ii.) Start/End on Sector Boundary + iii.) End on Sector Boundary + e.) Test Write/Read over sector boundary + +@SYMTestExpectedResults All tests must pass +*/ +LOCAL_C void TestSectorReadWrite() + { + TBuf<64> b; + b.Format(_L("MMC drive: Sector RdWr(%d)"), KDiskSectorSize); + + test.Next(b); + + TInt len; + + // Fill wrBuf with a pattern of ascending numbers. + wrBuf.SetLength(KDiskSectorSize); + TUint32 *p = REINTERPRET_CAST(TUint32 *, &wrBuf[0]); + TInt secPos; + for (secPos = 0; secPos < KDiskSectorSize; secPos++) + { + wrBuf[secPos] = TUint8(secPos % 0x0100); + } + + // Write 512 byte blocks to the card, writing the sector number to the first + // word in each buffer. + + test.Printf(_L("Writing ")); + TInt64 i; +// for (i=0;i savBuf1,savBuf2; + TInt fmtTestPos=(10< 0) + { + wrBuf.SetLength(rem); + defEraseVal = rdBuf[sec * KDiskSectorSize]; + test(defEraseVal == 0x00 || defEraseVal == 0xFF); // The card should erase with 0x00 or 0xFF + wrBuf.Fill(defEraseVal, rem); + test( CompareBuffers( wrBuf, rdBuf.Mid( sec * KDiskSectorSize, rem ) ) ); + } + } + } + } + + +class TRandGen + { + public: + TRandGen(); + void Seed(); + void Seed( const TInt64& aSeed ); + TUint Next(); + + private: + TInt64 iValue; + }; + + +TRandGen::TRandGen() + : iValue(KDefaultRandSeed) + { + } + + +void TRandGen::Seed( const TInt64& aSeed ) + { + iValue = aSeed; + } + +void TRandGen::Seed() + { + iValue = KDefaultRandSeed; + } + +TUint TRandGen::Next() + { + iValue *= 214013; + iValue += 2531011; + return static_cast( I64LOW(iValue) ); + } + + +GLDEF_C void FillRandomBuffer( TDes8& aBuf, TRandGen& aRand ) + /** + * Fill buffer aBuf with data generated by aRand + */ + { + TUint l = aBuf.MaxLength(); + aBuf.SetLength( l ); + TUint* p = (TUint*)aBuf.Ptr(); + + // Do any unaligned bytes at the start + TInt preAlign = (TUint)p & 3; + if( preAlign ) + { + preAlign = 4 - preAlign; + TUint8* p8 = (TUint8*)p; + TUint rand = aRand.Next(); + while( preAlign && l ) + { + *p8 = (TUint8)(rand & 0xFF); + rand >>= 8; + ++p8; + --preAlign; + --l; + } + p = (TUint*)p8; + } + + for( ; l > 3; l-=4 ) + { + *p++ = aRand.Next(); + } + // Fill in any trailing bytes + if( l > 0 ) + { + TUint8* q = (TUint8*)p; + TUint r = aRand.Next(); + if( l > 1 ) + { + *((TUint16*)q) = (TUint16)(r & 0xFFFF); + q += 2; + l -= 2; + r >>= 16; + } + if( l > 0 ) + { + *q = (TUint8)(r & 0xFF); + } + } + } + +GLDEF_C void FillRandomBuffer( HBufC8* aBuf, TRandGen& aRand ) + /** + * Fill buffer aBuf with data generated by aRand + * For convenience this version takes a HBufC8* + */ + { + TPtr8 ptr = aBuf->Des(); + FillRandomBuffer( ptr, aRand ); + } + + +/** +@SYMTestCaseID PBASE-T_MMCDRV-0164 +@SYMTestCaseDesc Test MMC Drive Capabilities +@SYMTestPriority High + +@SYMTestActions + a. Obtain MMC Drive Capabilities + b. If the card size is greater than 2GBytes, test that the driver reports FAT32 file system supported. + c. Test that the type of media is reported as EMediaHardDisk + d. Test that the drive attributes report KDriveAttLocal and KDriveAttRemovable + e. Test that the drive attributes do not report KDriveAttRemote + f. If the drive is not write protected or a ROM card, test that the media attributes report that the drive is formattable + g. If the drive is write protected or a ROM card, test that the media attributes do not report that the drive is formattable + h. Test that the media attributes do not report variable sized media. + +@SYMTestExpectedResults All tests must pass + +@SYMPREQ1389 CR0795 Support for >2G SD Cards +*/ +TBool TestDriveInfo() + { + test.Next( _L("Test drive info") ); + + TEST_FOR_ERROR( TheMmcDrive.Caps( DriveCaps ) ); + + test.Printf( _L("Caps V1:\n\tiSize=0x%lx\n\tiType=%d\n\tiBattery=%d\n\tiDriveAtt=0x%x\n\tiMediaAtt=0x%x\n\tiBaseAddress=0x%x\n\tiFileSystemId=0x%x\n\tiPartitionType=0x%x\n"), + DriveCaps().iSize, + DriveCaps().iType, + DriveCaps().iBattery, + DriveCaps().iDriveAtt, + DriveCaps().iMediaAtt, + DriveCaps().iBaseAddress, + DriveCaps().iFileSystemId, + DriveCaps().iPartitionType ); + + test.Printf( _L("Caps V2:\n\tiHiddenSectors=0x%x\n\tiEraseBlockSize=0x%x\nCaps V3:\n\tiExtraInfo=%x\n\tiMaxBytesPerFormat=0x%x\n"), + DriveCaps().iHiddenSectors, + DriveCaps().iEraseBlockSize, + DriveCaps().iExtraInfo, + DriveCaps().iMaxBytesPerFormat ); + + test.Printf( _L("Format info:\n\tiCapacity=0x%lx\n\tiSectorsPerCluster=0x%x\n\tiSectorsPerTrack=0x%x\n\tiNumberOfSides=0x%x\n\tiFatBits=%d\n"), + DriveCaps().iFormatInfo.iCapacity, + DriveCaps().iFormatInfo.iSectorsPerCluster, + DriveCaps().iFormatInfo.iSectorsPerTrack, + DriveCaps().iFormatInfo.iNumberOfSides, + DriveCaps().iFormatInfo.iFATBits ); + + if(DriveCaps().iSerialNumLength > 0) + { + test.Printf( _L("Serial Number : ") ); + TBuf8<2*KMaxSerialNumLength> snBuf; + TUint i; + for (i=0; i2GB is marked as requesting FAT32 + if( DriveCaps().iSize > KTwoGigbytes && DriveCaps().iExtraInfo) + { + TEST_FOR_VALUE( DriveCaps().iFormatInfo.iFATBits, TLDFormatInfo::EFB32 ); + } + + TEST_FOR_VALUE( DriveCaps().iType, EMediaHardDisk ); + + const TUint KExpectedDriveAtt = KDriveAttLocal | KDriveAttRemovable; + const TUint KNotExpectedDriveAtt = KDriveAttRemote; + TEST_FOR_VALUE( DriveCaps().iDriveAtt & KExpectedDriveAtt, KExpectedDriveAtt ); + TEST_FOR_VALUE( DriveCaps().iDriveAtt & KNotExpectedDriveAtt, 0 ); + + TUint expectedMediaAtt = KMediaAttFormattable; + TUint notExpectedMediaAtt = KMediaAttVariableSize; + + TBool isReadOnly = DriveCaps().iMediaAtt & KMediaAttWriteProtected; + if(isReadOnly) + { + expectedMediaAtt &= ~KMediaAttFormattable; + + test.Printf( _L("\n ---------------------------\n") ); + test.Printf( _L(" Media is Write Protected\n") ); + if((DriveCaps().iMediaAtt & KMediaAttFormattable) != KMediaAttFormattable) + { + test.Printf( _L(" Media is a ROM card\n") ); + } + test.Printf( _L(" Some tests will be skipped\n") ); + test.Printf( _L(" ---------------------------\n") ); + } + + TEST_FOR_VALUE( DriveCaps().iMediaAtt & expectedMediaAtt, expectedMediaAtt ); + TEST_FOR_VALUE( DriveCaps().iMediaAtt & notExpectedMediaAtt, 0 ); + + return(isReadOnly); + } + + +/** +@SYMTestCaseID PBASE-T_MMCDRV-0165 +@SYMTestCaseDesc Test MMC Card Reads +@SYMTestPriority High + +@SYMTestActions + a. Read 64K in one operation from the start of the media and store the contents. + b. Read 512 byte blocks from the start of the media at various offsets and compare with initial read. + b. Read 64K in 512 byte blocks from the start of the media and compare with the initial read. + c. read 64K from the end of the drive + +@SYMTestExpectedResults All tests must pass + +@SYMPREQ1389 CR0795 Support for >2G SD Cards +*/ +void TestRead() + { + // This just tests that we can read *something* from the drive + // We check elsewhere that we can read what we've written + test.Next( _L("Test reading" ) ); + + HBufC8* bigBuf = HBufC8::New( 65536 ); + HBufC8* smallBuf = HBufC8::New( 512 ); + + test( bigBuf != NULL ); + test( smallBuf != NULL ); + TPtr8 bigPtr( bigBuf->Des() ); + TPtr8 smallPtr( smallBuf->Des() ); + + test.Printf( _L("Read block from start of media\n") ); + TEST_FOR_ERROR( TheMmcDrive.Read( TInt64(0), 65536, bigPtr) ); + + test.Printf( _L("Read smaller blocks which should match the data in big block\n\r" ) ); + TInt i; + for( i = 0; i <= 512; ++i ) + { + test.Printf( _L("\toffset: %d\r"), i ); + TEST_FOR_ERROR( TheMmcDrive.Read( TInt64(i), 512, smallPtr ) ); + test( CompareBuffers( smallBuf->Des(), bigBuf->Mid( i, 512 ) ) ); + } + + for( i = 512; i <= 65536-512; i += 512 ) + { + test.Printf( _L("\toffset: %d\r"), i ); + TEST_FOR_ERROR( TheMmcDrive.Read( TInt64(i), 512, smallPtr ) ); + test( CompareBuffers( smallBuf->Des(), bigBuf->Mid( i, 512 ) ) ); + } + + test.Printf( _L("\nTest read from end of drive\n") ); + + if(CardType == TKnownCardTypes::EBuffalloMiniSD_512M || + CardType == TKnownCardTypes::EIntegralHSSD_2G) + { + // These cards have issues with reading at the end of the drive... + test.Printf( _L(" -- Skipping Test - Known card detected --\n") ); + } + else + { + TEST_FOR_ERROR( TheMmcDrive.Read( TheMmcDrive.Size() - 65536, 65536, bigPtr) ); + } + + delete smallBuf; + delete bigBuf; + } + + +/** +@SYMTestCaseID PBASE-T_MMCDRV-0511 +@SYMTestCaseDesc Test Moving Read/Write +@SYMTestPriority High + +@SYMTestActions + a.) Test Read/Verify Whole Sectors + b.) Test Read/Verify Sliding sector sized window + c.) Test Read/Verify Sliding byte sized window + d.) Test Read/Verify Increasing sized window + e.) Test Write/Read/Verify Whole Sectors + f.) Test Write/Read/Verify Sliding sector sized window + g.) Test Write/Read/Verify Increasing sized window + +@SYMTestExpectedResults All tests must pass +*/ +void DoReadWriteTest( TInt64 aPos, TInt aWindowSize, TBool aQuick ) + { + // Do various read/write tests within a aWindowSize window starting at aPos + HBufC8* wholeBuf = HBufC8::New( aWindowSize ); + test( wholeBuf != NULL ); + + HBufC8* readBuf = HBufC8::New( aWindowSize ); + test( readBuf != NULL ); + + TBuf8<512> sectorBuf; + TRandGen rand; + + test.Printf( _L("Walking sector read\n\r") ); + FillRandomBuffer( wholeBuf, rand ); + TPtr8 wholeBufPtr( wholeBuf->Des() ); + TEST_FOR_ERROR( TheMmcDrive.Write( aPos, *wholeBuf ) ); + + // Read each sector back and check that it's correct + TInt64 pos( aPos ); + TInt i; + for( i = 0; i < aWindowSize - 512; i += 512 ) + { + pos = aPos + i; + test.Printf(_L("\tRead @0x%lx\r"), pos); + TEST_FOR_ERROR( TheMmcDrive.Read( pos, 512, sectorBuf ) ); + test( CompareBuffers( sectorBuf, wholeBuf->Mid( i, 512 ) ) ); + } + + test.Printf( _L("\nSliding sector read\n\r") ); + // Slide a sector-sized window over the data + TInt maxl = Min( aWindowSize - 512, 512 * 3 ); + for( i = 0; i < maxl; i++ ) + { + pos = aPos + i; + test.Printf(_L("\tRead @0x%lx\r"), pos); + TEST_FOR_ERROR( TheMmcDrive.Read( pos, 512, sectorBuf ) ); + test( CompareBuffers( sectorBuf, wholeBuf->Mid( i, 512 ) ) ); + } + + if( !aQuick ) + { + test.Printf( _L("\nSliding byte read\n\r") ); + // Slide a byte-sized window over the data + for( i = 0; i < maxl; i++ ) + { + pos = aPos + i; + test.Printf(_L("\tRead @0x%lx\r"), pos); + TEST_FOR_ERROR( TheMmcDrive.Read( pos, 1, sectorBuf ) ); + test( CompareBuffers( sectorBuf, wholeBuf->Mid( i, 1 ) ) ); + } + + test.Printf( _L("\nGrowing read\n\r") ); + // Read from an increasing-sized window + for( i = 1; i < 512; i++ ) + { + test.Printf(_L("\tRead length: %d\r"), i); + TEST_FOR_ERROR( TheMmcDrive.Read( aPos, i, sectorBuf ) ); + test( CompareBuffers( sectorBuf, wholeBuf->Left( i ) ) ); + } + + test.Printf( _L("\nDownward-expanding read\n\r") ); + // Read from a window that grows downward from the end of the test region + for( i = 1; i <= 512; i++ ) + { + pos = aPos + aWindowSize - i; + test.Printf(_L("\t[pos:len] %lx:%d\r"), pos, i); + TEST_FOR_ERROR( TheMmcDrive.Read( pos, i, sectorBuf ) ); + test( CompareBuffers( sectorBuf, wholeBuf->Mid( aWindowSize - i, i ) ) ); + } + } + + test.Printf( _L("\nWalking sector write\n\r") ); + // Overwrite each sector and check the whole region is correct + for( i = 0; i < aWindowSize - 512; i += 512 ) + { + FillRandomBuffer( sectorBuf, rand ); + pos = aPos + i; + test.Printf(_L("\tWrite @0x%lx\r"), pos); + TEST_FOR_ERROR( TheMmcDrive.Write( pos, sectorBuf ) ); + wholeBufPtr.MidTPtr( i, 512 ) = sectorBuf; // update our match data + + TPtr8 ptr( readBuf->Des() ); + TEST_FOR_ERROR( TheMmcDrive.Read( aPos, aWindowSize, ptr ) ); + test( CompareBuffers( *readBuf, *wholeBuf ) ); + } + + if( !aQuick ) + { + test.Printf( _L("\nSliding sector overwrite\n\r") ); + // Overwrite a sector-sized region that slides across the test region + for( i = 0; i < maxl; i += 1 ) + { + FillRandomBuffer( sectorBuf, rand ); + pos = aPos + i; + test.Printf(_L("\tWrite @0x%lx\r"), pos); + TEST_FOR_ERROR( TheMmcDrive.Write( pos, sectorBuf ) ); + wholeBufPtr.MidTPtr( i, 512 ) = sectorBuf; // update our match data + + TPtr8 ptr( readBuf->Des() ); + TEST_FOR_ERROR( TheMmcDrive.Read( aPos, aWindowSize, ptr ) ); + test( CompareBuffers( *readBuf, *wholeBuf ) ); + } + + test.Printf( _L("\nGrowing sector overwrite\n\r") ); + // Overwrite an expanding region starting at aPos + for( i = 1; i < 512; i += 1 ) + { + FillRandomBuffer( sectorBuf, rand ); + test.Printf(_L("\tWrite length: %d\r"), i); + sectorBuf.SetLength( i ); + TEST_FOR_ERROR( TheMmcDrive.Write( aPos, sectorBuf ) ); + wholeBufPtr.LeftTPtr( i ) = sectorBuf; // update our match data + + TPtr8 ptr( readBuf->Des() ); + TEST_FOR_ERROR( TheMmcDrive.Read( aPos, aWindowSize, ptr ) ); + test( CompareBuffers( *readBuf, *wholeBuf ) ); + } + } + + test.Printf( _L("\nTest zero-length read\n") ); + FillRandomBuffer( sectorBuf, rand ); + TEST_FOR_ERROR( TheMmcDrive.Read( aPos, 0, sectorBuf ) ); + TEST_FOR_VALUE( sectorBuf.Length(), 0 ); + + delete wholeBuf; + delete readBuf; + } + + +// This tests for a bug observed in certain ESanDiskMmcMobile_1GB cards which never exit the busy state +// when writing a buffer which is one sector bigger than the PSL buffer size (resulting in a single write +// request split into 2 fragments, the last of which is one sector only). The "fix" for this is to make the +// PSL reject CMD23 (SET_BLOCK_COUNT) for these particular cards, forcing the PIL to issue a CMD12 (STOP_TRANSMISSION) +void TestFragmentedWrite(TInt aLength) + { + test.Next( _L("Test a large write just bigger than PSL buffer size") ); + + HBufC8* bigBuf = HBufC8::New( aLength); + test( bigBuf != NULL ); + TPtr8 bigPtr( bigBuf->Des() ); + + TInt64 startPos = 0; + + // for a dual-slot enabled H4, buffer size is 132K - (512 * 2) = 131K + + + test.Printf( _L("Initializing buffer contents...\n")); + bigPtr.SetLength(aLength); + TInt n; + for (n=0; n2G SD Cards +*/ +void TestCapacity() + { + if(!IsReadOnly) + { + test.Next( _L("Test access at end of media") ); + DoReadWriteTest( TheMmcDrive.Size() - 65536, 65536, ETrue ); + } + + test.Printf( _L("Test accesses past end of media produce an error\n") ); + + TBuf8<1024> buf; + + test( KErrNone != TheMmcDrive.Read( TheMmcDrive.Size(), 1, buf ) ); + test( KErrNone != TheMmcDrive.Read( TheMmcDrive.Size(), 2, buf ) ); + test( KErrNone != TheMmcDrive.Read( TheMmcDrive.Size(), 512, buf ) ); + test( KErrNone != TheMmcDrive.Read( TheMmcDrive.Size() + 1, 512, buf ) ); + test( KErrNone != TheMmcDrive.Read( TheMmcDrive.Size() + 512, 512, buf ) ); + test( KErrNone != TheMmcDrive.Read( TheMmcDrive.Size() - 1, 2, buf ) ); + test( KErrNone != TheMmcDrive.Read( TheMmcDrive.Size() - 511, 512, buf ) ); + test( KErrNone != TheMmcDrive.Read( TheMmcDrive.Size() - 512, 513, buf ) ); + test( KErrNone != TheMmcDrive.Read( TheMmcDrive.Size() - 65536, 65537, buf ) ); + test( KErrNone != TheMmcDrive.Read( TheMmcDrive.Size() - 512, 1024, buf ) ); + } + + +void WriteAcrossBoundaries(TInt64 aBoundary) + { + test.Printf( _L("Test for aliasing around boundary\n") ); + TBuf8<512> bufLo; + TBuf8<512> bufHi; + TBuf8<8192> bufRead; + + bufLo.Fill( 0xE4, 512 ); + bufHi.Fill( 0x19, 512 ); + + TEST_FOR_ERROR( TheMmcDrive.Write( 0, bufLo ) ); + TEST_FOR_ERROR( TheMmcDrive.Write( aBoundary, bufHi ) ); + TEST_FOR_ERROR( TheMmcDrive.Read( 0, 512, bufRead ) ); + test( bufRead == bufLo ); + TEST_FOR_ERROR( TheMmcDrive.Read( aBoundary, 512, bufRead ) ); + test( bufRead == bufHi ); + + bufHi.Fill( 0xBB, 1 ); + TEST_FOR_ERROR( TheMmcDrive.Write( aBoundary, bufHi ) ); + TEST_FOR_ERROR( TheMmcDrive.Read( 0, 512, bufRead ) ); + test( bufRead == bufLo ); + + bufHi.Fill( 0xCC, 1 ); + TEST_FOR_ERROR( TheMmcDrive.Write( (aBoundary+1), bufHi ) ); + TEST_FOR_ERROR( TheMmcDrive.Read( 0, 512, bufRead ) ); + test( bufRead == bufLo ); + + test.Printf( _L("Test write which ends at boundary\n") ); + bufHi.Fill( 0x33, 512 ); + TEST_FOR_ERROR( TheMmcDrive.Write( aBoundary, bufHi ) ); + TEST_FOR_ERROR( TheMmcDrive.Read( aBoundary, 512, bufRead ) ); + test( bufRead == bufHi ); + + bufHi.Fill( 0x44, 512 ); + TEST_FOR_ERROR( TheMmcDrive.Write( aBoundary - 512, bufHi ) ); + TEST_FOR_ERROR( TheMmcDrive.Read( aBoundary - 512, 512, bufRead ) ); + test( bufRead == bufHi ); + + TEST_FOR_ERROR( TheMmcDrive.Read( 0, 512, bufRead ) ); + test( bufRead == bufLo ); + + bufHi.Fill( 0x33, 512 ); + TEST_FOR_ERROR( TheMmcDrive.Read( aBoundary, 512, bufRead ) ); + test( bufRead == bufHi ); + + test.Printf( _L("Test read-modify-write across boundary\n") ); + TBuf8<512> rmw; + TBuf8<8192> data; + rmw.Fill( 0x66, 512 ); + data.Fill( 0x11, 8192 ); + + for( TInt i = 1; i < 511; ++i ) + { + ProgressBar(i, 511, 11); + + // Create initial data block + TEST_FOR_ERROR( TheMmcDrive.Write( aBoundary - 512, data ) ); + + // Read-modify-write some data + TEST_FOR_ERROR( TheMmcDrive.Write( aBoundary - 512 + i, rmw ) ); + + // Modify buffer to what we expect + data.MidTPtr( i, 512 ) = rmw; + + // Read it back and check it matches + TEST_FOR_ERROR( TheMmcDrive.Read( aBoundary - 512, 8192, bufRead ) ); + test( CompareBuffers( bufRead, data ) ); + } + test.Printf(_L("\n")); + } + + +/** +@SYMTestCaseID PBASE-T_MMCDRV-0167 +@SYMTestCaseDesc Test that the boundary >2GB doesn't produce aliases or errors +@SYMTestPriority High + +@SYMTestActions + a. Test that writing at the 2G boundary does not produce aliases. + b. Test writes that end at the 2G boundary. + c. Test read/modify/write across the 2G boundary. + +@SYMTestExpectedResults All tests must pass + +@SYMPREQ1389 CR0795 Support for >2G SD Cards +*/ +void TestBoundaries() + { + + if( TheMmcDrive.Size() < 0x80008000 ) + { + test.Printf( _L("Drive not large enough for 2GB boundary test... skipped\n") ); + return; + } + + // Test that the boundary 2GB doesn't produce aliases or errors + // >2Gb cards change addressing scheme from byte to block base + test.Next( _L("Test 2GB boundary") ); + WriteAcrossBoundaries(0x80000000); + +// N.B. Commented Out for now due to compiler warnings +// if( TheMmcDrive.Size() < 0x100008000ll ) +// { +// test.Printf( _L("Drive not large enough for 4GB boundary test... skipped\n") ); +// return; +// } +// // Test that the boundary 4GB doesn't produce aliases or errors +// // >4GB cards change addressing scheme from 32bit to 64bit addresses +// test.Next( _L("Test 4GB boundary") ); +// WriteAcrossBoundaries(0x100000000ll); + } + + +/** +@SYMTestCaseID PBASE-T_MMCDRV-0512 +@SYMTestCaseDesc Test Media Change/Capabilities Reporting +@SYMTestPriority High + +@SYMTestActions + a.) Test Media Change flag after Media Change + b.) Test Capabilities reporting for Out Of Memory Conditions + c.) Test Media Change flag after Machine power-off + d.) Test Capabilities reporting after Machine power-off + e.) Test Multiple Media Change flags after Media Change + +@SYMTestExpectedResults All tests must pass +*/ +void TestMediaChange() + { + test.Next(_L("MMC drive: Media change")); +#if defined (__WINS__) + test.Printf( _L("<<>>\r\n")); +#else + test.Printf( _L("<<>>\r\n")); +#endif + test.Getch(); + User::After(300000); // Allow 0.3s after power down for controller to detect door closed. + test(ChangeFlag!=EFalse); + + test.Next(_L("MMC drive: Caps following media change")); + + TLocalDriveCapsV4 info; + TPckg infoPckg(info); + + test(TheMmcDrive.Caps(infoPckg)==KErrNone); + test(info.iType==EMediaHardDisk); + + test.Next(_L("MMC drive: Caps while OOM")); + TInt err; + test.Printf(_L("Mount returns:")); + for (TInt j=1;j<16;j++) + { + __KHEAP_SETFAIL(RHeap::EDeterministic,j); + err=TheMmcDrive.Caps(infoPckg); + test.Printf(_L("(%d)"),err); + __KHEAP_RESET; + } + test.Printf(_L("\r\n")); + + test.Next(_L("MMC drive: Machine power-off.")); + ChangeFlag=EFalse; + RTimer timer; + TRequestStatus trs; + test(timer.CreateLocal()==KErrNone); + TTime tim; + tim.HomeTime(); + tim+=TTimeIntervalSeconds(8); + timer.At(trs,tim); + UserHal::SwitchOff(); + User::WaitForRequest(trs); + test(trs.Int()==KErrNone); + test(ChangeFlag==EFalse); // ie machine power off hasn't updated it + + test.Next(_L("MMC drive: Caps following power off")); + TInt r=TheMmcDrive.Caps(infoPckg); + test(r==KErrNone); + test(info.iType==EMediaHardDisk); + + test.Next(_L("Starting 2nd thread")); + SecThreadChangeFlag=EFalse; + RThread thread; + TRequestStatus stat; + test(thread.Create(_L("Thread"),dontDisconnectThread,KDefaultStackSize,KHeapSize,KHeapSize,NULL)==KErrNone); + thread.Logon(stat); + thread.Resume(); + User::WaitForRequest(stat); + test(stat==KErrNone); + thread.Close(); + + test.Next(_L("MMC drive: 2nd media change")); +// UserSvr::ForceRemountMedia(ERemovableMedia0); // Generate media change + test(ChangeFlag!=EFalse); + test(SecThreadChangeFlag==EFalse); // Closed 2nd thread so shouldn't have been updated + } + + +//// End of Test +void Format() +// +// Format current drive +// + { + RFs TheFs; + test(TheFs.Connect() == KErrNone); + + test.Next(_L("Format")); + TBuf<4> driveBuf=_L("?:\\"); + driveBuf[0]=(TText)(RFsDNum+'A'); + + RFormat format; + TInt count; + TInt r=format.Open(TheFs,driveBuf,EQuickFormat,count); + test(r==KErrNone); + while(count) + { + TInt r=format.Next(count); + test(r==KErrNone); + } + format.Close(); + } + +void AllocateBuffers() + { + test.Next(_L("Allocate Buffers")); + + //HBufC8* wrBufH = NULL; + //HBufC8* rdBufH = NULL; + + wrBufH = HBufC8::New(KVeryLongRdWrBufLen); + test(wrBufH != NULL); + + rdBufH = HBufC8::New(KVeryLongRdWrBufLen); + if(rdBufH == NULL) delete wrBufH; + test(rdBufH != NULL); + + wrBuf.Set(wrBufH->Des()); + rdBuf.Set(rdBufH->Des()); + } + +void AllocateSharedBuffers(TBool Fragmented, TBool Caching) + { + // Setup SharedMemory Buffers + test.Next(_L("Allocate Shared Memory\n")); + + RLoader l; + test(l.Connect()==KErrNone); + test(l.CancelLazyDllUnload()==KErrNone); + l.Close(); + + test.Printf(_L("Initialise\n")); + TInt r = UserHal::PageSizeInBytes(PageSize); + test(r==KErrNone); + + test.Printf(_L("Loading test driver\n")); + r = User::LoadLogicalDevice(KSharedChunkLddName); + test(r==KErrNone || r==KErrAlreadyExists); + + test.Printf(_L("Opening channel\n")); + r = Ldd.Open(); + test(r==KErrNone); + + test.Printf(_L("Create chunk\n")); + + TUint aCreateFlags = EMultiple|EOwnsMemory; + + if (Caching) + { + test.Printf(_L("Chunk Type:Caching\n")); + aCreateFlags |= ECached; + } + else + test.Printf(_L("Chunk Type:Fully Blocking\n")); + + TCommitType aCommitType = EContiguous; + + TUint TotalChunkSize = ChunkSize; // rounded to nearest Page Size + + TUint ChunkAttribs = TotalChunkSize|aCreateFlags; + r = Ldd.CreateChunk(ChunkAttribs); + test(r==KErrNone); + + if(Fragmented) + { + test.Printf(_L("Commit Fragmented Memory\n")); + + // Allocate Pages in reverse order to maximise memory fragmentation + TUint i = ChunkSize; + do + { + i-=PageSize; + test.Printf(_L("Commit %d\n"), i); + r = Ldd.CommitMemory(aCommitType|i,PageSize); + test(r==KErrNone); + }while (i>0); + } + else + { + test.Printf(_L("Commit Contigouos Memory\n")); + r = Ldd.CommitMemory(aCommitType,TotalChunkSize); + test(r==KErrNone); + } + + test.Printf(_L("Open user handle\n")); + r = Ldd.GetChunkHandle(TheChunk); + test(r==KErrNone); + + } + + +void DeAllocateBuffers() + { + delete rdBufH; + delete wrBufH; + } + +void DeAllocareSharedMemory() + { +// destory chunk + test.Printf(_L("Shared Memory\n")); + test.Printf(_L("Close user chunk handle\n")); + TheChunk.Close(); + + test.Printf(_L("Close kernel chunk handle\n")); + TInt r = Ldd.CloseChunk(); // 1==DObject::EObjectDeleted + test(r==1); + + test.Printf(_L("Check chunk is destroyed\n")); + r = Ldd.IsDestroyed(); + test(r==1); + + test.Printf(_L("Close test driver\n")); + Ldd.Close(); + } + + +TBool SetupDrivesForPlatform(TInt& aDrive, TInt &aRFsDriveNum) +/** + * Finds a MMC/SD suitable drive for testing + * + * @param aDrive The number of the local drive to test + * @return TBool ETrue if a suitable drive is found, EFalse otherwise. + */ + { + + TDriveInfoV1Buf diBuf; + UserHal::DriveInfo(diBuf); + TDriveInfoV1 &di=diBuf(); + + test.Printf(_L(" iRegisteredDriveBitmask 0x%08X"), di.iRegisteredDriveBitmask); + + aDrive = -1; + + TLocalDriveCapsV5Buf capsBuf; + TBusLocalDrive TBLD; + TLocalDriveCapsV5& caps = capsBuf(); + TPtrC8 localSerialNum; + TInt registeredDriveNum = 0; + for(aDrive=0; aDrive < KMaxLocalDrives; aDrive++) + { + TInt driveNumberMask = 1 << aDrive; + if ((di.iRegisteredDriveBitmask & driveNumberMask) == 0) + continue; + + test.Printf(_L(" Drive %d - %S\r\n"), aDrive, &di.iDriveName[registeredDriveNum]); + + // check that the card is readable (so we can ignore for empty card slots) + if ((di.iDriveName[registeredDriveNum].MatchF(_L("MultiMediaCard0")) == KErrNone) || + (di.iDriveName[registeredDriveNum].MatchF(_L("SDIOCard0")) == KErrNone)) + { + + TBool TBLDChangedFlag; + TInt r = TBLD.Connect(aDrive, TBLDChangedFlag); +//test.Printf(_L(" Connect returned %d\n"), r); + if (r == KErrNone) + { + r = TBLD.Caps(capsBuf); + localSerialNum.Set(caps.iSerialNum, caps.iSerialNumLength); + const TInt KSectSize = 512; + TBuf8 sect; + r = TBLD.Read(0, KSectSize, sect); +//test.Printf(_L(" Read returned %d\n"), r); + + TBLD.Disconnect(); + if (r == KErrNone) + break; + } + } + registeredDriveNum++; + } + + if(aDrive == KMaxLocalDrives) + { + test.Printf(_L(" MMC Drive Not Found\r\n")); + return EFalse; + } + + // Work out the file server drive number (which isn't necessarily the same + // as the TBusLocalDrive drive number) + RFs theFs; + test(theFs.Connect() == KErrNone); + + TInt i; + for (i = EDriveA; i < EDriveZ; i++) + { + TMediaSerialNumber serialNum; + TInt r = theFs.GetMediaSerialNumber(serialNum, i); + TInt len = serialNum.Length(); + TInt n; + for (n=0; n buf; + for (TInt m=n; m hexBuf; + hexBuf.Format(_L("%02X "),serialNum[m]); + buf.Append(hexBuf); + } + buf.Append(_L("\n")); + test.Printf(buf); + } + if (serialNum.Compare(localSerialNum) == 0) + { + TVolumeInfo vi; + r = theFs.Volume(vi, i); + TBool sizeMatch = (vi.iSize < caps.iSize); + if (sizeMatch) + { + aRFsDriveNum = i; + break; + } + } + + } + if (i == EDriveZ) + { + test.Printf(_L(" RFs MMC Drive Not Found\r\n")); + return EFalse; + } + + theFs.Close(); + + return ETrue; + } + + +LOCAL_D TBool ParseCommandLineArgs() + { + + TBuf<0x100> cmd; + User::CommandLine(cmd); + TLex lex(cmd); + + for (TPtrC token=lex.NextToken(); token.Length() != 0;token.Set(lex.NextToken())) + { + if (token.CompareF(_L("-m"))== 0) + { + ManualMode = ETrue; + continue; + } + } + + if (ManualMode) + { + // Get the list of drives + TDriveInfoV1Buf diBuf; + UserHal::DriveInfo(diBuf); + TDriveInfoV1 &di=diBuf(); + TInt driveCount = di.iTotalSupportedDrives; + + //Print the list of usable drives + test.Printf(_L("\nDRIVES USED AT PRESENT :\r\n")); + + for (TInt i=0; i < driveCount; i++) + { + TBool flag=EFalse; + RLocalDrive d; + TInt r=d.Connect(i,flag); + //Not all the drives are used at present + if (r == KErrNotSupported) + continue; + + test.Printf(_L("%d : DRIVE NAME :%- 16S\r\n"), i, &di.iDriveName[i]); + } + + test.Printf(_L("\r\nWarning - all data on removable drive will be lost.\r\n")); + test.Printf(_L("<<>>\r\n")); + + TChar driveToTest; + driveToTest=(TUint)test.Getch(); + DriveNumber=((TUint)driveToTest) - '0'; + test(DriveNumber >= 1 && DriveNumber < di.iTotalSupportedDrives); + + return ETrue; + } + else + { + //Auto Mode + //Lets find an MMC Drive to Test with.... + return SetupDrivesForPlatform(DriveNumber, RFsDNum); + } + } + + +GLDEF_C TInt E32Main() + { + test.Title(); + test.Start(_L("Test the MultiMediaCard (MMC) media driver")); + + if (!ParseCommandLineArgs()) + { + test.Printf(_L("MMC Drive Not Found - Skipping test\r\n")); + test.End(); + return(0); + } + + AllocateBuffers(); + + test.Printf(_L("Connect to local drive (%d)\n"),DriveNumber); + + ChangeFlag=EFalse; + test(TheMmcDrive.Connect(DriveNumber,ChangeFlag)==KErrNone); + + TTime startTime; + startTime.HomeTime(); + + IsReadOnly = TestDriveInfo(); + + // The following line causes t_mmcdrv to jump to the tests that check if the + // mmc driver will carry on reading when the door is opened, but abort with + // KErrGeneral when it is not. Enabling the goto here is useful because it + // allows the tester to skip the long read and write tests, which can take several + // minutes on a 16Mb card, and longer if tracing is enabled. It also stops the test + // from returning when !mediaChangeSupported and not getting to the door opening tests. + +#if TEST_DOOR_CLOSE + goto doorTest; +#endif + + for(TInt pass = 0; pass < TMMCDrive::EMaxTestModes; pass++) + { + TInt r = KErrNone; + switch (pass) + { + case 0 : r = TheMmcDrive.SetTestMode(TMMCDrive::ETestPartition); break; + case 1 : + // don't trash partition table in automated mode because... + // cards in test rigs have often got deliberately small partition sizes to testing (!) + if (!ManualMode) + continue; + r = TheMmcDrive.SetTestMode(TMMCDrive::ETestWholeMedia); + break; + case 2 : { + r = TheMmcDrive.SetTestMode(TMMCDrive::ETestSharedMemory); + AllocateSharedBuffers(EFalse,EFalse); + break; + } + case 3 : { + r = TheMmcDrive.SetTestMode(TMMCDrive::ETestSharedMemoryCache); + AllocateSharedBuffers(EFalse, ETrue); + break; + } + case 4 : { + r = TheMmcDrive.SetTestMode(TMMCDrive::ETestSharedMemoryFrag); + AllocateSharedBuffers(ETrue, EFalse); + break; + } + default: { + r = TheMmcDrive.SetTestMode(TMMCDrive::ETestSharedMemoryFragCache); + AllocateSharedBuffers(ETrue, ETrue); + break; + } + } + + + if(r == KErrNone) + { + TestRead(); + TestCapacity(); + + if(IsReadOnly == EFalse) + { + TestMultipleBlockReads(); + TestSectorReadWrite(); + TestWrite(); + TestBoundaries(); + TestFormat(); + } + } + + if (pass > 1) + { + // Shared memory Test Mode in use + DeAllocareSharedMemory(); + } + } + + if (mediaChangeSupported) + { + // Remainder of tests involve media change + TestMediaChange(); + + #if TEST_DOOR_CLOSE +doorTest: + #endif + test.Next(_L("Launching 1.0Mb Read to interrupt with media change.\n")); + TestHugeReadWrite(ETrue, 512 * 1024); + + test.Next(_L("Launching 1.0Mb Write to interrupt with media change.\n")); + TestHugeReadWrite(EFalse, 512 * 1024); + } + + TTime endTime; + endTime.HomeTime(); + TTimeIntervalMicroSeconds elapsed=endTime.MicroSecondsFrom(startTime); + test.Printf(_L("\n\r (Elapsed time: %dmS)\r\n"),(elapsed.Int64()/1000)); + + test.Printf(_L("Disconnect from local drive (%d)"),DriveNumber); + TheMmcDrive.Disconnect(); + + DeAllocateBuffers(); + + // Format card with a File System i.e. FAT + // Such that it is re-usable by next test + Format(); + + test.End(); + + return(0); + } +