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