os/kernelhwsrv/userlibandfileserver/fileserver/sfat/sl_fatcache.cpp
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
     1 // Copyright (c) 1996-2009 Nokia Corporation and/or its subsidiary(-ies).
     2 // All rights reserved.
     3 // This component and the accompanying materials are made available
     4 // under the terms of the License "Eclipse Public License v1.0"
     5 // which accompanies this distribution, and is available
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
     7 //
     8 // Initial Contributors:
     9 // Nokia Corporation - initial contribution.
    10 //
    11 // Contributors:
    12 //
    13 // Description:
    14 // f32\sfat32\sl_fatcache.cpp
    15 // FAT12 and FAT16 cache implementation
    16 // 
    17 //
    18 
    19 /**
    20  @file
    21 */
    22 
    23 
    24 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    25 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    26 //!!
    27 //!! WARNING!! DO NOT edit this file !! '\sfat' component is obsolete and is not being used. '\sfat32'replaces it
    28 //!!
    29 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    30 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    31 
    32 #include "sl_std.h"
    33 #include "sl_fatcache.h"
    34 
    35 
    36 //#################################################################################################################################
    37 //  CFatCacheBase implementation
    38 //  Base class for all types of FAT cache
    39 //#################################################################################################################################
    40 
    41 CFatCacheBase::~CFatCacheBase()
    42     {
    43     Close(ETrue); //-- deallocate cache's memory discarding any dirty data
    44     }
    45 
    46 CFatCacheBase::CFatCacheBase()
    47     {
    48     iCurrentFatNo = KInvalidFatNo;
    49     SetDirty(EFalse);
    50     }
    51 
    52 
    53 /**
    54     FAT cache initialisation.
    55 
    56     @param  aOwner pointer to the owning FAT mount
    57 */
    58 void CFatCacheBase::InitialiseL(CFatMountCB* aOwner)
    59     {
    60     ASSERT(aOwner);
    61     
    62     Close(ETrue); //-- deallocate cache's memory discarding any dirty data
    63     
    64     //-- populate parameters from the owning mount
    65     iFatType = aOwner->FatType();
    66     __ASSERT_ALWAYS((iFatType == EFat12 || iFatType == EFat16 || iFatType == EFat32), User::Leave(KErrCorrupt));  
    67     
    68     ipDrive = &aOwner->DriveInterface();
    69     iFatStartPos = aOwner->FirstFatSector() << aOwner->SectorSizeLog2(); 
    70     iFatSize = aOwner->FatSizeInBytes();
    71     iNumFATs = (TUint16)aOwner->NumberOfFats();
    72     iFatSecSzLog2   = (TUint16)aOwner->SectorSizeLog2(); 
    73     iFatClustSzLog2 = (TUint16)aOwner->ClusterSizeLog2();
    74 
    75     __ASSERT_ALWAYS(iNumFATs >=1, User::Leave(KErrCorrupt));
    76 
    77     __PRINT3(_L("#-CFatCacheBase::InitialiseL() FatStart:%u, FatSz:%d, drv:%d"),iFatStartPos, iFatSize, aOwner->DriveNumber());
    78     }
    79 
    80 //-----------------------------------------------------------------------------
    81 /**
    82     This method shall be called to check if we are allowed to invalidate dirty cache, i.e. discard non-flushed data.
    83     The behaviour is hardcoded (see KAllowInvalidateDirtyCache constant)
    84 
    85     @return ETrue if invalidating dirty cache is allowed. Otherwise panics the current thread
    86 */
    87 TBool CFatCacheBase::CheckInvalidatingDirtyCache() const
    88     {
    89     
    90     //-- If not EFalse, invalidating dirty cache (pages) is allowed. This shall be OK, because
    91     //-- invalidating the cache is required only after direct media writes to the FAT by RawWrite, which can corrupt it anyway. 
    92     TBool KAllowInvalidateDirtyCache = ETrue;
    93     
    94     if(!IsDirty())
    95         return KAllowInvalidateDirtyCache;        
    96 
    97     __PRINT(_L("#-CFatCacheBase::Invalidating dirty cache !"));
    98     
    99     if(!KAllowInvalidateDirtyCache)
   100         {
   101         __ASSERT_ALWAYS(0, Fault(EFatCache_DiscardingDirtyData));
   102         }
   103 
   104     return KAllowInvalidateDirtyCache;        
   105     }
   106 
   107 //-----------------------------------------------------------------------------
   108 
   109 /**
   110     Read portion of raw data from 1st FAT copy.
   111     
   112     @param  aPos   media position in the _FIRST_ FAT to start reading with
   113     @param  aLen   number of bytes to read
   114     @param  aData  data descriptor
   115 
   116     @return standard error code.
   117 */
   118 TInt CFatCacheBase::ReadFatData(TUint32 aPos, TUint32 aLen, TDes8& aData) const
   119     {
   120     //__PRINT2(_L("#-CFatCacheNew::ReadFatData() pos:%u, Len:%d"), aPos, aLen);
   121 
   122     //-- this method can pick up data corresponding to invalid FAT entries, like FAT[0], FAT[1] and
   123     //-- the last portion beyond FAT because of read granularity. This isn't a problem, because the data there
   124     //-- won't be written on disk.
   125     ASSERT(aPos >= FatStartPos()); 
   126 
   127     return ipDrive->ReadNonCritical(aPos, aLen, aData);
   128     }
   129 
   130 //-----------------------------------------------------------------------------
   131 
   132 /** 
   133     Writes data to the FAT table, which number is set in iCurrentFatNo member variable. 
   134     @param  aPos   data media position in the _FIRST_ FAT copy
   135     @param  aData  data descriptor
   136     @return standard error code.
   137 */
   138 TInt CFatCacheBase::WriteFatData(TUint32 aPos, const TDesC8& aData) const
   139     {
   140     //__PRINT3(_L("#-CFatCacheBase::WriteFatData() pos:%u, Len:%d, FAT:%d"), aPos, aData.Length(), iCurrentFatNo);
   141 
   142 #ifdef _DEBUG    
   143     //-- 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
   144     //-- calculate correct data position in FAT
   145     TInt reserved_Entries_Offset=0;
   146     switch(iFatType)
   147         {
   148         case EFat32: reserved_Entries_Offset = KFatFirstSearchCluster*sizeof(TFat32Entry); break;  //-- FAT32  
   149         case EFat16: reserved_Entries_Offset = KFatFirstSearchCluster*sizeof(TFat16Entry); break;  //-- FAT16  
   150         case EFat12: reserved_Entries_Offset = 3;   break;                                         //-- FAT12
   151         default: ASSERT(0); break;
   152         }
   153     ASSERT(aPos >= FatStartPos()+reserved_Entries_Offset);
   154     ASSERT((aPos+aData.Length()) <= FatStartPos()+FatSize());
   155     ASSERT(iCurrentFatNo < iNumFATs);
   156 #endif    
   157 
   158     //-- goto the required FAT copy. iCurrentFatNo shall contain FAT number we are writing to.
   159     aPos+=iCurrentFatNo*FatSize(); 
   160 
   161     return ipDrive->WriteCritical(aPos, aData);
   162     }
   163 
   164 //-----------------------------------------------------------------------------
   165 /** 
   166     get a pointer to the CFatBitCache interface. 
   167     @return NULL because it is not present here 
   168 */
   169 CFatBitCache* CFatCacheBase::BitCacheInterface() 
   170     {
   171     return NULL;
   172     }
   173     
   174 
   175 //#################################################################################################################################
   176 //  CFatPagedCacheBase implementation
   177 //  Base class for all paged FAT caches
   178 //#################################################################################################################################
   179 
   180 CFatPagedCacheBase::CFatPagedCacheBase()
   181                :CFatCacheBase() 
   182     {
   183     }
   184 
   185 
   186 //#################################################################################################################################
   187 //  CFatCachePageBase implementation
   188 //  Base class for FAT cache pages (FAT16 fixed and FAT32 LRU)
   189 //#################################################################################################################################
   190 
   191 CFatCachePageBase::CFatCachePageBase(CFatPagedCacheBase& aCache)
   192                   :iCache(aCache)
   193     {
   194     ASSERT(IsPowerOf2(aCache.PageSize()));
   195     iStartIndexInFAT = KMaxTUint;
   196 
   197     //-- calculate number of FAT entries in the page, it depends on FAT type
   198     switch(aCache.FatType())
   199         {
   200         case EFat32:
   201             iFatEntriesInPage = PageSize() >> KFat32EntrySzLog2;
   202         break;
   203     
   204         case EFat16:
   205             iFatEntriesInPage = PageSize() >> KFat16EntrySzLog2;
   206         break;
   207     
   208         default:
   209             ASSERT(0);
   210             Fault(EFatCache_BadFatType);
   211         break;
   212 
   213         };
   214 
   215     SetState(EInvalid); 
   216     }
   217 
   218 CFatCachePageBase::~CFatCachePageBase()
   219     {
   220     iData.Close();
   221     }
   222 
   223 //-----------------------------------------------------------------------------
   224 /**
   225     Mark the page as "invalid". I.e containing inalid data.
   226     On the first read/write access to such page it will be re-read from the media
   227 
   228     @param aIgnoreDirtyData if ETrue, it is allowed to ignore the fact that the page contains dirty (not flushed) data.
   229 */
   230 void CFatCachePageBase::Invalidate(TBool aIgnoreDirtyData /*= EFalse*/)
   231     {
   232     if(!aIgnoreDirtyData && IsDirty())
   233         {
   234         __PRINT1(_L("#-CFatCachePageBase::Invalidate() dirty page! FAT idx:%d"), iStartIndexInFAT);
   235         __ASSERT_ALWAYS(0, Fault(EFatCache_DiscardingDirtyData));
   236         }
   237 
   238     iDirtySectors.Clear(); //-- clear dirty sectors bitmap
   239     SetState(EInvalid);
   240     }
   241 
   242 //-----------------------------------------------------------------------------
   243 /**
   244     Flush all dirty page sectors to the media and mark the page as "clean" if required.
   245     If the page is "clean" i.e doesn't contain changed data, does nothing.
   246 
   247     @param  aKeepDirty  if ETrue, the "dirty" flag isn't reset after page flushing.
   248 */
   249 void CFatCachePageBase::FlushL(TBool aKeepDirty)
   250     {
   251     if(!IsDirty())
   252         return;
   253 
   254     if(!IsValid())
   255         {
   256         __PRINT1(_L("#-CFatCachePageBase::FlushL() Invalid page! FAT idx:%d"), iStartIndexInFAT);
   257         ASSERT(0);
   258         User::Leave(KErrCorrupt);
   259         return;
   260         }
   261 
   262     //__PRINT1(_L("#-CFatCachePageBase::FlushL() FAT idx:%d"), iStartIndexInFAT);
   263 
   264     //-- write dirty FAT sectors  to the media one by one. 
   265     //-- merging adjacent dirty subsectors into larger clusters and writing them at once looks like a good idea, but
   266     //-- in reality it showed FAT performance degradation, at least on MMC/SD media.
   267     
   268     const TInt MaxSectors = iCache.SectorsInPage();
   269     
   270     for(TInt i=0; i<MaxSectors; ++i)
   271         {
   272         if(iDirtySectors[i])
   273             {
   274             DoWriteSectorL(i);
   275             }
   276         }
   277 
   278     //-- All data flushed; mark page as clean if it isn't required not to do.
   279     if(!aKeepDirty)
   280         SetClean(); 
   281 
   282     }
   283 
   284 
   285 //#################################################################################################################################
   286 //  CFat16FixedCache implementation
   287 //  Fixed cache (caches all FAT16) but organised as an array of pages
   288 //#################################################################################################################################
   289 
   290 CFat16FixedCache::CFat16FixedCache()
   291                  :CFatPagedCacheBase(),iPages(1) //-- array granularity is 1
   292     {
   293     }
   294 
   295 //-----------------------------------------------------------------------------
   296 /**
   297     FAT16 fixed cache factory function.
   298     @param  aOwner              pointer to the owning FAT mount
   299     @param  aFatSize            size of the FAT table in bytes
   300     @param  aRdGranularityLog2  Log2(read granularity)
   301     @param  aWrGranularityLog2  Log2(write granularity)
   302 
   303     @return pointer to the constructed object.
   304 */
   305 CFat16FixedCache* CFat16FixedCache::NewL(CFatMountCB* aOwner, TUint32 aFatSize, TUint32 aRdGranularityLog2, TUint32 aWrGranularityLog2)
   306     {
   307     __PRINT(_L("#-CFat16FixedCache::NewL()"));
   308 
   309     CFat16FixedCache* pSelf = NULL;
   310     pSelf = new (ELeave) CFat16FixedCache;
   311 
   312     CleanupStack::PushL(pSelf);
   313     pSelf->InitialiseL(aOwner, aFatSize, aRdGranularityLog2, aWrGranularityLog2);
   314     CleanupStack::Pop();
   315     
   316     return pSelf;
   317     }
   318 
   319 //-----------------------------------------------------------------------------
   320 /**
   321     FAT16 fixed cache initialisation.
   322     @param  aOwner              pointer to the owning FAT mount
   323     @param  aFatSize            size of the FAT table in bytes
   324     @param  aRdGranularityLog2  Log2(read granularity)
   325     @param  aWrGranularityLog2  Log2(write granularity)
   326 */
   327 void CFat16FixedCache::InitialiseL(CFatMountCB* aOwner, TUint32 aFatSize, TUint32 aRdGranularityLog2, TUint32 aWrGranularityLog2)
   328     {
   329     const TUint32 ReadGranularity = Pow2(aRdGranularityLog2);
   330     const TUint32 WriteGranularity = Pow2(aWrGranularityLog2);
   331 
   332     __PRINT3(_L("#-CFat16FixedCache::InitialiseL FatSz:%u, RdGr:%d, WrGr:%d"),aFatSize, ReadGranularity, WriteGranularity);
   333     (void)ReadGranularity;
   334     (void)WriteGranularity;
   335 
   336     TBool bParamsValid = (aRdGranularityLog2 >= aWrGranularityLog2) && (aWrGranularityLog2 >= KDefSectorSzLog2);
   337     __ASSERT_ALWAYS(bParamsValid, Fault(EFatCache_BadGranularity));
   338 
   339     CFatPagedCacheBase::InitialiseL(aOwner);
   340     
   341     ASSERT(FatType() == EFat16);
   342 
   343     //-- See FAT specs, and round up the limit to the FAT sector boundary
   344     const TUint32 KMaxFat16Size = ((65524*sizeof(TFat16Entry)+FAT_SectorSz()-1) >> FAT_SectorSzLog2()) << FAT_SectorSzLog2(); 
   345     const TUint32 KMinFat16Size = 4086*sizeof(TFat16Entry);  //-- See FAT specs
   346     
   347     bParamsValid = aFatSize >= KMinFat16Size && aFatSize <= KMaxFat16Size;
   348     __ASSERT_ALWAYS(bParamsValid, User::Leave(KErrCorrupt));
   349 
   350     //-- cache page size is (2^aRdGranularityLog2) bytes and consists of 2^(aRdGranularityLog2-aWrGranularity) sectors.
   351     iPageSizeLog2 = aRdGranularityLog2;
   352     iSectorSizeLog2 = aWrGranularityLog2; //-- Log2(number of sectors in cache page)
   353     
   354     __ASSERT_ALWAYS(SectorsInPage() < KMaxSectorsInPage, Fault(EFatCache_BadGranularity));
   355 
   356     const TUint numPages = (aFatSize+(PageSize()-1)) >> iPageSizeLog2;
   357     __PRINT1(_L("#-CFat16FixedCache Num Pages:%d"), numPages);
   358 
   359     //-- prepare pointer array for pages. NULL entry in the array means that the page at this index isn't allocated.
   360     for(TUint i=0; i<numPages; ++i)
   361         iPages.Append(NULL);
   362     
   363     }
   364 
   365 
   366 //-----------------------------------------------------------------------------
   367 /**
   368     Close the cache and deallocate its memory.
   369     @param  aDiscardDirtyData if ETrue, will ignore dirty data. If EFalse, will panic on atempt to close dirty cache.  
   370 */
   371 void CFat16FixedCache::Close(TBool aDiscardDirtyData)
   372     {
   373     __PRINT1(_L("#-CFat16FixedCache::Close(%d)"), aDiscardDirtyData);
   374 
   375     TInt cnt = iPages.Count();
   376     while(cnt--)
   377         {//-- delete pages
   378         CFat16FixedCachePage *pPage = iPages[cnt];
   379         if(pPage && (pPage->IsDirty()))
   380             {//-- trying to destroy the cache that has dirty pages
   381             __PRINT1(_L("#-CFat16FixedCache::Close() The page is dirty! Start idx:%d"), pPage->StartFatIndex());
   382             if(!aDiscardDirtyData)
   383                 {
   384                 __ASSERT_ALWAYS(0, Fault(EFatCache_DiscardingDirtyData));
   385                 }
   386             //-- ignore this fact if requested.
   387             }
   388         
   389         delete pPage;
   390         }
   391 
   392     iPages.Close();
   393     SetDirty(EFalse);
   394     }
   395 
   396 //-----------------------------------------------------------------------------
   397 /**
   398     Read FAT entry from the cache. 
   399 
   400     @param  aIndex FAT entry index to read
   401     @return FAT entry value at the index "aIndex" 
   402 */
   403 TUint32 CFat16FixedCache::ReadEntryL(TUint32 aIndex)
   404     {
   405     //__PRINT1(_L("#-CFat16FixedCache::ReadEntryL() FAT idx:%d"), aIndex);
   406     ASSERT(aIndex >= KFatFirstSearchCluster &&  aIndex < (FatSize() >> KFat16EntrySzLog2));
   407 
   408     //-- calculate page index in the array
   409     const TInt pgIdx = aIndex >> (PageSizeLog2()-KFat16EntrySzLog2);
   410     CFat16FixedCachePage *pPage = iPages[pgIdx];
   411     
   412     TUint32 entry = KMaxTUint;
   413 
   414     if(!pPage)
   415         {//-- page at this position isn't allocated yet
   416         pPage = CFat16FixedCachePage::NewL(*this);
   417         iPages[pgIdx] = pPage;
   418         
   419         //-- read the page from media
   420         entry = pPage->ReadFromMediaL(aIndex);
   421         }
   422     else
   423         {//-- get cached entry from the page
   424         TBool bRes = pPage->ReadCachedEntryL(aIndex, entry);
   425         ASSERT(bRes);
   426         (void)bRes;
   427         }
   428 
   429     return entry;
   430     }
   431 
   432 //-----------------------------------------------------------------------------
   433 /**
   434     Write FAT entry to the cache. 
   435     Appropriate FAT cache sector will be marked as "dirty" and will be eventually flushed to the media.
   436 
   437     @param  aIndex FAT entry index
   438     @param  aEntry FAT entry value
   439 */
   440 void CFat16FixedCache::WriteEntryL(TUint32 aIndex, TUint32 aEntry)
   441     {
   442     //__PRINT2(_L("#-CFat16FixedCache::WriteEntryL() FAT idx:%d, val:%d"), aIndex, aEntry);
   443 
   444     ASSERT(aIndex >= KFatFirstSearchCluster &&  aIndex < (FatSize() >> KFat16EntrySzLog2));
   445 
   446     SetDirty(ETrue);
   447 
   448     //-- calculate page index in the array
   449     const TInt pgIdx = aIndex >> (PageSizeLog2()-KFat16EntrySzLog2);
   450     CFat16FixedCachePage *pPage = iPages[pgIdx];
   451 
   452     if(!pPage)
   453         {//-- page at this position isn't allocated yet
   454         pPage = CFat16FixedCachePage::NewL(*this);
   455         iPages[pgIdx] = pPage;
   456         
   457         //-- read the page from media
   458         pPage->ReadFromMediaL(aIndex);
   459         }
   460 
   461     //-- overwrite entry in cache
   462     TBool bRes = pPage->WriteCachedEntryL(aIndex, aEntry);
   463     ASSERT(bRes);
   464     (void)bRes;
   465     }
   466 
   467 /**
   468     A debug method that asserts that the cache is really clean
   469 */
   470 void CFat16FixedCache::AssertCacheReallyClean() const
   471     {
   472 #ifdef _DEBUG 
   473         for(TUint i=0; i<NumPages(); ++i)
   474         {
   475             CFat16FixedCachePage* pPage = iPages[i];
   476             if(pPage && pPage->IsDirty())
   477                 {
   478                 __PRINT(_L("#-CFat16FixedCache::AssertCacheReallyClean()"));
   479                 ASSERT(0);
   480                 }
   481         }
   482 #endif   
   483     }
   484 
   485 
   486 //-----------------------------------------------------------------------------
   487 /**
   488     Flushes all dirty data to the media.
   489 */
   490 void CFat16FixedCache::FlushL()
   491     {
   492     if(!IsDirty())
   493         {
   494         AssertCacheReallyClean();
   495         return;
   496         }
   497 
   498 
   499     //-- flush dirty data to all copies of FAT
   500     for(iCurrentFatNo=0; iCurrentFatNo < NumFATs(); ++iCurrentFatNo)
   501         {
   502         const TInt nPages = NumPages();
   503         for(TInt i=0; i<nPages; ++i)
   504             {
   505             const TBool keepDirty = iCurrentFatNo < (NumFATs() - 1);
   506 
   507             CFat16FixedCachePage* pPage = iPages[i];
   508             if(pPage)
   509                 pPage->FlushL(keepDirty);
   510             }
   511        
   512         }
   513    
   514     iCurrentFatNo = KInvalidFatNo;
   515     SetDirty(EFalse);
   516     }
   517 
   518 //-----------------------------------------------------------------------------
   519 /**
   520     Invalidate whole cache. All pages will be marked as invalid and will be re-read from the media on first access to them.
   521     @return always KErrNone
   522 */
   523 TInt CFat16FixedCache::Invalidate()
   524     {
   525     __PRINT(_L("#-CFat16FixedCache::Invalidate()"));
   526     const TBool bIgnoreDirtyData = CheckInvalidatingDirtyCache();
   527 
   528     //-- iterate through the array of pages marking invalidating every page
   529     TInt cnt = iPages.Count();
   530     while(cnt--)
   531         {//-- delete pages
   532         CFat16FixedCachePage *pPage = iPages[cnt];
   533         if(pPage)
   534             pPage->Invalidate(bIgnoreDirtyData);
   535         }
   536 
   537 
   538     SetDirty(EFalse);
   539 
   540     return KErrNone;
   541     }
   542 
   543 //-----------------------------------------------------------------------------
   544 /**
   545     Invalidate FAT cache pages that contain FAT entries from aStartIndex to (aStartIndex+aNumEntries)
   546     These pages will be marked as invalid and will be re-read from the media on first access to them.
   547     
   548     @param  aStartIndex FAT start index of the region being invalidated
   549     @param  aNumEntries number of entries to invalidate
   550     @return always KErrNone
   551 */
   552 TInt CFat16FixedCache::InvalidateRegion(TUint32 aStartIndex, TUint32 aNumEntries)
   553     {
   554     __PRINT2(_L("#-CFat16FixedCache::InvalidateRegion() startIndex:%d, entries:%d"),aStartIndex, aNumEntries);
   555     ASSERT(aStartIndex >= KFatFirstSearchCluster &&  aStartIndex < (FatSize() >> KFat16EntrySzLog2));
   556 
   557     if(!aNumEntries)
   558         {
   559         ASSERT(0);
   560         return KErrNone;
   561         }
   562 
   563     const TBool bIgnoreDirtyData = CheckInvalidatingDirtyCache();
   564     const TUint startPgIdx  = aStartIndex >> (PageSizeLog2()-KFat16EntrySzLog2);
   565     const TUint nPagesToInv = 1+(aNumEntries >> (PageSizeLog2()-KFat16EntrySzLog2));
   566 
   567     TUint i;
   568     //-- invalidate pages that contain [aStartIndex ... aStartIndex+aNumEntries] entries
   569     for(i=0; i<nPagesToInv; ++i)
   570         {
   571         const TUint pageIdx = i+startPgIdx;
   572         if(pageIdx >= NumPages())
   573             break;
   574         
   575         CFat16FixedCachePage* pPage = iPages[pageIdx];
   576         if(pPage)
   577             pPage->Invalidate(bIgnoreDirtyData);
   578         } 
   579 
   580     SetDirty(EFalse);
   581    
   582     //-- check if the cache still has dirty pages
   583     for(i=0; i<NumPages(); ++i)
   584         {
   585         CFat16FixedCachePage* pPage = iPages[i];
   586         if(pPage && pPage->IsDirty()) 
   587             {
   588             SetDirty(ETrue);
   589             break;
   590             }      
   591         }
   592 
   593     return KErrNone;
   594     }
   595 
   596 //#################################################################################################################################
   597 //  CFat16FixedCachePage implementation
   598 //  Page for the FAT16 fixed cache
   599 //#################################################################################################################################
   600 
   601 //-----------------------------------------------------------------------------
   602 
   603 CFat16FixedCachePage::CFat16FixedCachePage(CFatPagedCacheBase& aCache)
   604                      :CFatCachePageBase(aCache)
   605     {
   606     ASSERT(IsPowerOf2(EntriesInPage()));
   607     }
   608 
   609 
   610 /**
   611     Factory function.
   612     @param aCache reference to the owning cache.
   613     @return pointer to the constructed object or NULL on error
   614 */
   615 CFat16FixedCachePage* CFat16FixedCachePage::NewL(CFatPagedCacheBase& aCache)
   616     {
   617     CFat16FixedCachePage* pSelf = NULL;
   618     pSelf = new (ELeave) CFat16FixedCachePage(aCache);
   619 
   620     CleanupStack::PushL(pSelf);
   621     
   622     pSelf->iData.CreateMaxL(aCache.PageSize()); //-- allocate memory for the page
   623    
   624     CleanupStack::Pop();
   625 
   626     return pSelf;
   627     }
   628 
   629 
   630 //-----------------------------------------------------------------------------
   631 /**
   632     Read FAT16 entry from the cache. 
   633     
   634     1. If page's data are valid, just extracts data from the page buffer.
   635     2. If page's data are invalid firstly reads data from the media and goto 1
   636     
   637     @param  aFatIndex entry's absolute FAT index (from the FAT start)
   638     @param  aResult on sucess there will be FAT16 entry value
   639     @return ETrue, because FAT16 cache pages never get eviched.
   640 */
   641 TBool CFat16FixedCachePage::ReadCachedEntryL (TUint32 aFatIndex, TUint32& aResult)
   642     {
   643     if(IsValid())
   644         {//-- read entry directly from page buffer, the cached data are valid
   645         aResult = (*GetEntryPtr(aFatIndex)) & KFat16EntryMask;
   646         }
   647     else
   648         {//-- aFatIndex belongs to this page, but the page is invalid and needs to be read from the media
   649         //__PRINT(_L("#-CFat16FixedCachePage::ReadCachedEntry() The page is invalid, reading from the media"));
   650         aResult = ReadFromMediaL(aFatIndex);
   651         }
   652 
   653     return ETrue;
   654     }
   655 
   656 //-----------------------------------------------------------------------------
   657 
   658 /**
   659     Writes FAT cache page sector to the media (to all copies of the FAT)
   660     @param  aSector sector number winthin this page
   661 */
   662 void CFat16FixedCachePage::DoWriteSectorL(TUint32 aSector)
   663     {
   664     //__PRINT1(_L("#-CFat16FixedCachePage::DoWriteSectorL() startSec:%d, cnt:%d"), aSector);
   665 
   666     ASSERT(aSector < iCache.SectorsInPage());
   667 
   668     TInt offset = 0;
   669 
   670     if(iStartIndexInFAT == 0 && aSector == 0)
   671         {//-- this is the very beginning of FAT16. We must skip FAT[0] & FAT[1] entries and do not write them to media.    
   672         offset = KFatFirstSearchCluster << KFat16EntrySzLog2; 
   673         }    
   674     
   675     const TUint8* pData = iData.Ptr()+offset+(aSector << iCache.SectorSizeLog2());
   676     
   677     TUint32 dataLen = (1 << iCache.SectorSizeLog2()) - offset;
   678 
   679     const TUint32 mediaPosStart = iCache.FatStartPos() + (iStartIndexInFAT << KFat16EntrySzLog2) + (aSector << iCache.SectorSizeLog2()) + offset; 
   680     const TUint32 mediaPosEnd = mediaPosStart + dataLen; 
   681 
   682     //-- check if we are going to write beyond FAT. It can happen if the write granularity is bigger that the sector size.
   683     const TUint32 posFatEnd = iCache.FatStartPos() + iCache.FatSize();
   684     if(mediaPosEnd > posFatEnd)
   685         {//-- correct the leength of the data to write.
   686         dataLen -= (mediaPosEnd-posFatEnd);
   687         }
   688 
   689     TPtrC8 ptrData(pData, dataLen); //-- source data descriptor 
   690 
   691     TInt nRes = iCache.WriteFatData(mediaPosStart, ptrData);
   692     
   693     if(nRes != KErrNone)
   694         {
   695         __PRINT1(_L("#-CFat16FixedCachePage::DoWriteSectorsL() failed! code:%d"), nRes);
   696         User::Leave(nRes);
   697         }
   698 
   699     }
   700 
   701 //-----------------------------------------------------------------------------
   702 /**
   703     Write FAT16 entry at aFatIndex to the cache. Note that the data are not written to the media, only to the cache page.
   704     Corresponding page sector is marked as dirty and will be flushed on FlushL() call later.
   705 
   706     1. If page's data are valid, copies data to the page buffer and marks sector as dirty.
   707     2. If page's data are invalid, firstly reads data from the media and goto 1
   708 
   709     @param  aFatIndex entry's absolute FAT index (from the FAT start)
   710     @param  aFatEntry FAT16 entry value
   711     @return ETrue because FAT16 cache pages never get eviched.
   712 */
   713 TBool CFat16FixedCachePage::WriteCachedEntryL(TUint32 aFatIndex, TUint32 aFatEntry)
   714     {
   715     
   716     ASSERT(IsEntryCached(aFatIndex));
   717 
   718     if(!IsValid())
   719         {//-- we are trying to write data to the page that has invalid data. //-- read the data from the media first.
   720         ReadFromMediaL(aFatIndex);
   721         }
   722 
   723     TFat16Entry* pEntry = GetEntryPtr(aFatIndex);
   724     
   725     const TFat16Entry orgEntry = *pEntry;
   726     *pEntry = (TFat16Entry)((orgEntry & ~KFat16EntryMask) | (aFatEntry & KFat16EntryMask));
   727     
   728     //-- mark corresponding sector of the cache page as dirty
   729     const TUint entryIndexInPage = aFatIndex & (EntriesInPage()-1); //-- number of entries in page is always a power of 2
   730     const TUint dirtySectorNum   = entryIndexInPage >> (iCache.SectorSizeLog2() - KFat16EntrySzLog2);
   731 
   732     ASSERT(dirtySectorNum < iCache.SectorsInPage());
   733 
   734     iDirtySectors.SetBit(dirtySectorNum);
   735     SetState(EDirty); //-- mark page as dirty.
   736 
   737     return ETrue;
   738     }
   739 
   740 //-----------------------------------------------------------------------------
   741 
   742 /**
   743     Get a pointer to the FAT16 entry in the page buffer.
   744     The page 's data shall be valid and the entry shall belong to this page.
   745     
   746     @param aFatIndex absolute FAT index (from the FAT start) of the entry
   747     @return pointer to the FAT16 entry in the page buffer.
   748 */
   749 TFat16Entry* CFat16FixedCachePage::GetEntryPtr(TUint32 aFatIndex) const
   750     {
   751     ASSERT(IsValid() && IsEntryCached(aFatIndex));
   752 
   753     const TUint KEntryIndexInPage = aFatIndex & (EntriesInPage()-1); //-- number of entries in page is always a power of 2
   754     TFat16Entry* pEntry = ((TFat16Entry*)iData.Ptr()) + KEntryIndexInPage;
   755 
   756     return  pEntry;
   757     }
   758 
   759 //-----------------------------------------------------------------------------
   760 /**
   761     Read the FAT16 cache page from the media and returns required FAT16 entry.    
   762 
   763     @param  aFatIndex entry's absolute FAT index (from the FAT start)
   764     @return entry value at aFatIndex.
   765 */
   766 TUint32 CFat16FixedCachePage::ReadFromMediaL(TUint32 aFatIndex)
   767     {
   768     //__PRINT1(_L("#-CFat16FixedCachePage::ReadFromMediaL() FAT idx:%d"), aFatIndex);
   769     const TUint KFat16EntriesInPageLog2 = iCache.PageSizeLog2()-KFat16EntrySzLog2; //-- number of FAT16 entries in page is always a power of 2
   770 
   771     //-- find out index in FAT this page starts from
   772     iStartIndexInFAT = (aFatIndex >> KFat16EntriesInPageLog2) << KFat16EntriesInPageLog2;
   773     SetState(EInvalid); //-- mark the page as invalid just in case if the read fails.
   774 
   775     //-- read page from the media
   776     const TUint32 pageStartPos = iCache.FatStartPos() + (iStartIndexInFAT << KFat16EntrySzLog2);
   777     
   778     TInt nRes = iCache.ReadFatData(pageStartPos, iCache.PageSize(), iData);
   779     if(nRes != KErrNone)
   780         {
   781         __PRINT1(_L("#-CFat16FixedCachePage::ReadFromMediaL() failed! code:%d"), nRes);
   782         User::Leave(nRes);
   783         }
   784 
   785     SetClean(); //-- mark this page as clean
   786 
   787     const TFat16Entry entry = (TFat16Entry)((*GetEntryPtr(aFatIndex)) & KFat16EntryMask);
   788 
   789     return entry;
   790     }
   791 
   792 
   793 //-----------------------------------------------------------------------------
   794 
   795 //#################################################################################################################################
   796 //  CFat12Cache implementation
   797 //  FAT12 non-paged fixed cache. This cache consists from only 1 page, logically divided up to 32 sectors (write granularity unit)
   798 //#################################################################################################################################
   799 
   800 CFat12Cache::CFat12Cache()
   801             :CFatCacheBase()
   802     {
   803     }
   804 
   805 //-----------------------------------------------------------------------------
   806 /**
   807     FAT12 fixed cache factory function.
   808     @param  aOwner              pointer to the owning FAT mount
   809     @param  aFatSize            size of the FAT table in bytes
   810 
   811     @return pointer to the constructed object.
   812 */
   813 CFat12Cache* CFat12Cache::NewL(CFatMountCB* aOwner, TUint32 aFatSize)
   814     {
   815     __PRINT(_L("#-CFat12Cache::NewL()"));
   816     CFat12Cache* pSelf = NULL;
   817     pSelf = new (ELeave) CFat12Cache;
   818 
   819     CleanupStack::PushL(pSelf);
   820     pSelf->InitialiseL(aOwner, aFatSize);
   821     CleanupStack::Pop();
   822     
   823     return pSelf;
   824     }
   825 
   826 //-----------------------------------------------------------------------------
   827 /**
   828     FAT16 fixed cache initialisation.
   829     @param  aOwner              pointer to the owning FAT mount
   830     @param  aFatSize            size of the FAT table in bytes
   831 */
   832 void CFat12Cache::InitialiseL(CFatMountCB* aOwner, TUint32 aFatSize)
   833     {
   834     __PRINT1(_L("#-CFat12Cache::InitialiseL FatSz:%u"),aFatSize);
   835 
   836     CFatCacheBase::InitialiseL(aOwner);
   837     ASSERT(FatType() == EFat12);
   838 
   839     //-- 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
   840     const TUint32 KMaxFat12Size = ( ((TUint32)(4084*1.5+FAT_SectorSz()-1)) >> FAT_SectorSzLog2()) << FAT_SectorSzLog2();
   841     const TUint32 KMinFat12Size = FAT_SectorSz();  //-- 1 FAT sector
   842     __ASSERT_ALWAYS(aFatSize >= KMinFat12Size && aFatSize <= KMaxFat12Size, User::Leave(KErrCorrupt));
   843     (void)KMaxFat12Size;
   844     (void)KMinFat12Size;
   845 
   846     //-- as soon as FAT12 max size is 4084 entries or 6126 bytes, the cache is contiguous and divided 
   847     //-- to logical sectors (write granularity). 
   848 
   849     //-- calculate number write cache sector in the cache
   850     iSectorsInCache = (aFatSize + (FAT_SectorSz()-1)) >> FAT_SectorSzLog2();
   851     __ASSERT_ALWAYS(NumSectors() <= KMaxSectorsInCache, Fault(EFatCache_BadGranularity));
   852 
   853     //-- round up cache size to write granularity (sector size)
   854     const TUint32 cacheSize = NumSectors() << FAT_SectorSzLog2();
   855     
   856     //-- create buffer for the whole FAT12
   857     iData.CreateMaxL(cacheSize);
   858 
   859     //-- this will read whole FAT into the cache 
   860     User::LeaveIfError(Invalidate()); 
   861     }
   862 
   863 //-----------------------------------------------------------------------------
   864 /**
   865     Close the cache and deallocate its memory.
   866     @param  aDiscardDirtyData if ETrue, will ignore dirty data. If EFalse, will panic on atempt to close dirty cache.  
   867 */
   868 void CFat12Cache::Close(TBool aDiscardDirtyData)
   869     {
   870     __PRINT1(_L("#-CFat12Cache::Close(%d)"), aDiscardDirtyData);    
   871     
   872     for(TUint32 i=0; i<NumSectors(); ++i)
   873         {
   874         if(iDirtySectors[i])
   875             {//-- trying to destroy the cache that has dirty sectors
   876             __PRINT1(_L("#-CFat12Cache::Close() The cache is dirty! cache sector:%d"), i);
   877             if(!aDiscardDirtyData)
   878                 {
   879                 __ASSERT_ALWAYS(0, Fault(EFatCache_DiscardingDirtyData));
   880                 }
   881             //-- ignore this fact if requested.
   882             }
   883         }
   884 
   885     iData.Close();
   886     SetDirty(EFalse);
   887     }
   888 
   889 //-----------------------------------------------------------------------------
   890 /**
   891     Read FAT entry from the cache. 
   892 
   893     @param  aIndex FAT entry index to read
   894     @return FAT entry value at the index "aIndex" 
   895 */
   896 TUint32 CFat12Cache::ReadEntryL(TUint32 aIndex)
   897     {
   898     //__PRINT1(_L("#-CFat12Cache::ReadEntryL() FAT idx:%d"), aIndex);
   899     ASSERT(aIndex >= KFatFirstSearchCluster &&  aIndex <  (FatSize() + FatSize()/2)); //-- FAT12 entry is 1.5 bytes long
   900 
   901     TUint32 entry;
   902 
   903     if(aIndex & 0x01)
   904         {//-- odd index
   905         --aIndex;
   906         const TUint32 byteIdx = 1 + aIndex + (aIndex >> 1); //-- byteIdx = 1+(aIndex-1)*1.5
   907         Mem::Copy(&entry, iData.Ptr()+byteIdx, 2); 
   908         entry >>= 4;   
   909         }
   910     else
   911         {//-- even index
   912         const TUint32 byteIdx = aIndex + (aIndex >> 1); //-- byteIdx = aIndex*1.5
   913         Mem::Copy(&entry, iData.Ptr()+byteIdx, 2);
   914         }
   915 
   916     entry &= KFat12EntryMask; 
   917 
   918     return entry;
   919     }
   920 
   921 //-----------------------------------------------------------------------------
   922 /**
   923     Write FAT entry to the cache. 
   924     Appropriate FAT cache sector will be marked as "dirty" and will be eventually flushed to the media.
   925 
   926     @param  aIndex FAT entry index
   927     @param  aEntry FAT entry value
   928 */
   929 void CFat12Cache::WriteEntryL(TUint32 aIndex, TUint32 aEntry)
   930     {
   931     //__PRINT2(_L("#-CFat12Cache::WriteEntryL() FAT idx:%d, entry:%u"), aIndex, aEntry);
   932     ASSERT(aIndex >= KFatFirstSearchCluster &&  aIndex <  (FatSize() + FatSize()/2)); //-- FAT12 entry is 1.5 bytes long
   933 
   934     aEntry &= KFat12EntryMask; 
   935  
   936     TUint32 byteIdx = 0;
   937     TUint8 tmp;
   938 
   939     if(aIndex & 0x01)
   940         {//-- odd index
   941         --aIndex;
   942         byteIdx = 1 + aIndex + (aIndex >> 1); //-- byteIdx = 1+(aIndex-1)*1.5
   943         tmp = (TUint8)(iData[byteIdx] & 0x0F); //-- we modifying a higher nibble 
   944         tmp |= (TUint8) ((aEntry & 0x0F)<<4);
   945         iData[byteIdx] = tmp;
   946 
   947         iData[byteIdx+1] = (TUint8)(aEntry >> 4);  
   948         }
   949     else
   950         {//-- even index
   951         byteIdx = aIndex + (aIndex >> 1); //-- byteIdx = aIndex*1.5
   952         iData[byteIdx] = (TUint8)aEntry;  
   953 
   954         const TUint32 nextIdx = byteIdx+1;
   955         tmp = (TUint8)(iData[nextIdx] & 0xF0); //-- we modifying a lower nibble 
   956         tmp |= (TUint8)((aEntry >> 8) & 0x0F);
   957         iData[nextIdx] = tmp;
   958 
   959         }
   960 
   961     //-- mark changed sectors dirty. We modified 2 bytes at [byteIdx] and [byteIdx+1]
   962     iDirtySectors.SetBit(byteIdx >> FAT_SectorSzLog2());
   963     iDirtySectors.SetBit((byteIdx+1) >> FAT_SectorSzLog2());
   964 
   965     SetDirty(ETrue);
   966     }
   967 
   968 //-----------------------------------------------------------------------------
   969 /**
   970     A debug method that asserts that the cache is really clean
   971 */
   972 void CFat12Cache::AssertCacheReallyClean() const
   973     {
   974 #ifdef _DEBUG 
   975     if(iDirtySectors.HasBitsSet())
   976         {
   977         __PRINT(_L("#-CFat12Cache::AssertCacheReallyClean()"));
   978         ASSERT(0);
   979         }
   980 
   981 #endif   
   982     }
   983 
   984 //-----------------------------------------------------------------------------
   985 /**
   986     Flushes all dirty data to the media.
   987     Walks through all sectors in this cache and flushes dirty ones.
   988 */
   989 void CFat12Cache::FlushL()
   990     {
   991     if(!IsDirty())
   992         {
   993         AssertCacheReallyClean();
   994         return;
   995         }
   996 
   997     //-- write all dirty sectors to the media (into all copies of FAT)
   998     for(iCurrentFatNo=0; iCurrentFatNo < NumFATs(); ++iCurrentFatNo)
   999         {
  1000         for(TUint secNo=0; secNo<NumSectors(); ++secNo)
  1001             {
  1002             if(iDirtySectors[secNo])
  1003                 {//-- this sector is dirty, write it to the media
  1004 
  1005                 TInt offset = 0;
  1006                 if(secNo == 0)
  1007                     {//-- this is a first sector in FAT. We must skip FAT[0] & FAT[1] entries and do not write them to the media.    
  1008                     offset = 3; //-- 2 FAT12 entries
  1009                     }
  1010 
  1011                 const TUint32 secPos = secNo << FAT_SectorSzLog2(); //-- relative sector position in FAT
  1012                 const TUint8* pData = iData.Ptr()+offset+secPos;    //-- pointer to the data in cache buffer
  1013                 const TUint32 len = FAT_SectorSz() - offset;          
  1014                 TPtrC8 ptrData(pData, len);                         //-- source data descriptor 
  1015                 const TUint32 mediaPos = FatStartPos() + secPos + offset;
  1016                 
  1017                 TInt nRes = WriteFatData(mediaPos, ptrData);
  1018 
  1019                 if(nRes != KErrNone)
  1020                     {
  1021                     __PRINT1(_L("#-CFat12Cache::FlushL() failed! code:%d"), nRes);
  1022                     User::Leave(nRes);
  1023                     }
  1024 
  1025                 }//if(iDirtySectors[secNo])
  1026             }
  1027 
  1028         }
  1029 
  1030     iCurrentFatNo = KInvalidFatNo;
  1031 
  1032     //-- mark the cache as clean
  1033     iDirtySectors.Clear();
  1034     SetDirty(EFalse);
  1035     
  1036     }
  1037 
  1038 //-----------------------------------------------------------------------------
  1039 /**
  1040     Invalidates whole cache. Because FAT12 is tiny, just re-reads data from the media to the cache
  1041     @return Media read result code.
  1042 */
  1043 TInt CFat12Cache::Invalidate()
  1044     {
  1045     __PRINT(_L("#-CFat12Cache::Invalidate()"));
  1046     CheckInvalidatingDirtyCache();
  1047     
  1048     //-- read whole cache from the media
  1049     const TUint32 posStart = FatStartPos();
  1050     const TUint32 len      = NumSectors() << FAT_SectorSzLog2();
  1051      
  1052     TInt nRes = ReadFatData(posStart, len, iData);
  1053     if(nRes != KErrNone)
  1054         return nRes;
  1055 
  1056     //-- mark the cache as clean
  1057     SetDirty(EFalse);
  1058     iDirtySectors.Clear();
  1059 
  1060     return KErrNone;
  1061     }
  1062 
  1063 //-----------------------------------------------------------------------------
  1064 /**
  1065     Invalidate wholes cache. Because FAT12 is tiny, just re-reads data from the media to the cache
  1066     @param  aStartIndex ignored
  1067     @param  aNumEntries ignored
  1068     @return Media read result code.
  1069 */
  1070 TInt CFat12Cache::InvalidateRegion(TUint32 aStartIndex, TUint32 aNumEntries)
  1071     {
  1072     __PRINT2(_L("#-CFat12Cache::InvalidateRegion() startIndex:%d, entries:%d"),aStartIndex, aNumEntries);
  1073     ASSERT(aStartIndex >= KFatFirstSearchCluster &&  aStartIndex <  (FatSize() + FatSize()/2)); //-- FAT12 entry is 1.5 bytes long
  1074     (void)aStartIndex;
  1075     (void)aNumEntries;
  1076 
  1077     //-- just re-read all FAT12, it is just 6K max and isn't worth calculating invalid sectors
  1078     return Invalidate();
  1079     }
  1080 
  1081 
  1082 
  1083 
  1084 
  1085 
  1086 
  1087 
  1088 
  1089 
  1090 
  1091 
  1092 
  1093 
  1094 
  1095 
  1096 
  1097 
  1098 
  1099 
  1100 
  1101 
  1102 
  1103 
  1104 
  1105 
  1106 
  1107 
  1108 
  1109 
  1110 
  1111 
  1112 
  1113 
  1114 
  1115 
  1116 
  1117 
  1118 
  1119 
  1120 
  1121 
  1122