diff -r 000000000000 -r 96e5fb8b040d userlibandfileserver/fileserver/sfat/sl_fatcache.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/userlibandfileserver/fileserver/sfat/sl_fatcache.cpp Thu Dec 17 09:24:54 2009 +0200 @@ -0,0 +1,1122 @@ +// 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: +// f32\sfat32\sl_fatcache.cpp +// FAT12 and FAT16 cache implementation +// +// + +/** + @file +*/ + + +//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +//!! +//!! WARNING!! DO NOT edit this file !! '\sfat' component is obsolete and is not being used. '\sfat32'replaces it +//!! +//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +#include "sl_std.h" +#include "sl_fatcache.h" + + +//################################################################################################################################# +// CFatCacheBase implementation +// Base class for all types of FAT cache +//################################################################################################################################# + +CFatCacheBase::~CFatCacheBase() + { + Close(ETrue); //-- deallocate cache's memory discarding any dirty data + } + +CFatCacheBase::CFatCacheBase() + { + iCurrentFatNo = KInvalidFatNo; + SetDirty(EFalse); + } + + +/** + FAT cache initialisation. + + @param aOwner pointer to the owning FAT mount +*/ +void CFatCacheBase::InitialiseL(CFatMountCB* aOwner) + { + ASSERT(aOwner); + + Close(ETrue); //-- deallocate cache's memory discarding any dirty data + + //-- populate parameters from the owning mount + iFatType = aOwner->FatType(); + __ASSERT_ALWAYS((iFatType == EFat12 || iFatType == EFat16 || iFatType == EFat32), User::Leave(KErrCorrupt)); + + ipDrive = &aOwner->DriveInterface(); + iFatStartPos = aOwner->FirstFatSector() << aOwner->SectorSizeLog2(); + iFatSize = aOwner->FatSizeInBytes(); + iNumFATs = (TUint16)aOwner->NumberOfFats(); + iFatSecSzLog2 = (TUint16)aOwner->SectorSizeLog2(); + iFatClustSzLog2 = (TUint16)aOwner->ClusterSizeLog2(); + + __ASSERT_ALWAYS(iNumFATs >=1, User::Leave(KErrCorrupt)); + + __PRINT3(_L("#-CFatCacheBase::InitialiseL() FatStart:%u, FatSz:%d, drv:%d"),iFatStartPos, iFatSize, aOwner->DriveNumber()); + } + +//----------------------------------------------------------------------------- +/** + This method shall be called to check if we are allowed to invalidate dirty cache, i.e. discard non-flushed data. + The behaviour is hardcoded (see KAllowInvalidateDirtyCache constant) + + @return ETrue if invalidating dirty cache is allowed. Otherwise panics the current thread +*/ +TBool CFatCacheBase::CheckInvalidatingDirtyCache() const + { + + //-- If not EFalse, invalidating dirty cache (pages) is allowed. This shall be OK, because + //-- invalidating the cache is required only after direct media writes to the FAT by RawWrite, which can corrupt it anyway. + TBool KAllowInvalidateDirtyCache = ETrue; + + if(!IsDirty()) + return KAllowInvalidateDirtyCache; + + __PRINT(_L("#-CFatCacheBase::Invalidating dirty cache !")); + + if(!KAllowInvalidateDirtyCache) + { + __ASSERT_ALWAYS(0, Fault(EFatCache_DiscardingDirtyData)); + } + + return KAllowInvalidateDirtyCache; + } + +//----------------------------------------------------------------------------- + +/** + Read portion of raw data from 1st FAT copy. + + @param aPos media position in the _FIRST_ FAT to start reading with + @param aLen number of bytes to read + @param aData data descriptor + + @return standard error code. +*/ +TInt CFatCacheBase::ReadFatData(TUint32 aPos, TUint32 aLen, TDes8& aData) const + { + //__PRINT2(_L("#-CFatCacheNew::ReadFatData() pos:%u, Len:%d"), aPos, aLen); + + //-- this method can pick up data corresponding to invalid FAT entries, like FAT[0], FAT[1] and + //-- the last portion beyond FAT because of read granularity. This isn't a problem, because the data there + //-- won't be written on disk. + ASSERT(aPos >= FatStartPos()); + + return ipDrive->ReadNonCritical(aPos, aLen, aData); + } + +//----------------------------------------------------------------------------- + +/** + Writes data to the FAT table, which number is set in iCurrentFatNo member variable. + @param aPos data media position in the _FIRST_ FAT copy + @param aData data descriptor + @return standard error code. +*/ +TInt CFatCacheBase::WriteFatData(TUint32 aPos, const TDesC8& aData) const + { + //__PRINT3(_L("#-CFatCacheBase::WriteFatData() pos:%u, Len:%d, FAT:%d"), aPos, aData.Length(), iCurrentFatNo); + +#ifdef _DEBUG + //-- FAT[0] and FAT[1] entries are reserved and we must not write data there. It's up to the caller of this method to + //-- calculate correct data position in FAT + TInt reserved_Entries_Offset=0; + switch(iFatType) + { + case EFat32: reserved_Entries_Offset = KFatFirstSearchCluster*sizeof(TFat32Entry); break; //-- FAT32 + case EFat16: reserved_Entries_Offset = KFatFirstSearchCluster*sizeof(TFat16Entry); break; //-- FAT16 + case EFat12: reserved_Entries_Offset = 3; break; //-- FAT12 + default: ASSERT(0); break; + } + ASSERT(aPos >= FatStartPos()+reserved_Entries_Offset); + ASSERT((aPos+aData.Length()) <= FatStartPos()+FatSize()); + ASSERT(iCurrentFatNo < iNumFATs); +#endif + + //-- goto the required FAT copy. iCurrentFatNo shall contain FAT number we are writing to. + aPos+=iCurrentFatNo*FatSize(); + + return ipDrive->WriteCritical(aPos, aData); + } + +//----------------------------------------------------------------------------- +/** + get a pointer to the CFatBitCache interface. + @return NULL because it is not present here +*/ +CFatBitCache* CFatCacheBase::BitCacheInterface() + { + return NULL; + } + + +//################################################################################################################################# +// CFatPagedCacheBase implementation +// Base class for all paged FAT caches +//################################################################################################################################# + +CFatPagedCacheBase::CFatPagedCacheBase() + :CFatCacheBase() + { + } + + +//################################################################################################################################# +// CFatCachePageBase implementation +// Base class for FAT cache pages (FAT16 fixed and FAT32 LRU) +//################################################################################################################################# + +CFatCachePageBase::CFatCachePageBase(CFatPagedCacheBase& aCache) + :iCache(aCache) + { + ASSERT(IsPowerOf2(aCache.PageSize())); + iStartIndexInFAT = KMaxTUint; + + //-- calculate number of FAT entries in the page, it depends on FAT type + switch(aCache.FatType()) + { + case EFat32: + iFatEntriesInPage = PageSize() >> KFat32EntrySzLog2; + break; + + case EFat16: + iFatEntriesInPage = PageSize() >> KFat16EntrySzLog2; + break; + + default: + ASSERT(0); + Fault(EFatCache_BadFatType); + break; + + }; + + SetState(EInvalid); + } + +CFatCachePageBase::~CFatCachePageBase() + { + iData.Close(); + } + +//----------------------------------------------------------------------------- +/** + Mark the page as "invalid". I.e containing inalid data. + On the first read/write access to such page it will be re-read from the media + + @param aIgnoreDirtyData if ETrue, it is allowed to ignore the fact that the page contains dirty (not flushed) data. +*/ +void CFatCachePageBase::Invalidate(TBool aIgnoreDirtyData /*= EFalse*/) + { + if(!aIgnoreDirtyData && IsDirty()) + { + __PRINT1(_L("#-CFatCachePageBase::Invalidate() dirty page! FAT idx:%d"), iStartIndexInFAT); + __ASSERT_ALWAYS(0, Fault(EFatCache_DiscardingDirtyData)); + } + + iDirtySectors.Clear(); //-- clear dirty sectors bitmap + SetState(EInvalid); + } + +//----------------------------------------------------------------------------- +/** + Flush all dirty page sectors to the media and mark the page as "clean" if required. + If the page is "clean" i.e doesn't contain changed data, does nothing. + + @param aKeepDirty if ETrue, the "dirty" flag isn't reset after page flushing. +*/ +void CFatCachePageBase::FlushL(TBool aKeepDirty) + { + if(!IsDirty()) + return; + + if(!IsValid()) + { + __PRINT1(_L("#-CFatCachePageBase::FlushL() Invalid page! FAT idx:%d"), iStartIndexInFAT); + ASSERT(0); + User::Leave(KErrCorrupt); + return; + } + + //__PRINT1(_L("#-CFatCachePageBase::FlushL() FAT idx:%d"), iStartIndexInFAT); + + //-- write dirty FAT sectors to the media one by one. + //-- merging adjacent dirty subsectors into larger clusters and writing them at once looks like a good idea, but + //-- in reality it showed FAT performance degradation, at least on MMC/SD media. + + const TInt MaxSectors = iCache.SectorsInPage(); + + for(TInt i=0; iInitialiseL(aOwner, aFatSize, aRdGranularityLog2, aWrGranularityLog2); + CleanupStack::Pop(); + + return pSelf; + } + +//----------------------------------------------------------------------------- +/** + FAT16 fixed cache initialisation. + @param aOwner pointer to the owning FAT mount + @param aFatSize size of the FAT table in bytes + @param aRdGranularityLog2 Log2(read granularity) + @param aWrGranularityLog2 Log2(write granularity) +*/ +void CFat16FixedCache::InitialiseL(CFatMountCB* aOwner, TUint32 aFatSize, TUint32 aRdGranularityLog2, TUint32 aWrGranularityLog2) + { + const TUint32 ReadGranularity = Pow2(aRdGranularityLog2); + const TUint32 WriteGranularity = Pow2(aWrGranularityLog2); + + __PRINT3(_L("#-CFat16FixedCache::InitialiseL FatSz:%u, RdGr:%d, WrGr:%d"),aFatSize, ReadGranularity, WriteGranularity); + (void)ReadGranularity; + (void)WriteGranularity; + + TBool bParamsValid = (aRdGranularityLog2 >= aWrGranularityLog2) && (aWrGranularityLog2 >= KDefSectorSzLog2); + __ASSERT_ALWAYS(bParamsValid, Fault(EFatCache_BadGranularity)); + + CFatPagedCacheBase::InitialiseL(aOwner); + + ASSERT(FatType() == EFat16); + + //-- See FAT specs, and round up the limit to the FAT sector boundary + const TUint32 KMaxFat16Size = ((65524*sizeof(TFat16Entry)+FAT_SectorSz()-1) >> FAT_SectorSzLog2()) << FAT_SectorSzLog2(); + const TUint32 KMinFat16Size = 4086*sizeof(TFat16Entry); //-- See FAT specs + + bParamsValid = aFatSize >= KMinFat16Size && aFatSize <= KMaxFat16Size; + __ASSERT_ALWAYS(bParamsValid, User::Leave(KErrCorrupt)); + + //-- cache page size is (2^aRdGranularityLog2) bytes and consists of 2^(aRdGranularityLog2-aWrGranularity) sectors. + iPageSizeLog2 = aRdGranularityLog2; + iSectorSizeLog2 = aWrGranularityLog2; //-- Log2(number of sectors in cache page) + + __ASSERT_ALWAYS(SectorsInPage() < KMaxSectorsInPage, Fault(EFatCache_BadGranularity)); + + const TUint numPages = (aFatSize+(PageSize()-1)) >> iPageSizeLog2; + __PRINT1(_L("#-CFat16FixedCache Num Pages:%d"), numPages); + + //-- prepare pointer array for pages. NULL entry in the array means that the page at this index isn't allocated. + for(TUint i=0; iIsDirty())) + {//-- trying to destroy the cache that has dirty pages + __PRINT1(_L("#-CFat16FixedCache::Close() The page is dirty! Start idx:%d"), pPage->StartFatIndex()); + if(!aDiscardDirtyData) + { + __ASSERT_ALWAYS(0, Fault(EFatCache_DiscardingDirtyData)); + } + //-- ignore this fact if requested. + } + + delete pPage; + } + + iPages.Close(); + SetDirty(EFalse); + } + +//----------------------------------------------------------------------------- +/** + Read FAT entry from the cache. + + @param aIndex FAT entry index to read + @return FAT entry value at the index "aIndex" +*/ +TUint32 CFat16FixedCache::ReadEntryL(TUint32 aIndex) + { + //__PRINT1(_L("#-CFat16FixedCache::ReadEntryL() FAT idx:%d"), aIndex); + ASSERT(aIndex >= KFatFirstSearchCluster && aIndex < (FatSize() >> KFat16EntrySzLog2)); + + //-- calculate page index in the array + const TInt pgIdx = aIndex >> (PageSizeLog2()-KFat16EntrySzLog2); + CFat16FixedCachePage *pPage = iPages[pgIdx]; + + TUint32 entry = KMaxTUint; + + if(!pPage) + {//-- page at this position isn't allocated yet + pPage = CFat16FixedCachePage::NewL(*this); + iPages[pgIdx] = pPage; + + //-- read the page from media + entry = pPage->ReadFromMediaL(aIndex); + } + else + {//-- get cached entry from the page + TBool bRes = pPage->ReadCachedEntryL(aIndex, entry); + ASSERT(bRes); + (void)bRes; + } + + return entry; + } + +//----------------------------------------------------------------------------- +/** + Write FAT entry to the cache. + Appropriate FAT cache sector will be marked as "dirty" and will be eventually flushed to the media. + + @param aIndex FAT entry index + @param aEntry FAT entry value +*/ +void CFat16FixedCache::WriteEntryL(TUint32 aIndex, TUint32 aEntry) + { + //__PRINT2(_L("#-CFat16FixedCache::WriteEntryL() FAT idx:%d, val:%d"), aIndex, aEntry); + + ASSERT(aIndex >= KFatFirstSearchCluster && aIndex < (FatSize() >> KFat16EntrySzLog2)); + + SetDirty(ETrue); + + //-- calculate page index in the array + const TInt pgIdx = aIndex >> (PageSizeLog2()-KFat16EntrySzLog2); + CFat16FixedCachePage *pPage = iPages[pgIdx]; + + if(!pPage) + {//-- page at this position isn't allocated yet + pPage = CFat16FixedCachePage::NewL(*this); + iPages[pgIdx] = pPage; + + //-- read the page from media + pPage->ReadFromMediaL(aIndex); + } + + //-- overwrite entry in cache + TBool bRes = pPage->WriteCachedEntryL(aIndex, aEntry); + ASSERT(bRes); + (void)bRes; + } + +/** + A debug method that asserts that the cache is really clean +*/ +void CFat16FixedCache::AssertCacheReallyClean() const + { +#ifdef _DEBUG + for(TUint i=0; iIsDirty()) + { + __PRINT(_L("#-CFat16FixedCache::AssertCacheReallyClean()")); + ASSERT(0); + } + } +#endif + } + + +//----------------------------------------------------------------------------- +/** + Flushes all dirty data to the media. +*/ +void CFat16FixedCache::FlushL() + { + if(!IsDirty()) + { + AssertCacheReallyClean(); + return; + } + + + //-- flush dirty data to all copies of FAT + for(iCurrentFatNo=0; iCurrentFatNo < NumFATs(); ++iCurrentFatNo) + { + const TInt nPages = NumPages(); + for(TInt i=0; iFlushL(keepDirty); + } + + } + + iCurrentFatNo = KInvalidFatNo; + SetDirty(EFalse); + } + +//----------------------------------------------------------------------------- +/** + Invalidate whole cache. All pages will be marked as invalid and will be re-read from the media on first access to them. + @return always KErrNone +*/ +TInt CFat16FixedCache::Invalidate() + { + __PRINT(_L("#-CFat16FixedCache::Invalidate()")); + const TBool bIgnoreDirtyData = CheckInvalidatingDirtyCache(); + + //-- iterate through the array of pages marking invalidating every page + TInt cnt = iPages.Count(); + while(cnt--) + {//-- delete pages + CFat16FixedCachePage *pPage = iPages[cnt]; + if(pPage) + pPage->Invalidate(bIgnoreDirtyData); + } + + + SetDirty(EFalse); + + return KErrNone; + } + +//----------------------------------------------------------------------------- +/** + Invalidate FAT cache pages that contain FAT entries from aStartIndex to (aStartIndex+aNumEntries) + These pages will be marked as invalid and will be re-read from the media on first access to them. + + @param aStartIndex FAT start index of the region being invalidated + @param aNumEntries number of entries to invalidate + @return always KErrNone +*/ +TInt CFat16FixedCache::InvalidateRegion(TUint32 aStartIndex, TUint32 aNumEntries) + { + __PRINT2(_L("#-CFat16FixedCache::InvalidateRegion() startIndex:%d, entries:%d"),aStartIndex, aNumEntries); + ASSERT(aStartIndex >= KFatFirstSearchCluster && aStartIndex < (FatSize() >> KFat16EntrySzLog2)); + + if(!aNumEntries) + { + ASSERT(0); + return KErrNone; + } + + const TBool bIgnoreDirtyData = CheckInvalidatingDirtyCache(); + const TUint startPgIdx = aStartIndex >> (PageSizeLog2()-KFat16EntrySzLog2); + const TUint nPagesToInv = 1+(aNumEntries >> (PageSizeLog2()-KFat16EntrySzLog2)); + + TUint i; + //-- invalidate pages that contain [aStartIndex ... aStartIndex+aNumEntries] entries + for(i=0; i= NumPages()) + break; + + CFat16FixedCachePage* pPage = iPages[pageIdx]; + if(pPage) + pPage->Invalidate(bIgnoreDirtyData); + } + + SetDirty(EFalse); + + //-- check if the cache still has dirty pages + for(i=0; iIsDirty()) + { + SetDirty(ETrue); + break; + } + } + + return KErrNone; + } + +//################################################################################################################################# +// CFat16FixedCachePage implementation +// Page for the FAT16 fixed cache +//################################################################################################################################# + +//----------------------------------------------------------------------------- + +CFat16FixedCachePage::CFat16FixedCachePage(CFatPagedCacheBase& aCache) + :CFatCachePageBase(aCache) + { + ASSERT(IsPowerOf2(EntriesInPage())); + } + + +/** + Factory function. + @param aCache reference to the owning cache. + @return pointer to the constructed object or NULL on error +*/ +CFat16FixedCachePage* CFat16FixedCachePage::NewL(CFatPagedCacheBase& aCache) + { + CFat16FixedCachePage* pSelf = NULL; + pSelf = new (ELeave) CFat16FixedCachePage(aCache); + + CleanupStack::PushL(pSelf); + + pSelf->iData.CreateMaxL(aCache.PageSize()); //-- allocate memory for the page + + CleanupStack::Pop(); + + return pSelf; + } + + +//----------------------------------------------------------------------------- +/** + Read FAT16 entry from the cache. + + 1. If page's data are valid, just extracts data from the page buffer. + 2. If page's data are invalid firstly reads data from the media and goto 1 + + @param aFatIndex entry's absolute FAT index (from the FAT start) + @param aResult on sucess there will be FAT16 entry value + @return ETrue, because FAT16 cache pages never get eviched. +*/ +TBool CFat16FixedCachePage::ReadCachedEntryL (TUint32 aFatIndex, TUint32& aResult) + { + if(IsValid()) + {//-- read entry directly from page buffer, the cached data are valid + aResult = (*GetEntryPtr(aFatIndex)) & KFat16EntryMask; + } + else + {//-- aFatIndex belongs to this page, but the page is invalid and needs to be read from the media + //__PRINT(_L("#-CFat16FixedCachePage::ReadCachedEntry() The page is invalid, reading from the media")); + aResult = ReadFromMediaL(aFatIndex); + } + + return ETrue; + } + +//----------------------------------------------------------------------------- + +/** + Writes FAT cache page sector to the media (to all copies of the FAT) + @param aSector sector number winthin this page +*/ +void CFat16FixedCachePage::DoWriteSectorL(TUint32 aSector) + { + //__PRINT1(_L("#-CFat16FixedCachePage::DoWriteSectorL() startSec:%d, cnt:%d"), aSector); + + ASSERT(aSector < iCache.SectorsInPage()); + + TInt offset = 0; + + if(iStartIndexInFAT == 0 && aSector == 0) + {//-- this is the very beginning of FAT16. We must skip FAT[0] & FAT[1] entries and do not write them to media. + offset = KFatFirstSearchCluster << KFat16EntrySzLog2; + } + + const TUint8* pData = iData.Ptr()+offset+(aSector << iCache.SectorSizeLog2()); + + TUint32 dataLen = (1 << iCache.SectorSizeLog2()) - offset; + + const TUint32 mediaPosStart = iCache.FatStartPos() + (iStartIndexInFAT << KFat16EntrySzLog2) + (aSector << iCache.SectorSizeLog2()) + offset; + const TUint32 mediaPosEnd = mediaPosStart + dataLen; + + //-- check if we are going to write beyond FAT. It can happen if the write granularity is bigger that the sector size. + const TUint32 posFatEnd = iCache.FatStartPos() + iCache.FatSize(); + if(mediaPosEnd > posFatEnd) + {//-- correct the leength of the data to write. + dataLen -= (mediaPosEnd-posFatEnd); + } + + TPtrC8 ptrData(pData, dataLen); //-- source data descriptor + + TInt nRes = iCache.WriteFatData(mediaPosStart, ptrData); + + if(nRes != KErrNone) + { + __PRINT1(_L("#-CFat16FixedCachePage::DoWriteSectorsL() failed! code:%d"), nRes); + User::Leave(nRes); + } + + } + +//----------------------------------------------------------------------------- +/** + Write FAT16 entry at aFatIndex to the cache. Note that the data are not written to the media, only to the cache page. + Corresponding page sector is marked as dirty and will be flushed on FlushL() call later. + + 1. If page's data are valid, copies data to the page buffer and marks sector as dirty. + 2. If page's data are invalid, firstly reads data from the media and goto 1 + + @param aFatIndex entry's absolute FAT index (from the FAT start) + @param aFatEntry FAT16 entry value + @return ETrue because FAT16 cache pages never get eviched. +*/ +TBool CFat16FixedCachePage::WriteCachedEntryL(TUint32 aFatIndex, TUint32 aFatEntry) + { + + ASSERT(IsEntryCached(aFatIndex)); + + if(!IsValid()) + {//-- we are trying to write data to the page that has invalid data. //-- read the data from the media first. + ReadFromMediaL(aFatIndex); + } + + TFat16Entry* pEntry = GetEntryPtr(aFatIndex); + + const TFat16Entry orgEntry = *pEntry; + *pEntry = (TFat16Entry)((orgEntry & ~KFat16EntryMask) | (aFatEntry & KFat16EntryMask)); + + //-- mark corresponding sector of the cache page as dirty + const TUint entryIndexInPage = aFatIndex & (EntriesInPage()-1); //-- number of entries in page is always a power of 2 + const TUint dirtySectorNum = entryIndexInPage >> (iCache.SectorSizeLog2() - KFat16EntrySzLog2); + + ASSERT(dirtySectorNum < iCache.SectorsInPage()); + + iDirtySectors.SetBit(dirtySectorNum); + SetState(EDirty); //-- mark page as dirty. + + return ETrue; + } + +//----------------------------------------------------------------------------- + +/** + Get a pointer to the FAT16 entry in the page buffer. + The page 's data shall be valid and the entry shall belong to this page. + + @param aFatIndex absolute FAT index (from the FAT start) of the entry + @return pointer to the FAT16 entry in the page buffer. +*/ +TFat16Entry* CFat16FixedCachePage::GetEntryPtr(TUint32 aFatIndex) const + { + ASSERT(IsValid() && IsEntryCached(aFatIndex)); + + const TUint KEntryIndexInPage = aFatIndex & (EntriesInPage()-1); //-- number of entries in page is always a power of 2 + TFat16Entry* pEntry = ((TFat16Entry*)iData.Ptr()) + KEntryIndexInPage; + + return pEntry; + } + +//----------------------------------------------------------------------------- +/** + Read the FAT16 cache page from the media and returns required FAT16 entry. + + @param aFatIndex entry's absolute FAT index (from the FAT start) + @return entry value at aFatIndex. +*/ +TUint32 CFat16FixedCachePage::ReadFromMediaL(TUint32 aFatIndex) + { + //__PRINT1(_L("#-CFat16FixedCachePage::ReadFromMediaL() FAT idx:%d"), aFatIndex); + const TUint KFat16EntriesInPageLog2 = iCache.PageSizeLog2()-KFat16EntrySzLog2; //-- number of FAT16 entries in page is always a power of 2 + + //-- find out index in FAT this page starts from + iStartIndexInFAT = (aFatIndex >> KFat16EntriesInPageLog2) << KFat16EntriesInPageLog2; + SetState(EInvalid); //-- mark the page as invalid just in case if the read fails. + + //-- read page from the media + const TUint32 pageStartPos = iCache.FatStartPos() + (iStartIndexInFAT << KFat16EntrySzLog2); + + TInt nRes = iCache.ReadFatData(pageStartPos, iCache.PageSize(), iData); + if(nRes != KErrNone) + { + __PRINT1(_L("#-CFat16FixedCachePage::ReadFromMediaL() failed! code:%d"), nRes); + User::Leave(nRes); + } + + SetClean(); //-- mark this page as clean + + const TFat16Entry entry = (TFat16Entry)((*GetEntryPtr(aFatIndex)) & KFat16EntryMask); + + return entry; + } + + +//----------------------------------------------------------------------------- + +//################################################################################################################################# +// CFat12Cache implementation +// FAT12 non-paged fixed cache. This cache consists from only 1 page, logically divided up to 32 sectors (write granularity unit) +//################################################################################################################################# + +CFat12Cache::CFat12Cache() + :CFatCacheBase() + { + } + +//----------------------------------------------------------------------------- +/** + FAT12 fixed cache factory function. + @param aOwner pointer to the owning FAT mount + @param aFatSize size of the FAT table in bytes + + @return pointer to the constructed object. +*/ +CFat12Cache* CFat12Cache::NewL(CFatMountCB* aOwner, TUint32 aFatSize) + { + __PRINT(_L("#-CFat12Cache::NewL()")); + CFat12Cache* pSelf = NULL; + pSelf = new (ELeave) CFat12Cache; + + CleanupStack::PushL(pSelf); + pSelf->InitialiseL(aOwner, aFatSize); + CleanupStack::Pop(); + + return pSelf; + } + +//----------------------------------------------------------------------------- +/** + FAT16 fixed cache initialisation. + @param aOwner pointer to the owning FAT mount + @param aFatSize size of the FAT table in bytes +*/ +void CFat12Cache::InitialiseL(CFatMountCB* aOwner, TUint32 aFatSize) + { + __PRINT1(_L("#-CFat12Cache::InitialiseL FatSz:%u"),aFatSize); + + CFatCacheBase::InitialiseL(aOwner); + ASSERT(FatType() == EFat12); + + //-- see FAT specs; 4084 is a max. number of clusters, fat12 entry is 1.5 bytes; but we need to round up FAT12 size to the sector size + const TUint32 KMaxFat12Size = ( ((TUint32)(4084*1.5+FAT_SectorSz()-1)) >> FAT_SectorSzLog2()) << FAT_SectorSzLog2(); + const TUint32 KMinFat12Size = FAT_SectorSz(); //-- 1 FAT sector + __ASSERT_ALWAYS(aFatSize >= KMinFat12Size && aFatSize <= KMaxFat12Size, User::Leave(KErrCorrupt)); + (void)KMaxFat12Size; + (void)KMinFat12Size; + + //-- as soon as FAT12 max size is 4084 entries or 6126 bytes, the cache is contiguous and divided + //-- to logical sectors (write granularity). + + //-- calculate number write cache sector in the cache + iSectorsInCache = (aFatSize + (FAT_SectorSz()-1)) >> FAT_SectorSzLog2(); + __ASSERT_ALWAYS(NumSectors() <= KMaxSectorsInCache, Fault(EFatCache_BadGranularity)); + + //-- round up cache size to write granularity (sector size) + const TUint32 cacheSize = NumSectors() << FAT_SectorSzLog2(); + + //-- create buffer for the whole FAT12 + iData.CreateMaxL(cacheSize); + + //-- this will read whole FAT into the cache + User::LeaveIfError(Invalidate()); + } + +//----------------------------------------------------------------------------- +/** + Close the cache and deallocate its memory. + @param aDiscardDirtyData if ETrue, will ignore dirty data. If EFalse, will panic on atempt to close dirty cache. +*/ +void CFat12Cache::Close(TBool aDiscardDirtyData) + { + __PRINT1(_L("#-CFat12Cache::Close(%d)"), aDiscardDirtyData); + + for(TUint32 i=0; i= KFatFirstSearchCluster && aIndex < (FatSize() + FatSize()/2)); //-- FAT12 entry is 1.5 bytes long + + TUint32 entry; + + if(aIndex & 0x01) + {//-- odd index + --aIndex; + const TUint32 byteIdx = 1 + aIndex + (aIndex >> 1); //-- byteIdx = 1+(aIndex-1)*1.5 + Mem::Copy(&entry, iData.Ptr()+byteIdx, 2); + entry >>= 4; + } + else + {//-- even index + const TUint32 byteIdx = aIndex + (aIndex >> 1); //-- byteIdx = aIndex*1.5 + Mem::Copy(&entry, iData.Ptr()+byteIdx, 2); + } + + entry &= KFat12EntryMask; + + return entry; + } + +//----------------------------------------------------------------------------- +/** + Write FAT entry to the cache. + Appropriate FAT cache sector will be marked as "dirty" and will be eventually flushed to the media. + + @param aIndex FAT entry index + @param aEntry FAT entry value +*/ +void CFat12Cache::WriteEntryL(TUint32 aIndex, TUint32 aEntry) + { + //__PRINT2(_L("#-CFat12Cache::WriteEntryL() FAT idx:%d, entry:%u"), aIndex, aEntry); + ASSERT(aIndex >= KFatFirstSearchCluster && aIndex < (FatSize() + FatSize()/2)); //-- FAT12 entry is 1.5 bytes long + + aEntry &= KFat12EntryMask; + + TUint32 byteIdx = 0; + TUint8 tmp; + + if(aIndex & 0x01) + {//-- odd index + --aIndex; + byteIdx = 1 + aIndex + (aIndex >> 1); //-- byteIdx = 1+(aIndex-1)*1.5 + tmp = (TUint8)(iData[byteIdx] & 0x0F); //-- we modifying a higher nibble + tmp |= (TUint8) ((aEntry & 0x0F)<<4); + iData[byteIdx] = tmp; + + iData[byteIdx+1] = (TUint8)(aEntry >> 4); + } + else + {//-- even index + byteIdx = aIndex + (aIndex >> 1); //-- byteIdx = aIndex*1.5 + iData[byteIdx] = (TUint8)aEntry; + + const TUint32 nextIdx = byteIdx+1; + tmp = (TUint8)(iData[nextIdx] & 0xF0); //-- we modifying a lower nibble + tmp |= (TUint8)((aEntry >> 8) & 0x0F); + iData[nextIdx] = tmp; + + } + + //-- mark changed sectors dirty. We modified 2 bytes at [byteIdx] and [byteIdx+1] + iDirtySectors.SetBit(byteIdx >> FAT_SectorSzLog2()); + iDirtySectors.SetBit((byteIdx+1) >> FAT_SectorSzLog2()); + + SetDirty(ETrue); + } + +//----------------------------------------------------------------------------- +/** + A debug method that asserts that the cache is really clean +*/ +void CFat12Cache::AssertCacheReallyClean() const + { +#ifdef _DEBUG + if(iDirtySectors.HasBitsSet()) + { + __PRINT(_L("#-CFat12Cache::AssertCacheReallyClean()")); + ASSERT(0); + } + +#endif + } + +//----------------------------------------------------------------------------- +/** + Flushes all dirty data to the media. + Walks through all sectors in this cache and flushes dirty ones. +*/ +void CFat12Cache::FlushL() + { + if(!IsDirty()) + { + AssertCacheReallyClean(); + return; + } + + //-- write all dirty sectors to the media (into all copies of FAT) + for(iCurrentFatNo=0; iCurrentFatNo < NumFATs(); ++iCurrentFatNo) + { + for(TUint secNo=0; secNo= KFatFirstSearchCluster && aStartIndex < (FatSize() + FatSize()/2)); //-- FAT12 entry is 1.5 bytes long + (void)aStartIndex; + (void)aNumEntries; + + //-- just re-read all FAT12, it is just 6K max and isn't worth calculating invalid sectors + return Invalidate(); + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +