diff -r 000000000000 -r a41df078684a kerneltest/e32test/lffs/tf_write.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kerneltest/e32test/lffs/tf_write.cpp Mon Oct 19 15:55:17 2009 +0100 @@ -0,0 +1,1185 @@ +// Copyright (c) 2001-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: +// + +#include +#include +#include +#include +#include "randgen.h" +#include "user_config.h" +#include "tf_write.h" + +_LIT( KTestName, "TF_WRITE" ); +RTest test( KTestName ); + + +const TInt64 KRandomSeed1(MAKE_TINT64(0x3e000111,0xAFCBDF0F)); + + +GLDEF_C void Panic( TPanicNo aPanic ) + { + User::Panic( KTestName, aPanic ); + } + + +// ********************************************************************** +// Implementation of the writer classes + +TWriteBase::TWriteBase( CWriteTest& aOwner ) + : iOwner( aOwner ) + { + } + +void TWriteBase::CheckedWrite(TInt aPos,const TDesC8& aSrc) + { + Write( aPos, aSrc ); + test( iOwner.CompareAgainstFlash( aPos, aSrc ) ); + } + + +TSimpleWrite::TSimpleWrite( CWriteTest& aOwner ) + : TWriteBase( aOwner ), iDrive( aOwner.Drive() ) + { + } + +void TSimpleWrite::Write(TInt aPos,const TDesC8& aSrc) + { + TInt64 pos( aPos ); +// test( KErrNone == iDrive.Write( pos, aSrc ) ); + TInt rv = iDrive.Write( pos, aSrc ); + if( KErrNone != rv ) + { + test.Printf( _L("TBusLocalDrive::Write returned %d"), rv ); + test( EFalse ); + } + } + + + +TThreadWrite::TThreadWrite( CWriteTest& aOwner ) + : TWriteBase( aOwner ), iDrive( aOwner.Drive() ), + iThreadHandle( aOwner.DummyThreadHandle() ) + { + } + +void TThreadWrite::Write(TInt aPos,const TDesC8& aSrc) + { + TInt64 pos( aPos ); +#if 0 + test( KErrNone == iDrive.Write( pos, aSrc.Length(), &aSrc, iThreadHandle, 0 ) ); +#else + test( KErrNone == iDrive.Write( pos, aSrc.Length(), &aSrc, KLocalMessageHandle, 0 ) ); +#endif + } + + +void TThreadWrite::CheckedThreadWrite(TInt aPos, TInt aLength, const TDesC8& aSrc, TInt aDescOffset ) + { + TInt64 pos( aPos ); +#if 0 + test( KErrNone == iDrive.Write( pos, aLength, &aSrc, iThreadHandle, aDescOffset ) ); +#else + test( KErrNone == iDrive.Write( pos, aLength, &aSrc, KLocalMessageHandle, aDescOffset ) ); +#endif + test( iOwner.CompareAgainstFlash( aPos, aLength, aSrc, aDescOffset ) ); + } + +void TThreadWrite::CurrentThreadCheckedThreadWrite(TInt aPos, TInt aLength, const TDesC8& aSrc, TInt aDescOffset ) + { + TInt64 pos( aPos ); +#if 0 + test( KErrNone == iDrive.Write( pos, aLength, &aSrc, RThread().Handle(), aDescOffset ) ); +#else + test( KErrNone == iDrive.Write( pos, aLength, &aSrc, KLocalMessageHandle, aDescOffset ) ); +#endif + test( iOwner.CompareAgainstFlash( aPos, aLength, aSrc, aDescOffset ) ); + } + +// ********************************************************************** +// Implementation of CBlockManager + +CBlockManager::CBlockManager( TBusLocalDrive& aDrive, CWriteTest& aOwner ) + : iDrive( aDrive ), iOwner( aOwner ) + { + } + +CBlockManager::~CBlockManager() + { + delete[] iEraseArray; + } + +void CBlockManager::CreateL() + { + // + // Get size of Flash drive + // + test.Printf( _L("Reading block info...") ); + TLocalDriveCapsV2Buf info; + iDrive.Caps(info); + TUint flashSize = I64LOW(info().iSize); + test( 0 == I64HIGH(info().iSize)); + iBlockSize = info().iEraseBlockSize; + test( 0 == (iBlockSize & 3) ); + iBlockCount = flashSize / iBlockSize; + test( 0 != iBlockCount ); + + test.Printf( _L("Flash block size=0x%x; block count=%d\n"), iBlockSize, iBlockCount ); + + iEraseArray = new(ELeave) TEraseStatus[iBlockCount]; + test.Printf( _L("Erase status array created") ); + } + + +void CBlockManager::EraseBlock( TInt aBlockNumber ) + { + __ASSERT_ALWAYS( aBlockNumber < iBlockCount, Panic( EPanicEraseBlockOOR ) ); + __ASSERT_ALWAYS( aBlockNumber < iBlockCount, Panic( EPanicEraseBlockNeg ) ); + _LIT( KEraseMsg, "Erasing block %d" ); + test.Printf( KEraseMsg, aBlockNumber ); + test( KErrNone == iDrive.Format( BlockAddress( aBlockNumber ), iBlockSize ) ); + VerifyErased( aBlockNumber ); + iEraseArray[ aBlockNumber ] = EErased; + } + +void CBlockManager::EraseAllBlocks() + { + _LIT( KEraseMsg, "Erasing all blocks" ); + test.Printf( KEraseMsg ); + for( TInt i = 0; i < iBlockCount; i++ ) + { + EraseBlock( i ); + } + } + +void CBlockManager::VerifyErased( TInt aBlockNumber ) + { + TUint offset = aBlockNumber * iBlockSize; + + TBool failed = EFalse; + const TInt readBufLen = iReadBuffer.MaxLength(); + + for( TInt remaining = iBlockSize; remaining > 0 && !failed ;) + { + TInt r = iDrive.Read( offset, readBufLen, iReadBuffer ); + if( r != KErrNone ) + { + test.Printf( _L("... FAIL: read failed (%d) at offset 0x%x\n"), r, offset ); + test( KErrNone == r ); + } + test( iReadBuffer.Length() == readBufLen ); + + const TUint32* p = (const TUint32*)iReadBuffer.Ptr(); + for( TInt i = 0; i < readBufLen; i += 4 ) + { + if( 0xFFFFFFFF != *p ) + { + failed = ETrue; + test.Printf( _L("... FAILED: byte @ offs=0x%x, read=0x%x, expected=0xFF\n"), + offset+i, p[0] ); + test(EFalse); + } + ++p; + } + offset += readBufLen; + remaining -= readBufLen; + } + } + + +void CBlockManager::InitialiseSequentialBlockAllocator() + // + // Clears the erase status and resets to block zero + // + { + for( TInt i = 0; i < iBlockCount; i++ ) + { + iEraseArray[i] = ENotErased; + } + iNextBlock = 0; + } + + +TInt CBlockManager::NextErasedBlock() + { + if( iNextBlock >= iBlockCount ) + { + iNextBlock = 0; + } + + if( ENotErased == iEraseArray[iNextBlock] ) + { + EraseBlock( iNextBlock ); + } + iEraseArray[iNextBlock] = ENotErased; // assume it is going to be used + return iNextBlock; + } + +void CBlockManager::InitialiseDataChunkAllocator() + { + iDataBlock = NextErasedBlock(); + iDataOffset = 0; + } + + +TUint CBlockManager::NextErasedDataChunk( TInt aRequiredLength, TInt aMultiple ) + // + // Request a chunk of erased flash of size aRequiredLength bytes on a + // boundary of aMultiple bytes. E,g, to allocate a buffer on 12 bytes length + // on a 32-byte boundary, aRequiredLength = 12, aMultiple=12 + // + // The byte count is rounded up to a multiple of 4 bytes + // + { + aRequiredLength = (aRequiredLength + 3) & ~0x3; + + TUint chunkBase = ((iDataOffset + aMultiple - 1) / aMultiple) * aMultiple; + if( chunkBase > (TUint)iBlockSize || chunkBase + aRequiredLength > (TUint)iBlockSize ) + { + iDataBlock = NextErasedBlock(); + chunkBase = 0; + } + + iDataOffset = ( chunkBase + aRequiredLength + 3) & ~0x3; + + return BlockAddress( iDataBlock ) + chunkBase; + } + + + +inline TInt CBlockManager::BlockCount() const + { + return iBlockCount; + } + +inline TInt CBlockManager::BlockSize() const + { + return iBlockSize; + } + +inline TInt CBlockManager::FlashSize() const + { + return iBlockSize * iBlockCount; + } + +inline TUint CBlockManager::BlockAddress( TInt aBlockNumber ) const + { + return (TUint)aBlockNumber * (TUint)iBlockSize; + } + + +// ********************************************************************** +// Implementation of CWriteTest + +CWriteTest::~CWriteTest() + { + if( iDriveOpened ) + { + iDrive.Disconnect(); + } + + delete iBlocks; + delete iSimpleWriter; + delete iThreadWriter; + } + + +void CWriteTest::CreateL() + { + // + // Load the device drivers + // + TInt r; +#ifndef SKIP_PDD_LOAD + test.Printf( _L("Loading %S\n"), &KLfsDriverName ); + r = User::LoadPhysicalDevice( KLfsDriverName ); + test( KErrNone == r || KErrAlreadyExists == r ); +#endif + +#ifdef UNMOUNT_DRIVE + RFs fs; + test( KErrNone == fs.Connect() ); +#if 0 + // XXX - not EKA2 + test( KErrNone == fs.SetDefaultPath( _L("Z:\\") ) ); +#endif + TFullName name; + fs.FileSystemName( name, KLffsLogicalDriveNumber ); + if( name.Length() > 0 ) + { + test.Printf( _L("Unmounting drive") ); + test( KErrNone == fs.DismountFileSystem( _L("Lffs"), KLffsLogicalDriveNumber) ); + User::After( 2000000 ); + test.Printf( _L("Drive unmounted") ); + } + fs.Close(); +#endif + + // + // Open a TBusLogicalDevice to it + // + test.Printf( _L("Opening media channel\n") ); + TBool changedFlag = EFalse; + r = iDrive.Connect( KDriveNumber, changedFlag ); + User::LeaveIfError( r ); + iDriveOpened = ETrue; + + // + // Initialise the block manager + // + iBlocks = new(ELeave) CBlockManager( iDrive, *this ); + iBlocks->CreateL(); + + // + // Create a dummy thread that we can use to force + // other-thread write operations + // +#if 0 + test( KErrNone == iDummyThread.Create( _L("DUMMY"), DummyThread, 256, KMinHeapSize, KMinHeapSize, NULL ) ); +#else + test( KErrNone == iDummyThread.Create( _L("DUMMY"), DummyThread, KDefaultStackSize, KMinHeapSize, KMinHeapSize, NULL ) ); +#endif + test.Printf( _L("Main thread handle=%d; dummy thread handle=%d"), + RThread().Handle(), DummyThreadHandle() ); + + // + // Create the writer classes + // + iSimpleWriter = new(ELeave) TSimpleWrite( *this ); + iThreadWriter = new(ELeave) TThreadWrite( *this ); + + // + // Seed the pseudo-random number generator + // + iRandom.SetSeed( KRandomSeed1 ); + + + test.Printf( _L("CWriteTest::CreateL complete\n") ); + } + + +TInt CWriteTest::DummyThread( TAny* /* aParam */ ) + // + // Thread does nothing at all + // + { + for(;;) + { + User::WaitForAnyRequest(); // just block + } + } + +void CWriteTest::CreateRandomData( TDes8& aDestBuf, TInt aLength ) + // + // Fills supplied descriptor with aLength bytes of pseudo-random test data + // + { + aDestBuf.SetLength( aLength ); + TUint32* p = (TUint32*)aDestBuf.Ptr(); + for( TInt j = aLength/4; j > 0 ; j-- ) + { + *p++ = iRandom.Next(); + } + + if( aLength & 0x3 ) + { + TUint8* q = (TUint8*)p; + for( TInt k = aLength & 3; k > 0; k-- ) + { + *q++ = (TUint8)iRandom.Next(); + } + } + } + + +TBool CWriteTest::CheckOnes( TUint aFlashOffset, TInt aLength ) + // + // Checks that aLength bytes of data from offset aFlashOffset + // all contain 0xFF + // + { + TUint offset = aFlashOffset; + + TBool failed = EFalse; + const TInt readBufLen = iReadBuffer.MaxLength(); + + for( TInt remaining = aLength; remaining > 0 && !failed ;) + { + TInt readLen = Min( remaining, readBufLen ); + TInt r = iDrive.Read( offset, readLen, iReadBuffer ); + if( r != KErrNone ) + { + test.Printf( _L("... FAIL: read failed (%d) at offset 0x%x\n"), r, offset ); + test( KErrNone == r ); + } + test( iReadBuffer.Length() == readLen ); + + const TUint8* p = iReadBuffer.Ptr(); + for( TInt i = 0; i < readLen; ++i ) + { + if( 0xFF != *p ) + { + failed = ETrue; + test.Printf( _L("... FAILED: byte @ offs=0x%x, read=0x%x, expected=0xFF\n"), + offset+i, p[0] ); + break; + } + ++p; + } + offset += readLen; + remaining -= readLen; + } + + return !failed; + } + + +TBool CWriteTest::CompareAgainstFlash( TInt aFlashOffset, TInt aLength, const TDesC8& aDes, TInt aDescOffset ) + // + // Checks that the data in aDes matches that in the Flash at position + // aFlashOffset. + // The test starts at offset aDescOffset in aSampleData. aLength bytes + // are tested. + // + { + __ASSERT_ALWAYS( aDescOffset + aLength <= aDes.Length(), Panic( EPanicCompareDescOverflow ) ); + TInt dataLength = aLength; + const TUint8* srcPtr = aDes.Ptr() + aDescOffset; + + TUint offset = aFlashOffset; + + TBool failed = EFalse; + const TInt readBufLen = iReadBuffer.MaxLength(); + + while( (dataLength > 0) && !failed ) + { + TInt len = Min( dataLength, readBufLen ); + TInt r = iDrive.Read( offset, len, iReadBuffer ); + if( r != KErrNone ) + { + test.Printf( _L("... FAIL: read failed (%d) at offset 0x%x\n"), r, offset ); + test( KErrNone == r ); + } + test( iReadBuffer.Length() == len ); + + if( 0 != Mem::Compare( srcPtr, len, iReadBuffer.Ptr(), len ) ) + { + test.Printf( _L("... FAIL: mismatch around offset 0x%x\n"), offset ); + failed = ETrue; + } + offset += len; + dataLength -= len; + srcPtr += len; + } + + return !failed; + } + +TBool CWriteTest::CompareAgainstFlash( TInt aFlashOffset, const TDesC8& aDes ) + // + // Checks that the data in aDes matches that in the Flash at position + // aFlashOffset. + // aDes->Length() bytes are tested. + // + { + return CompareAgainstFlash( aFlashOffset, aDes.Length(), aDes, 0 ); + } + + +void CWriteTest::SimpleWriteTest() + { + test.Next( _L("Simple write test, simple write function") ); + DoSimpleWriteTest( *iSimpleWriter ); + } + +void CWriteTest::SimpleThreadWriteTest() + { + test.Next( _L("Simple write test, thread write function") ); + DoSimpleWriteTest( *iThreadWriter ); + } + + +void CWriteTest::DoSimpleWriteTest( MGeneralizedWrite& aWriter ) + // + // Writes some random test data to the start of a block, checks that + // it is written correctly and that the source data isn't modified + // + { + TInt blockNo = iBlocks->NextErasedBlock(); + TUint blockBase = iBlocks->BlockAddress( blockNo ); + + TBuf8<512> randomData; + CreateRandomData( randomData, randomData.MaxLength() ); + + TBuf8<512> randomDataDuplicate; + randomDataDuplicate.Copy( randomData ); + test( randomDataDuplicate == randomData ); + + TBuf8 ptrCopy; // used to take copies of descriptors + + // + // Write using a constant descriptor TPtrC + // + test.Printf( _L("Write using TPtrC") ); + TPtrC8 ptrC( randomData ); + ptrCopy.Copy( (TUint8*)&ptrC, sizeof(ptrC) ); + + aWriter.CheckedWrite( blockBase + 0, ptrC ); + + test.Printf( _L("Check descriptor not modified by write function") ); + test( 0 == Mem::Compare( (TUint8*)&ptrC, sizeof(ptrC), ptrCopy.Ptr(), sizeof(ptrC) ) ); + + test.Printf( _L("Check data not modified by write function") ); + test( randomDataDuplicate == randomData ); + + // + // Write using a modifiable descriptor TPtr + // + test.Printf( _L("Write using TPtr") ); + TPtr8 ptr( (TUint8*)randomData.Ptr(), randomData.Length(), randomData.Length() ); + ptrCopy.Copy( (TUint8*)&ptr, sizeof(ptr) ); + + aWriter.CheckedWrite( blockBase + 1024, ptr ); + + test.Printf( _L("Check descriptor not modified by write function") ); + test( 0 == Mem::Compare( (TUint8*)&ptr, sizeof(ptr), ptrCopy.Ptr(), sizeof(ptr) ) ); + + test.Printf( _L("Check data not modified by write function") ); + test( randomDataDuplicate == randomData ); + + // + // Write using a modifiable descriptor TBuf + // + test.Printf( _L("Write using TBuf") ); + + aWriter.CheckedWrite( blockBase + 2048, randomData ); + + test.Printf( _L("Check descriptor not modified by write function") ); + test( ptrC.Ptr() == randomData.Ptr() ); + test( 512 == randomData.Length() ); + test( 512 == randomData.MaxLength() ); + + test.Printf( _L("Check data not modified by write function") ); + test( randomDataDuplicate == randomData ); + + // + // Read the data back and check it matches + // + test.Printf( _L("Reading data back with TBusLocalDrive::Read") ); + test( KErrNone == iDrive.Read( blockBase + 0, 512, randomDataDuplicate ) ); + test( randomDataDuplicate == randomData ); + test( KErrNone == iDrive.Read( blockBase + 1024, 512, randomDataDuplicate ) ); + test( randomDataDuplicate == randomData ); + test( KErrNone == iDrive.Read( blockBase + 2048, 512, randomDataDuplicate ) ); + test( randomDataDuplicate == randomData ); + } + + + +void CWriteTest::AlignedWriteTest() + { + test.Next( _L("Aligned write test, simple write function") ); + DoAlignedWriteTest( *iSimpleWriter ); + } + +void CWriteTest::AlignedThreadWriteTest() + { + test.Next( _L("Aligned write test, thread write function") ); + DoAlignedWriteTest( *iThreadWriter ); + } + + +void CWriteTest::DoAlignedWriteTest( MGeneralizedWrite& aWriter ) + // + // Writes data of various lengths to word-aligned addresses + // + { + iBlocks->InitialiseDataChunkAllocator(); + + TBuf8<512> data; + + _LIT( KWriteMsg, " writing %d bytes @0x%x" ); + + test.Printf( _L("Testing small writes") ); + + for( TInt length = 1; length < 16; length++ ) + { + CreateRandomData( data, length ); + + // get a 32-byte data chunk on a word boundary + TUint offset = iBlocks->NextErasedDataChunk( 32, 4 ); + + test.Printf( KWriteMsg, length, offset ); + aWriter.CheckedWrite( offset, data ); + // check that the section after the data still contains all ones + test( CheckOnes( offset + length, 32 - length ) ); + } + + + test.Printf( _L("Testing large writes") ); + for( TInt length = 512-32; length <= 512 ; length++ ) + { + CreateRandomData( data, length ); + + // get a 544-byte data chunk on a word boundary + TUint offset = iBlocks->NextErasedDataChunk( 544, 4 ); + + test.Printf( KWriteMsg, length, offset ); + aWriter.CheckedWrite( offset, data ); + + // check that the section after the data still contains all ones + test( CheckOnes( offset + length, 544 - length ) ); + } + } + + + + +void CWriteTest::UnalignedWriteTest() + { + test.Next( _L("Unaligned write test, simple write function") ); + DoUnalignedWriteTest( *iSimpleWriter ); + } + +void CWriteTest::UnalignedThreadWriteTest() + { + test.Next( _L("Unaligned write test, thread write function") ); + DoUnalignedWriteTest( *iThreadWriter ); + } + + +void CWriteTest::DoUnalignedWriteTest( MGeneralizedWrite& aWriter ) + // + // Tests writing to unaligned addresses. "Unaligned" here means + // addresses that are not on a word boundary. + // + { + TBuf8<32> data; + + _LIT( KWriteMsg, " writing 32 bytes @0x%x" ); + + + for( TInt offset = 1; offset < 32; offset++ ) + { + CreateRandomData( data, data.MaxLength() ); + + // + // get a 64-byte data chunk on a 256-byte boundary, then + // start the write at bytes into this buffer + // + TUint dataChunk = iBlocks->NextErasedDataChunk( 64, 256 ); + + test.Printf( KWriteMsg, dataChunk + offset ); + aWriter.CheckedWrite( dataChunk + offset, data ); + + _LIT( KBeforeMsg, " checking unused portion before data" ); + test.Printf( KBeforeMsg ); + test( CheckOnes( dataChunk, offset ) ); + + // check that the section after the data still contains all ones + _LIT( KAfterMsg, " checking unused portion after data" ); + test.Printf( KAfterMsg ); + test( CheckOnes( dataChunk + offset + data.Length(), 64 - offset - data.Length() ) ); + } + } + + + +void CWriteTest::OffsetDescriptorAlignedWriteTest() + // + // Tests writing using an offset into the source data buffer. Writes + // are done to word-aligned destination addresses. + // + { + test.Next( _L("Offset-desc write test, aligned dest address") ); + + TBuf8<64> data; + + _LIT( KWriteMsg, " writing 32 bytes from offset %d to @0x%x" ); + +// CreateRandomData( data, data.MaxLength() ); + data.SetLength(64); + for( TInt i = 0; i < 64; i++ ) + { + data[i] = i; + } + + for( TInt descOffset = 1; descOffset < 32; descOffset++ ) + { + // + // Get a 32-byte data chunk on a word boundary. + // + TUint dataChunk = iBlocks->NextErasedDataChunk( 32, 4 ); + + test.Printf( KWriteMsg, descOffset, dataChunk ); + iThreadWriter->CheckedThreadWrite( dataChunk, 32, data, descOffset ); + + // + // Read the data back out and check it matches + // + _LIT( KReadBackMsg, "Reading back data" ); + test.Printf( KReadBackMsg ); + TBuf8<32> readData; + iDrive.Read( dataChunk, 32, readData ); + TPtrC8 ptr( data.Ptr() + descOffset, 32 ); + test( ptr == readData ); + } + } + + +void CWriteTest::OffsetDescriptorUnalignedWriteTest() + // + // This is a variation of OffsetDescriptorAlignedWriteTest that + // also writes to non-word-aligned destionation addresses. + // + { + test.Next( _L("Offset-desc write test, unaligned dest address") ); + + TBuf8<64> data; + + _LIT( KWriteMsg, " writing 32 bytes from offset %d to @0x%x" ); + + CreateRandomData( data, data.MaxLength() ); + + for( TInt descOffset = 1; descOffset < 32; descOffset++ ) + { + for( TInt unalign = 1; unalign < 4; unalign++ ) + { + // + // Get a 40-byte data chunk on a word boundary. + // + TUint dataChunk = iBlocks->NextErasedDataChunk( 40, 4 ); + TUint destOffset = dataChunk + unalign; + + test.Printf( KWriteMsg, descOffset, destOffset ); + iThreadWriter->CheckedThreadWrite( destOffset, 32, data, descOffset ); + + // + // Read the data back out and check it matches + // + _LIT( KReadBackMsg, "Reading back data" ); + test.Printf( KReadBackMsg ); + TBuf8<32> readData; + iDrive.Read( destOffset, 32, readData ); + TPtrC8 ptr( data.Ptr() + descOffset, 32 ); + test( ptr == readData ); + } + } + } + + +void CWriteTest::OffsetDescriptorCurrentThreadAlignedWriteTest() + // + // Tests writing using an offset into the source data buffer. Writes + // are done to word-aligned destination addresses. This uses the + // thread variant of the write function but passes the handle + // of this thread. + // + { + test.Next( _L("Offset-desc write test, current thread, aligned dest address") ); + + TBuf8<64> data; + + _LIT( KWriteMsg, " writing 32 bytes from offset %d to @0x%x" ); + +// CreateRandomData( data, data.MaxLength() ); + data.SetLength(64); + for( TInt i = 0; i < 64; i++ ) + { + data[i] = i; + } + + for( TInt descOffset = 1; descOffset < 32; descOffset++ ) + { + // + // Get a 32-byte data chunk on a word boundary. + // + TUint dataChunk = iBlocks->NextErasedDataChunk( 32, 4 ); + + test.Printf( KWriteMsg, descOffset, dataChunk ); + iThreadWriter->CurrentThreadCheckedThreadWrite( dataChunk, 32, data, descOffset ); + + // + // Read the data back out and check it matches + // + _LIT( KReadBackMsg, "Reading back data" ); + test.Printf( KReadBackMsg ); + TBuf8<32> readData; + iDrive.Read( dataChunk, 32, readData ); + TPtrC8 ptr( data.Ptr() + descOffset, 32 ); + test( ptr == readData ); + } + } + + +void CWriteTest::OffsetDescriptorCurrentThreadUnalignedWriteTest() + // + // This is a variation of OffsetDescriptorCurrentThreadAlignedWriteTest + // that also writes to non-word-aligned destionation addresses. + // + { + test.Next( _L("Offset-desc write test, current thread, unaligned dest address") ); + + TBuf8<64> data; + + _LIT( KWriteMsg, " writing 32 bytes from offset %d to @0x%x" ); + + CreateRandomData( data, data.MaxLength() ); + + for( TInt descOffset = 1; descOffset < 32; descOffset++ ) + { + for( TInt unalign = 1; unalign < 4; unalign++ ) + { + // + // Get a 40-byte data chunk on a word boundary. + // + TUint dataChunk = iBlocks->NextErasedDataChunk( 40, 4 ); + TUint destOffset = dataChunk + unalign; + + test.Printf( KWriteMsg, descOffset, destOffset ); + iThreadWriter->CurrentThreadCheckedThreadWrite( destOffset, 32, data, descOffset ); + + // + // Read the data back out and check it matches + // + _LIT( KReadBackMsg, "Reading back data" ); + test.Printf( KReadBackMsg ); + TBuf8<32> readData; + iDrive.Read( destOffset, 32, readData ); + TPtrC8 ptr( data.Ptr() + descOffset, 32 ); + test( ptr == readData ); + } + } + } + + + +void CWriteTest::JoinedWriteTest() + // + // Makes two consecutive writes. Checks that the complete + // data block was written correctly. The data is written within + // a 64-byte window and the join position is moved along to each + // possible location + { + + test.Next( _L("Joined write test, simple writes") ); + + // + // Reinitialise the chunk allocator + // + iBlocks->InitialiseDataChunkAllocator(); + + for( TInt join = 1; join < 63; join++ ) + { + TBuf8<64> fullData; + CreateRandomData( fullData, fullData.MaxLength() ); + + // + // Create two TPtrC8s to the two parts of the data + // + TPtrC8 first( fullData.Ptr(), join ); + TPtrC8 second( fullData.Ptr() + join, fullData.MaxLength() - join ); + __ASSERT_ALWAYS( first.Length() + second.Length() == 64, Panic( EPanicJoinMaths ) ); + + // + // Get a location in the Flash to write to + // + TUint dataChunk = iBlocks->NextErasedDataChunk( 64, 64 ); + + // + // Write the two halves of the data + // + _LIT( KWriteMsg, " writing %d bytes @ 0x%x and %d bytes @ 0x%x" ); + test.Printf( KWriteMsg, first.Length(), dataChunk, + second.Length(), dataChunk + first.Length() ); + test( KErrNone == iDrive.Write( dataChunk, first ) ); + test( KErrNone == iDrive.Write( dataChunk + first.Length(), second ) ); + + // + // Compare the data + // + _LIT( KCompareMsg, " comparing data against Flash" ); + test.Printf( KCompareMsg ); + test( CompareAgainstFlash( dataChunk, fullData ) ); + } + } + + +void CWriteTest::JoinedThreadWriteTest() + // + // Makes two consecutive writes. Checks that the complete + // data block was written correctly. The data is written within + // a 64-byte window and the join position is moved along to each + // possible location + // + // This is similar to JoinedWriteTest except that the thread write + // function is used with a descriptor offset to chop up the + // source data + // + { + + test.Next( _L("Joined write test, thread writes") ); + + // + // Reinitialise the chunk allocator + // + iBlocks->InitialiseDataChunkAllocator(); + + for( TInt join = 1; join < 63; join++ ) + { + TBuf8<64> fullData; + CreateRandomData( fullData, fullData.MaxLength() ); + + // + // Get a location in the Flash to write to + // + TUint dataChunk = iBlocks->NextErasedDataChunk( 64, 64 ); + + // + // Write the two halves of the data + // + _LIT( KWriteMsg, " writing %d bytes @ 0x%x and %d bytes @ 0x%x" ); + test.Printf( KWriteMsg, join, dataChunk, 64 - join, dataChunk + join ); +#if 0 + test( KErrNone == iDrive.Write( dataChunk, join, &fullData, DummyThreadHandle(), 0 ) ); + test( KErrNone == iDrive.Write( dataChunk + join, 64-join, &fullData, DummyThreadHandle(), join ) ); +#else + test( KErrNone == iDrive.Write( dataChunk, join, &fullData, KLocalMessageHandle, 0 ) ); + test( KErrNone == iDrive.Write( dataChunk + join, 64-join, &fullData, KLocalMessageHandle, join ) ); +#endif + + + // + // Compare the data + // + _LIT( KCompareMsg, " comparing data against Flash" ); + test.Printf( KCompareMsg ); + test( CompareAgainstFlash( dataChunk, fullData ) ); + } + } + + + +void CWriteTest::SingleBitOverwriteTest() + // + // Tests overwriting single bits within a byte. a 32-byte + // section of Flash is filled with data, with one byte initially + // 0xFF. A bit is then written to zero and the whole data block + // is verified. + // + { + test.Next( _L("Single bit overwrite test") ); + + iBlocks->InitialiseDataChunkAllocator(); + + for( TInt testByteOffset = 0; testByteOffset < 32; testByteOffset++ ) + { + for( TInt testBitNumber = 0; testBitNumber < 8; testBitNumber++ ) + { + TBuf8<32> data; + CreateRandomData( data, data.MaxLength() ); + data[ testByteOffset ] = 0xFF; // force test byte to 0xFF + + TUint flashOffset = iBlocks->NextErasedDataChunk( 32, 32 ); + + _LIT( KWriteMsg, "writing test data @0x%x, test byte offset=%d; test bit #%d"); + test.Printf( KWriteMsg, flashOffset, testByteOffset, testBitNumber ); + + iSimpleWriter->CheckedWrite( flashOffset, data ); + + // clear the test bit + TBuf8<1> byte; + byte.SetLength(1); + byte[0] = ~(1 << testBitNumber); + data[ testByteOffset ] = byte[0]; + + iSimpleWriter->CheckedWrite( flashOffset + testByteOffset, byte ); + + // check that the contents of the Flash matches the buffer + test( CompareAgainstFlash( flashOffset, data ) ); + } + } + } + +void CWriteTest::TwoBitOverwriteTest() + // + // Tests overwriting two bits within a byte. a 32-byte + // section of Flash is filled with data, with one byte initially + // 0xFF. Two bits are then written to zero and the whole data block + // is verified. + // + { + static const TUint pattConv[16] = + { + // used to create a string representation of binary value + 0x0000, 0x0001, 0x0010, 0x0011, 0x0100, 0x0101, 0x0110, 0x0111, + 0x1000, 0x1001, 0x1010, 0x1011, 0x1100, 0x1101, 0x1110, 0x1111 + }; + test.Next( _L("Two bit overwrite test") ); + + for( TInt testByteOffset = 0; testByteOffset < 32; testByteOffset++ ) + { + for( TInt testBitJ = 0; testBitJ < 7; testBitJ++ ) + { + for( TInt testBitK = testBitJ+1; testBitK < 8; testBitK++ ) + { + TBuf8<32> data; + CreateRandomData( data, data.MaxLength() ); + data[ testByteOffset ] = 0xFF; // force test byte to 0xFF + + TUint flashOffset = iBlocks->NextErasedDataChunk( 32, 32 ); + + TUint8 testPattern = ~((1 << testBitJ) | (1 << testBitK)); + + _LIT( KWriteMsg, "writing test data @0x%x, test byte offset=%d; test pattern = %04x%04x"); + test.Printf( KWriteMsg, flashOffset, testByteOffset, + pattConv[ testPattern >> 4 ], pattConv[ testPattern&0xF ] ); + + iSimpleWriter->CheckedWrite( flashOffset, data ); + + TBuf8<1> byte; + byte.SetLength(1); + byte[0] = testPattern; + data[ testByteOffset ] = testPattern; + + iSimpleWriter->CheckedWrite( flashOffset + testByteOffset, byte ); + + // check that the contents of the Flash matches the buffer + test( CompareAgainstFlash( flashOffset, data ) ); + } + } + } + } + + +void CWriteTest::RunSimulationTest() + // + // A simulation of the way the LFFS filesystem will use a Flash block + // Alternately writes 24 bytes to bottom of block, 512 bytes to top, + // clears a bit in the 24-byte block. Repeats until block is full. + // + { + test.Next( _L("Simulation test") ); + + TUint blockBase = iBlocks->BlockAddress( iBlocks->NextErasedBlock() ); + + TUint lowAddress = blockBase; + TUint highAddress = blockBase + iBlocks->BlockSize() - 512; + + TBuf8<24> lowData; + TBuf8<512> highData; + TPtrC8 overwritePtr( lowData.Ptr(), 1 ); + + while( lowAddress + 24 < highAddress ) + { + CreateRandomData( lowData, lowData.MaxLength() ); + CreateRandomData( highData, highData.MaxLength() ); + lowData[0] = 0xE7; // just some non-0xFF value + + _LIT( KWriteMsg, "Writing block size 24 @ 0x%x; block size 512 @ 0x%x" ); + test.Printf( KWriteMsg, lowAddress, highAddress ); + + iSimpleWriter->CheckedWrite( lowAddress, lowData ); + iSimpleWriter->Write( highAddress, highData ); + + // Overwrite the byte + lowData[0] = 0xA7; + iSimpleWriter->Write( lowAddress, overwritePtr ); + + test( CompareAgainstFlash( lowAddress, lowData ) ); + test( CompareAgainstFlash( highAddress, highData ) ); + + lowAddress += lowData.Length(); + highAddress -= highData.Length(); + } + } + + + +void CWriteTest::DoTests() + // + // Main test dispatcher + // + { + test.Next( _L("Erasing all blocks") ); + iBlocks->InitialiseSequentialBlockAllocator(); + iBlocks->EraseAllBlocks(); + + // + // Basic tests that we can write data correctly without corrupting + // the source buffer + // + SimpleWriteTest(); + SimpleThreadWriteTest(); + + // + // Test aligned writes of various lengths + // + AlignedWriteTest(); + AlignedThreadWriteTest(); + + // + // Test writing to unaligned locations + // + UnalignedWriteTest(); + UnalignedThreadWriteTest(); + + // + // Test writes with offset into source desriptor + // + OffsetDescriptorCurrentThreadAlignedWriteTest(); + OffsetDescriptorCurrentThreadUnalignedWriteTest(); + OffsetDescriptorAlignedWriteTest(); + OffsetDescriptorUnalignedWriteTest(); + + // + // Test two consecutive writes + // + JoinedWriteTest(); + JoinedThreadWriteTest(); + + // + // Test that we can overwrite bits + // + SingleBitOverwriteTest(); + TwoBitOverwriteTest(); + + // + // A simulation test of LFFS usage + // + RunSimulationTest(); + } + + + + + + + + + + +TInt E32Main() + { + test.Title(); + test.Start(_L("Testing media read operations")); + + CWriteTest writeTest; + TRAPD( ret, writeTest.CreateL() ); + test( KErrNone == ret ); + writeTest.DoTests(); + test.End(); + + return 0; + }