Browse Source

Restructure cache classes to single class

Bill Greiman 4 years ago
parent
commit
f62283abb3

+ 6 - 6
examples/RtcTimestampTest/RtcTimestampTest.ino

@@ -150,17 +150,17 @@ bool setRtc() {
   Serial.print(F("Input: "));
   Serial.println(line);
 
-  y = strtol(line, &ptr, 0);
+  y = strtol(line, &ptr, 10);
   if (*ptr++ != '-' || y < 2000 || y > 2099) return error("year");
-  m = strtol(ptr, &ptr, 0);
+  m = strtol(ptr, &ptr, 10);
   if (*ptr++ != '-' || m < 1 || m > 12) return error("month");
-  d = strtol(ptr, &ptr, 0);
+  d = strtol(ptr, &ptr, 10);
   if (d < 1 || d > 31) return error("day");
-  hh = strtol(ptr, &ptr, 0);
+  hh = strtol(ptr, &ptr, 10);
   if (*ptr++ != ':' || hh > 23) return error("hour");
-  mm = strtol(ptr, &ptr, 0);
+  mm = strtol(ptr, &ptr, 10);
   if (*ptr++ != ':' || mm > 59) return error("minute");
-  ss = strtol(ptr, &ptr, 0);
+  ss = strtol(ptr, &ptr, 10);
   if (ss > 59) return error("second");
 
   rtc.adjust(DateTime(y, m, d, hh, mm, ss));

+ 1 - 1
library.properties

@@ -1,5 +1,5 @@
 name=SdFat
-version=2.0.2
+version=2.0.3
 license=MIT
 author=Bill Greiman <fat16lib@sbcglobal.net>
 maintainer=Bill Greiman <fat16lib@sbcglobal.net>

+ 2 - 11
src/ExFatLib/ExFatFile.cpp

@@ -647,16 +647,7 @@ int ExFatFile::read(void* buf, size_t count) {
         ns = maxNs;
       }
       n = ns << m_vol->bytesPerSectorShift();
-      // Check for cache sector in read range.
-      if (sector <= m_vol->dataCacheSector()
-          && m_vol->dataCacheSector() < (sector + ns)) {
-        // Flush cache if cache sector is in the range.
-        if (!m_vol->dataCacheSync()) {
-          DBG_FAIL_MACRO;
-          goto fail;
-        }
-      }
-      if (!m_vol->readSectors(sector, dst, ns)) {
+     if (!m_vol->cacheSafeRead(sector, dst, ns)) {
         DBG_FAIL_MACRO;
         goto fail;
       }
@@ -664,7 +655,7 @@ int ExFatFile::read(void* buf, size_t count) {
     } else {
       // read single sector
       n = m_vol->bytesPerSector();
-      if (!m_vol->readSector(sector, dst)) {
+      if (!m_vol->cacheSafeRead(sector, dst)) {
         DBG_FAIL_MACRO;
         goto fail;
       }

+ 0 - 1
src/ExFatLib/ExFatFile.h

@@ -82,7 +82,6 @@ struct ExFatPos_t {
   uint64_t position;
   /** cluster for position */
   uint32_t cluster;
-  ExFatPos_t() : position(0), cluster(0) {}
 };
 //------------------------------------------------------------------------------
 /**

+ 3 - 13
src/ExFatLib/ExFatFileWrite.cpp

@@ -747,24 +747,14 @@ size_t ExFatFile::write(const void* buf, size_t nbyte) {
         ns = maxNs;
       }
       n = ns << m_vol->bytesPerSectorShift();
-      // Check for cache sector in write range.
-      if (sector <= m_vol->dataCacheSector()
-          && m_vol->dataCacheSector() < (sector + ns)) {
-        // Invalidate cache if cache sector is in the range.
-        m_vol->dataCacheInvalidate();
-      }
-      if (!m_vol->writeSectors(sector, src, ns)) {
-        DBG_FAIL_MACRO;
+      if (!m_vol->cacheSafeWrite(sector, src, ns)) {
+         DBG_FAIL_MACRO;
         goto fail;
       }
 #endif  // USE_MULTI_SECTOR_IO
     } else {
-      // use single sector write command
       n = m_vol->bytesPerSector();
-      if (m_vol->dataCacheSector() == sector) {
-        m_vol->dataCacheInvalidate();
-      }
-      if (!m_vol->writeSector(sector, src)) {
+      if (!m_vol->cacheSafeWrite(sector, src)) {
         DBG_FAIL_MACRO;
         goto fail;
       }

+ 0 - 45
src/ExFatLib/ExFatPartition.cpp

@@ -27,51 +27,6 @@
 #include "ExFatVolume.h"
 #include "../common/FsStructs.h"
 //------------------------------------------------------------------------------
-uint8_t* FsCache::get(uint32_t sector, uint8_t option) {
-  if (!m_blockDev) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  if (m_sector != sector) {
-    if (!sync()) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    if (!(option & CACHE_OPTION_NO_READ)) {
-      if (!m_blockDev->readSector(sector, m_cacheBuffer)) {
-        DBG_FAIL_MACRO;
-        goto fail;
-      }
-    }
-    m_status = 0;
-    m_sector = sector;
-  }
-  m_status |= option & CACHE_STATUS_MASK;
-  return m_cacheBuffer;
-
- fail:
-  return nullptr;
-}
-//------------------------------------------------------------------------------
-void FsCache::invalidate() {
-  m_status = 0;
-  m_sector = 0XFFFFFFFF;
-}
-//------------------------------------------------------------------------------
-bool FsCache::sync() {
-  if (m_status & CACHE_STATUS_DIRTY) {
-    if (!m_blockDev->writeSector(m_sector, m_cacheBuffer)) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    m_status &= ~CACHE_STATUS_DIRTY;
-  }
-  return true;
-
- fail:
-  return false;
-}
-//==============================================================================
 // return 0 if error, 1 if no space, else start cluster.
 uint32_t ExFatPartition::bitmapFind(uint32_t cluster, uint32_t count) {
   uint32_t start = cluster ? cluster - 2 : m_bitmapStart;

+ 13 - 81
src/ExFatLib/ExFatPartition.h

@@ -30,86 +30,14 @@
  */
 #include "../common/SysCall.h"
 #include "../common/BlockDevice.h"
+#include "../common/FsCache.h"
 #include "ExFatConfig.h"
 #include "ExFatTypes.h"
 /** Type for exFAT partition */
 const uint8_t FAT_TYPE_EXFAT = 64;
 
 class ExFatFile;
-//==============================================================================
-/**
- * \class FsCache
- * \brief Sector cache.
- */
-class FsCache {
- public:
-  /** Cached sector is dirty */
-  static const uint8_t CACHE_STATUS_DIRTY = 1;
-  /** Cache sector status bits */
-  static const uint8_t CACHE_STATUS_MASK = CACHE_STATUS_DIRTY;
-  /** Sync existing sector but do not read new sector. */
-  static const uint8_t CACHE_OPTION_NO_READ = 2;
-  /** Cache sector for read. */
-  static const uint8_t CACHE_FOR_READ = 0;
-  /** Cache sector for write. */
-  static const uint8_t CACHE_FOR_WRITE = CACHE_STATUS_DIRTY;
-  /** Reserve cache sector for write - do not read from sector device. */
-  static const uint8_t CACHE_RESERVE_FOR_WRITE
-    = CACHE_STATUS_DIRTY | CACHE_OPTION_NO_READ;
 
-  FsCache() : m_blockDev(nullptr) {
-    invalidate();
-  }
-
-  /** \return Cache sector address. */
-  uint8_t* cacheBuffer() {
-    return m_cacheBuffer;
-  }
-  /** \return Clear the cache and returns a pointer to the cache. */
-  uint8_t* clear() {
-    if (isDirty() && !sync()) {
-      return nullptr;
-    }
-    invalidate();
-    return m_cacheBuffer;
-  }
-  /** Set current sector dirty. */
-  void dirty() {
-    m_status |= CACHE_STATUS_DIRTY;
-  }
-  /** Initialize the cache.
-   * \param[in] blockDev Block device for this partition.
-   */
-  void init(BlockDevice* blockDev) {
-    m_blockDev = blockDev;
-    invalidate();
-  }
-  /** Invalidate current cache sector. */
-  void invalidate();
-  /** \return dirty status */
-  bool isDirty() {
-    return m_status & CACHE_STATUS_DIRTY;
-  }
-  /** \return Logical sector number for cached sector. */
-  uint32_t sector() {
-    return m_sector;
-  }
-  /** Fill cache with sector data.
-   * \param[in] sector Sector to read.
-   * \param[in] option mode for cached sector.
-   * \return Address of cached sector. */
-  uint8_t* get(uint32_t sector, uint8_t option);
-  /** Write current sector if dirty.
-   * \return true for success or false for failure.
-   */
-  bool sync();
-
- private:
-  uint8_t m_status;
-  BlockDevice* m_blockDev;
-  uint32_t m_sector;
-  uint8_t m_cacheBuffer[512];
-};
 //==============================================================================
 /**
  * \class ExFatPartition
@@ -233,20 +161,24 @@ class ExFatPartition {
   bool syncDevice() {
     return m_blockDev->syncDevice();
   }
+  bool cacheSafeRead(uint32_t sector, uint8_t* dst) {
+    return m_dataCache.cacheSafeRead(sector, dst);
+  }
+  bool cacheSafeWrite(uint32_t sector, const uint8_t* src) {
+    return m_dataCache.cacheSafeWrite(sector, src);
+  }
+  bool cacheSafeRead(uint32_t sector, uint8_t* dst, size_t count) {
+    return m_dataCache.cacheSafeRead(sector, dst, count);
+  }
+  bool cacheSafeWrite(uint32_t sector, const uint8_t* src, size_t count) {
+     return m_dataCache.cacheSafeWrite(sector, src, count);
+  }
   bool readSector(uint32_t sector, uint8_t* dst) {
     return m_blockDev->readSector(sector, dst);
   }
   bool writeSector(uint32_t sector, const uint8_t* src) {
     return m_blockDev->writeSector(sector, src);
   }
-#if USE_MULTI_SECTOR_IO
-  bool readSectors(uint32_t sector, uint8_t* dst, size_t count) {
-    return m_blockDev->readSectors(sector, dst, count);
-  }
-  bool writeSectors(uint32_t sector, const uint8_t* src, size_t count) {
-    return m_blockDev->writeSectors(sector, src, count);
-  }
-#endif  // USE_MULTI_SECTOR_IO
   //----------------------------------------------------------------------------
   static const uint8_t  m_bytesPerSectorShift = 9;
   static const uint16_t m_bytesPerSector = 512;

+ 1 - 1
src/FatLib/FatDbg.cpp

@@ -144,7 +144,7 @@ void FatPartition::dmpFat(print_t* pr, uint32_t start, uint32_t count) {
   uint32_t sector = m_fatStartSector + start;
   uint32_t cluster = nf*start;
   for (uint32_t i = 0; i < count; i++) {
-    cache_t* pc = cacheFetchFat(sector + i, FatCache::CACHE_FOR_READ);
+    cache_t* pc = cacheFetchFat(sector + i, FsCache::CACHE_FOR_READ);
     if (!pc) {
       pr->println(F("cache read failed"));
       return;

+ 17 - 35
src/FatLib/FatFile.cpp

@@ -71,7 +71,7 @@ bool FatFile::addDirCluster() {
     goto fail;
   }
   sector = m_vol->clusterStartSector(m_curCluster);
-  pc = m_vol->cacheFetchData(sector, FatCache::CACHE_RESERVE_FOR_WRITE);
+  pc = m_vol->cacheFetchData(sector, FsCache::CACHE_RESERVE_FOR_WRITE);
   if (!pc) {
     DBG_FAIL_MACRO;
     goto fail;
@@ -187,7 +187,7 @@ bool FatFile::dirEntry(DirFat_t* dst) {
     goto fail;
   }
   // read entry
-  dir = cacheDirEntry(FatCache::CACHE_FOR_READ);
+  dir = cacheDirEntry(FsCache::CACHE_FOR_READ);
   if (!dir) {
     DBG_FAIL_MACRO;
     goto fail;
@@ -378,7 +378,7 @@ bool FatFile::mkdir(FatFile* parent, fname_t* fname) {
     goto fail;
   }
   // cache entry - should already be in cache due to sync() call
-  dir = cacheDirEntry(FatCache::CACHE_FOR_WRITE);
+  dir = cacheDirEntry(FsCache::CACHE_FOR_WRITE);
   if (!dir) {
     DBG_FAIL_MACRO;
     goto fail;
@@ -395,7 +395,7 @@ bool FatFile::mkdir(FatFile* parent, fname_t* fname) {
 
   // cache sector for '.'  and '..'
   sector = m_vol->clusterStartSector(m_firstCluster);
-  pc = m_vol->cacheFetchData(sector, FatCache::CACHE_FOR_WRITE);
+  pc = m_vol->cacheFetchData(sector, FsCache::CACHE_FOR_WRITE);
   if (!pc) {
     DBG_FAIL_MACRO;
     goto fail;
@@ -786,7 +786,7 @@ int FatFile::read(void* buf, size_t nbyte) {
         n = toRead;
       }
       // read sector to cache and copy data to caller
-      pc = m_vol->cacheFetchData(sector, FatCache::CACHE_FOR_READ);
+      pc = m_vol->cacheFetchData(sector, FsCache::CACHE_FOR_READ);
       if (!pc) {
         DBG_FAIL_MACRO;
         goto fail;
@@ -803,16 +803,7 @@ int FatFile::read(void* buf, size_t nbyte) {
         }
       }
       n = ns << m_vol->bytesPerSectorShift();
-      // Check for cache sector in read range.
-      if (sector <= m_vol->cacheSectorNumber()
-          && m_vol->cacheSectorNumber() < (sector + ns)) {
-        // Flush cache if cache sector is in the range.
-        if (!m_vol->cacheSyncData()) {
-          DBG_FAIL_MACRO;
-          goto fail;
-        }
-      }
-      if (!m_vol->readSectors(sector, dst, ns)) {
+      if (!m_vol->cacheSafeRead(sector, dst, ns)) {
         DBG_FAIL_MACRO;
         goto fail;
       }
@@ -820,7 +811,7 @@ int FatFile::read(void* buf, size_t nbyte) {
     } else {
       // read single sector
       n = m_vol->bytesPerSector();
-      if (!m_vol->readSector(sector, dst)) {
+      if (!m_vol->cacheSafeRead(sector, dst)) {
         DBG_FAIL_MACRO;
         goto fail;
       }
@@ -929,7 +920,7 @@ bool FatFile::rename(FatFile* dirFile, const char* newPath) {
   // sync() and cache directory entry
   sync();
   oldFile = *this;
-  dir = cacheDirEntry(FatCache::CACHE_FOR_READ);
+  dir = cacheDirEntry(FsCache::CACHE_FOR_READ);
   if (!dir) {
     DBG_FAIL_MACRO;
     goto fail;
@@ -962,7 +953,7 @@ bool FatFile::rename(FatFile* dirFile, const char* newPath) {
   file.m_flags = 0;
 
   // cache new directory entry
-  dir = cacheDirEntry(FatCache::CACHE_FOR_WRITE);
+  dir = cacheDirEntry(FsCache::CACHE_FOR_WRITE);
   if (!dir) {
     DBG_FAIL_MACRO;
     goto fail;
@@ -976,7 +967,7 @@ bool FatFile::rename(FatFile* dirFile, const char* newPath) {
   if (dirCluster) {
     // get new dot dot
     uint32_t sector = m_vol->clusterStartSector(dirCluster);
-    pc = m_vol->cacheFetchData(sector, FatCache::CACHE_FOR_READ);
+    pc = m_vol->cacheFetchData(sector, FsCache::CACHE_FOR_READ);
     if (!pc) {
       DBG_FAIL_MACRO;
       goto fail;
@@ -990,7 +981,7 @@ bool FatFile::rename(FatFile* dirFile, const char* newPath) {
     }
     // store new dot dot
     sector = m_vol->clusterStartSector(m_firstCluster);
-    pc = m_vol->cacheFetchData(sector, FatCache::CACHE_FOR_WRITE);
+    pc = m_vol->cacheFetchData(sector, FsCache::CACHE_FOR_WRITE);
     if (!pc) {
       DBG_FAIL_MACRO;
       goto fail;
@@ -1200,7 +1191,7 @@ bool FatFile::sync() {
     return true;
   }
   if (m_flags & FILE_FLAG_DIR_DIRTY) {
-    DirFat_t* dir = cacheDirEntry(FatCache::CACHE_FOR_WRITE);
+    DirFat_t* dir = cacheDirEntry(FsCache::CACHE_FOR_WRITE);
     // check for deleted by another open file object
     if (!dir || dir->name[0] == FAT_NAME_DELETED) {
       DBG_FAIL_MACRO;
@@ -1259,7 +1250,7 @@ bool FatFile::timestamp(uint8_t flags, uint16_t year, uint8_t month,
     DBG_FAIL_MACRO;
     goto fail;
   }
-  dir = cacheDirEntry(FatCache::CACHE_FOR_WRITE);
+  dir = cacheDirEntry(FsCache::CACHE_FOR_WRITE);
   if (!dir) {
     DBG_FAIL_MACRO;
     goto fail;
@@ -1420,10 +1411,10 @@ size_t FatFile::write(const void* buf, size_t nbyte) {
       if (sectorOffset == 0 &&
          (m_curPosition >= m_fileSize || m_flags & FILE_FLAG_PREALLOCATE)) {
         // start of new sector don't need to read into cache
-        cacheOption = FatCache::CACHE_RESERVE_FOR_WRITE;
+        cacheOption = FsCache::CACHE_RESERVE_FOR_WRITE;
       } else {
         // rewrite part of sector
-        cacheOption = FatCache::CACHE_FOR_WRITE;
+        cacheOption = FsCache::CACHE_FOR_WRITE;
       }
       pc = m_vol->cacheFetchData(sector, cacheOption);
       if (!pc) {
@@ -1448,13 +1439,7 @@ size_t FatFile::write(const void* buf, size_t nbyte) {
         nSector = maxSectors;
       }
       n = nSector << m_vol->bytesPerSectorShift();
-      // Check for cache sector in write range.
-      if (sector <= m_vol->cacheSectorNumber()
-          && m_vol->cacheSectorNumber() < (sector + nSector)) {
-        // Invalidate cache if cache sector is in the range.
-        m_vol->cacheInvalidate();
-      }
-      if (!m_vol->writeSectors(sector, src, nSector)) {
+      if (!m_vol->cacheSafeWrite(sector, src, nSector)) {
         DBG_FAIL_MACRO;
         goto fail;
       }
@@ -1462,10 +1447,7 @@ size_t FatFile::write(const void* buf, size_t nbyte) {
     } else {
       // use single sector write command
       n = m_vol->bytesPerSector();
-      if (m_vol->cacheSectorNumber() == sector) {
-        m_vol->cacheInvalidate();
-      }
-      if (!m_vol->writeSector(sector, src)) {
+      if (!m_vol->cacheSafeWrite(sector, src)) {
         DBG_FAIL_MACRO;
         goto fail;
       }

+ 0 - 1
src/FatLib/FatFile.h

@@ -70,7 +70,6 @@ struct FatPos_t {
   uint32_t position;
   /** cluster for position */
   uint32_t cluster;
-  FatPos_t() : position(0), cluster(0) {}
 };
 //------------------------------------------------------------------------------
 /** Expression for path name separator. */

+ 1 - 1
src/FatLib/FatFileLFN.cpp

@@ -580,7 +580,7 @@ bool FatFile::remove() {
     goto fail;
   }
   // Cache directory entry.
-  dir = cacheDirEntry(FatCache::CACHE_FOR_WRITE);
+  dir = cacheDirEntry(FsCache::CACHE_FOR_WRITE);
   if (!dir) {
     DBG_FAIL_MACRO;
     goto fail;

+ 2 - 2
src/FatLib/FatFileSFN.cpp

@@ -43,7 +43,7 @@ bool FatFile::getSFN(char* name) {
     return true;
   }
   // cache entry
-  dir = reinterpret_cast<DirFat_t*>(cacheDirEntry(FatCache::CACHE_FOR_READ));
+  dir = reinterpret_cast<DirFat_t*>(cacheDirEntry(FsCache::CACHE_FOR_READ));
   if (!dir) {
     DBG_FAIL_MACRO;
     goto fail;
@@ -285,7 +285,7 @@ bool FatFile::remove() {
     goto fail;
   }
   // Cache directory entry.
-  dir = reinterpret_cast<DirFat_t*>(cacheDirEntry(FatCache::CACHE_FOR_WRITE));
+  dir = reinterpret_cast<DirFat_t*>(cacheDirEntry(FsCache::CACHE_FOR_WRITE));
   if (!dir) {
     DBG_FAIL_MACRO;
     goto fail;

+ 1 - 1
src/FatLib/FatFormatter.cpp

@@ -24,7 +24,7 @@
  */
 #include "FatFormatter.h"
 // Set nonzero to use calculated CHS in MBR.  Should not be required.
-#define USE_LBA_TO_CHS 0
+#define USE_LBA_TO_CHS 1
 
 // Constants for file system structure optimized for flash.
 uint16_t const BU16 = 128;

+ 17 - 63
src/FatLib/FatPartition.cpp

@@ -28,51 +28,6 @@
 #include "../common/FsStructs.h"
 #include "FatPartition.h"
 //------------------------------------------------------------------------------
-cache_t* FatCache::read(uint32_t sector, uint8_t option) {
-  if (m_lbn != sector) {
-    if (!sync()) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    if (!(option & CACHE_OPTION_NO_READ)) {
-      if (!m_part->readSector(sector, m_buffer.data)) {
-        DBG_FAIL_MACRO;
-        goto fail;
-      }
-    }
-    m_status = 0;
-    m_lbn = sector;
-  }
-  m_status |= option & CACHE_STATUS_MASK;
-  return &m_buffer;
-
- fail:
-
-  return nullptr;
-}
-//------------------------------------------------------------------------------
-bool FatCache::sync() {
-  if (m_status & CACHE_STATUS_DIRTY) {
-    if (!m_part->writeSector(m_lbn, m_buffer.data)) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    // mirror second FAT
-    if (m_status & CACHE_STATUS_MIRROR_FAT) {
-      uint32_t sector = m_lbn + m_part->sectorsPerFat();
-      if (!m_part->writeSector(sector, m_buffer.data)) {
-        DBG_FAIL_MACRO;
-        goto fail;
-      }
-    }
-    m_status &= ~CACHE_STATUS_DIRTY;
-  }
-  return true;
-
- fail:
-  return false;
-}
-//------------------------------------------------------------------------------
 bool FatPartition::allocateCluster(uint32_t current, uint32_t* next) {
   uint32_t find;
   bool setStart;
@@ -199,10 +154,6 @@ bool FatPartition::allocContiguous(uint32_t count, uint32_t* firstCluster) {
   return false;
 }
 //------------------------------------------------------------------------------
-uint32_t FatPartition::clusterStartSector(uint32_t cluster) const {
-  return m_dataStartSector + ((cluster - 2) << m_sectorsPerClusterShift);
-}
-//------------------------------------------------------------------------------
 // Fetch a FAT entry - return -1 error, 0 EOC, else 1.
 int8_t FatPartition::fatGet(uint32_t cluster, uint32_t* value) {
   uint32_t sector;
@@ -217,7 +168,7 @@ int8_t FatPartition::fatGet(uint32_t cluster, uint32_t* value) {
 
   if (fatType() == 32) {
     sector = m_fatStartSector + (cluster >> (m_bytesPerSectorShift - 2));
-    pc = cacheFetchFat(sector, FatCache::CACHE_FOR_READ);
+    pc = cacheFetchFat(sector, FsCache::CACHE_FOR_READ);
     if (!pc) {
       DBG_FAIL_MACRO;
       goto fail;
@@ -227,7 +178,7 @@ int8_t FatPartition::fatGet(uint32_t cluster, uint32_t* value) {
   } else if (fatType() == 16) {
     cluster &= 0XFFFF;
     sector = m_fatStartSector + (cluster >> (m_bytesPerSectorShift - 1) );
-    pc = cacheFetchFat(sector, FatCache::CACHE_FOR_READ);
+    pc = cacheFetchFat(sector, FsCache::CACHE_FOR_READ);
     if (!pc) {
       DBG_FAIL_MACRO;
       goto fail;
@@ -238,7 +189,7 @@ int8_t FatPartition::fatGet(uint32_t cluster, uint32_t* value) {
     uint16_t index = cluster;
     index += index >> 1;
     sector = m_fatStartSector + (index >> m_bytesPerSectorShift);
-    pc = cacheFetchFat(sector, FatCache::CACHE_FOR_READ);
+    pc = cacheFetchFat(sector, FsCache::CACHE_FOR_READ);
     if (!pc) {
       DBG_FAIL_MACRO;
       goto fail;
@@ -247,7 +198,7 @@ int8_t FatPartition::fatGet(uint32_t cluster, uint32_t* value) {
     uint16_t tmp = pc->data[index];
     index++;
     if (index == m_bytesPerSector) {
-      pc = cacheFetchFat(sector + 1, FatCache::CACHE_FOR_READ);
+      pc = cacheFetchFat(sector + 1, FsCache::CACHE_FOR_READ);
       if (!pc) {
         DBG_FAIL_MACRO;
         goto fail;
@@ -283,7 +234,7 @@ bool FatPartition::fatPut(uint32_t cluster, uint32_t value) {
 
   if (fatType() == 32) {
     sector = m_fatStartSector + (cluster >> (m_bytesPerSectorShift - 2));
-    pc = cacheFetchFat(sector, FatCache::CACHE_FOR_WRITE);
+    pc = cacheFetchFat(sector, FsCache::CACHE_FOR_WRITE);
     if (!pc) {
       DBG_FAIL_MACRO;
       goto fail;
@@ -296,7 +247,7 @@ bool FatPartition::fatPut(uint32_t cluster, uint32_t value) {
   if (fatType() == 16) {
     cluster &= 0XFFFF;
     sector = m_fatStartSector + (cluster >> (m_bytesPerSectorShift - 1) );
-    pc = cacheFetchFat(sector, FatCache::CACHE_FOR_WRITE);
+    pc = cacheFetchFat(sector, FsCache::CACHE_FOR_WRITE);
     if (!pc) {
       DBG_FAIL_MACRO;
       goto fail;
@@ -310,7 +261,7 @@ bool FatPartition::fatPut(uint32_t cluster, uint32_t value) {
     uint16_t index = cluster;
     index += index >> 1;
     sector = m_fatStartSector + (index >> m_bytesPerSectorShift);
-    pc = cacheFetchFat(sector, FatCache::CACHE_FOR_WRITE);
+    pc = cacheFetchFat(sector, FsCache::CACHE_FOR_WRITE);
     if (!pc) {
       DBG_FAIL_MACRO;
       goto fail;
@@ -326,7 +277,7 @@ bool FatPartition::fatPut(uint32_t cluster, uint32_t value) {
     if (index == m_bytesPerSector) {
       sector++;
       index = 0;
-      pc = cacheFetchFat(sector, FatCache::CACHE_FOR_WRITE);
+      pc = cacheFetchFat(sector, FsCache::CACHE_FOR_WRITE);
       if (!pc) {
         DBG_FAIL_MACRO;
         goto fail;
@@ -402,7 +353,7 @@ int32_t FatPartition::freeClusterCount() {
   } else if (fatType() == 16 || fatType() == 32) {
     sector = m_fatStartSector;
     while (todo) {
-      cache_t* pc = cacheFetchFat(sector++, FatCache::CACHE_FOR_READ);
+      cache_t* pc = cacheFetchFat(sector++, FsCache::CACHE_FOR_READ);
       if (!pc) {
         DBG_FAIL_MACRO;
         goto fail;
@@ -449,9 +400,9 @@ bool FatPartition::init(BlockDevice* dev, uint8_t part) {
   uint8_t tmp;
   m_fatType = 0;
   m_allocSearchStart = 1;
-  m_cache.init(this);
+  m_cache.init(dev);
 #if USE_SEPARATE_FAT_CACHE
-  m_fatCache.init(this);
+  m_fatCache.init(dev);
 #endif  // USE_SEPARATE_FAT_CACHE
   // if part == 0 assume super floppy with FAT boot sector in sector zero
   // if part > 0 assume mbr volume with partition table
@@ -461,7 +412,7 @@ bool FatPartition::init(BlockDevice* dev, uint8_t part) {
       goto fail;
     }
     mbr = reinterpret_cast<MbrSector_t*>
-          (cacheFetchData(0, FatCache::CACHE_FOR_READ));
+          (cacheFetchData(0, FsCache::CACHE_FOR_READ));
     MbrPart_t* mp = mbr->part + part - 1;
 
     if (!mbr || mp->type == 0 || (mp->boot != 0 && mp->boot != 0X80)) {
@@ -471,9 +422,8 @@ bool FatPartition::init(BlockDevice* dev, uint8_t part) {
     volumeStartSector = getLe32(mp->relativeSectors);
   }
   pbs = reinterpret_cast<pbs_t*>
-        (cacheFetchData(volumeStartSector, FatCache::CACHE_FOR_READ));
+        (cacheFetchData(volumeStartSector, FsCache::CACHE_FOR_READ));
   bpb = reinterpret_cast<BpbFat32_t*>(pbs->bpb);
-
   if (!pbs || bpb->fatCount != 2 || getLe16(bpb->bytesPerSector) != 512) {
     DBG_FAIL_MACRO;
     goto fail;
@@ -531,6 +481,10 @@ bool FatPartition::init(BlockDevice* dev, uint8_t part) {
     m_rootDirStart = getLe32(bpb->fat32RootCluster);
     m_fatType = 32;
   }
+  m_cache.setMirrorOffset(m_sectorsPerFat);
+#if USE_SEPARATE_FAT_CACHE
+  m_fatCache.setMirrorOffset(m_sectorsPerFat);
+#endif  // USE_SEPARATE_FAT_CACHE
   return true;
 
  fail:

+ 28 - 98
src/FatLib/FatPartition.h

@@ -32,6 +32,7 @@
 #include "FatLibConfig.h"
 #include "../common/SysCall.h"
 #include "../common/BlockDevice.h"
+#include "../common/FsCache.h"
 #include "../common/FsStructs.h"
 
 /** Type for FAT12 partition */
@@ -43,12 +44,9 @@ const uint8_t FAT_TYPE_FAT16 = 16;
 /** Type for FAT12 partition */
 const uint8_t FAT_TYPE_FAT32 = 32;
 
-//------------------------------------------------------------------------------
-// Forward declaration of FatPartition.
-class FatPartition;
 //------------------------------------------------------------------------------
 /**
- * \brief Cache for an raw data sector.
+ * \brief Cache type for a sector.
  */
 union cache_t {
   /** Used to access cached file data sectors. */
@@ -58,74 +56,7 @@ union cache_t {
   /** Used to access cached FAT32 entries. */
   uint32_t fat32[128];
   /** Used to access cached directory entries. */
-  DirFat_t    dir[16];
-};
-//==============================================================================
-/**
- * \class FatCache
- * \brief Sector cache.
- */
-class FatCache {
- public:
-  /** Cached sector is dirty */
-  static const uint8_t CACHE_STATUS_DIRTY = 1;
-  /** Cashed sector is FAT entry and must be mirrored in second FAT. */
-  static const uint8_t CACHE_STATUS_MIRROR_FAT = 2;
-  /** Cache sector status bits */
-  static const uint8_t CACHE_STATUS_MASK
-    = CACHE_STATUS_DIRTY | CACHE_STATUS_MIRROR_FAT;
-  /** Sync existing sector but do not read new sector. */
-  static const uint8_t CACHE_OPTION_NO_READ = 4;
-  /** Cache sector for read. */
-  static const uint8_t CACHE_FOR_READ = 0;
-  /** Cache sector for write. */
-  static const uint8_t CACHE_FOR_WRITE = CACHE_STATUS_DIRTY;
-  /** Reserve cache sector for write - do not read from sector device. */
-  static const uint8_t CACHE_RESERVE_FOR_WRITE
-    = CACHE_STATUS_DIRTY | CACHE_OPTION_NO_READ;
-  /** \return Cache sector address. */
-  cache_t* buffer() {
-    return &m_buffer;
-  }
-  /** Set current sector dirty. */
-  void dirty() {
-    m_status |= CACHE_STATUS_DIRTY;
-  }
-  /** Initialize the cache.
-   * \param[in] vol FatPartition that owns this FatCache.
-   */
-  void init(FatPartition *vol) {
-    m_part = vol;
-    invalidate();
-  }
-  /** Invalidate current cache sector. */
-  void invalidate() {
-    m_status = 0;
-    m_lbn = 0XFFFFFFFF;
-  }
-  /** \return dirty status */
-  bool isDirty() {
-    return m_status & CACHE_STATUS_DIRTY;
-  }
-  /** \return Logical sector number for cached sector. */
-  uint32_t sector() {
-    return m_lbn;
-  }
-  /** Read a sector into the cache.
-   * \param[in] sector Sector to read.
-   * \param[in] option mode for cached sector.
-   * \return Address of cached sector. */
-  cache_t* read(uint32_t sector, uint8_t option);
-  /** Write current sector if dirty.
-   * \return true for success or false for failure.
-   */
-  bool sync();
-
- private:
-  uint8_t m_status;
-  FatPartition* m_part;
-  uint32_t m_lbn;
-  cache_t m_buffer;
+  DirFat_t dir[16];
 };
 //==============================================================================
 /**
@@ -173,12 +104,8 @@ class FatPartition {
   /** Clear the cache and returns a pointer to the cache.  Not for normal apps.
    * \return A pointer to the cache buffer or zero if an error occurs.
    */
-  cache_t* cacheClear() {
-    if (!cacheSync()) {
-      return nullptr;
-    }
-    m_cache.invalidate();
-    return m_cache.buffer();
+  uint8_t* cacheClear() {
+    return m_cache.clear();
   }
   /** \return The total number of clusters in the volume. */
   uint32_t clusterCount() const {
@@ -251,8 +178,6 @@ class FatPartition {
 #endif  // DOXYGEN_SHOULD_SKIP_THIS
   //----------------------------------------------------------------------------
  private:
-  /** FatCache allowed access to private members. */
-  friend class FatCache;
   /** FatFile allowed access to private members. */
   friend class FatFile;
   //----------------------------------------------------------------------------
@@ -274,6 +199,18 @@ class FatPartition {
   uint32_t m_rootDirStart;            // Start sector FAT16, cluster FAT32.
   //----------------------------------------------------------------------------
   // sector I/O functions.
+  bool cacheSafeRead(uint32_t sector, uint8_t* dst) {
+    return m_cache.cacheSafeRead(sector, dst);
+  }
+  bool cacheSafeRead(uint32_t sector, uint8_t* dst, size_t count) {
+    return m_cache.cacheSafeRead(sector, dst, count);
+  }
+  bool cacheSafeWrite(uint32_t sector, const uint8_t* dst) {
+    return m_cache.cacheSafeWrite(sector, dst);
+  }
+  bool cacheSafeWrite(uint32_t sector, const uint8_t* dst, size_t count) {
+    return m_cache.cacheSafeWrite(sector, dst, count);
+  }
   bool readSector(uint32_t sector, uint8_t* dst) {
     return m_blockDev->readSector(sector, dst);
   }
@@ -283,14 +220,6 @@ class FatPartition {
   bool writeSector(uint32_t sector, const uint8_t* src) {
     return m_blockDev->writeSector(sector, src);
   }
-#if USE_MULTI_SECTOR_IO
-  bool readSectors(uint32_t sector, uint8_t* dst, size_t ns) {
-    return m_blockDev->readSectors(sector, dst, ns);
-  }
-  bool writeSectors(uint32_t sector, const uint8_t* src, size_t ns) {
-    return m_blockDev->writeSectors(sector, src, ns);
-  }
-#endif  // USE_MULTI_SECTOR_IO
 #if MAINTAIN_FREE_CLUSTER_COUNT
   int32_t  m_freeClusterCount;     // Count of free clusters in volume.
   void setFreeClusterCount(int32_t value) {
@@ -309,29 +238,28 @@ class FatPartition {
     (void)change;
   }
 #endif  // MAINTAIN_FREE_CLUSTER_COUNT
-
 // sector caches
-  FatCache m_cache;
+  FsCache m_cache;
 #if USE_SEPARATE_FAT_CACHE
-  FatCache m_fatCache;
+  FsCache m_fatCache;
   cache_t* cacheFetchFat(uint32_t sector, uint8_t options) {
-    return m_fatCache.read(sector,
-                           options | FatCache::CACHE_STATUS_MIRROR_FAT);
+    options |= FsCache::CACHE_STATUS_MIRROR_FAT;
+    return reinterpret_cast<cache_t*>(m_fatCache.get(sector, options));
   }
   bool cacheSync() {
     return m_cache.sync() && m_fatCache.sync() && syncDevice();
   }
 #else  // USE_SEPARATE_FAT_CACHE
   cache_t* cacheFetchFat(uint32_t sector, uint8_t options) {
-    return cacheFetchData(sector,
-                          options | FatCache::CACHE_STATUS_MIRROR_FAT);
+    options |= FsCache::CACHE_STATUS_MIRROR_FAT;
+    return cacheFetchData(sector, options);
   }
   bool cacheSync() {
     return m_cache.sync() && syncDevice();
   }
 #endif  // USE_SEPARATE_FAT_CACHE
   cache_t* cacheFetchData(uint32_t sector, uint8_t options) {
-    return m_cache.read(sector, options);
+    return reinterpret_cast<cache_t*>(m_cache.get(sector, options));
   }
   void cacheInvalidate() {
     m_cache.invalidate();
@@ -340,7 +268,7 @@ class FatPartition {
     return m_cache.sync();
   }
   cache_t* cacheAddress() {
-    return m_cache.buffer();
+    return reinterpret_cast<cache_t*>(m_cache.cacheBuffer());
   }
   uint32_t cacheSectorNumber() {
     return m_cache.sector();
@@ -354,7 +282,9 @@ class FatPartition {
   uint8_t sectorOfCluster(uint32_t position) const {
     return (position >> 9) & m_clusterSectorMask;
   }
-  uint32_t clusterStartSector(uint32_t cluster) const;
+  uint32_t clusterStartSector(uint32_t cluster) const {
+    return m_dataStartSector + ((cluster - 2) << m_sectorsPerClusterShift);
+  }
   int8_t fatGet(uint32_t cluster, uint32_t* value);
   bool fatPut(uint32_t cluster, uint32_t value);
   bool fatPutEOC(uint32_t cluster) {

+ 2 - 2
src/SdFat.h

@@ -357,7 +357,7 @@ class SdFat32 : public SdBase<FatVolume> {
    */
   bool format(print_t* pr = nullptr) {
     FatFormatter fmt;
-    uint8_t* cache = reinterpret_cast<uint8_t*>(cacheClear());
+    uint8_t* cache = cacheClear();
     if (!cache) {
       return false;
     }
@@ -378,7 +378,7 @@ class SdExFat : public SdBase<ExFatVolume> {
    */
   bool format(print_t* pr = nullptr) {
     ExFatFormatter fmt;
-    uint8_t* cache = reinterpret_cast<uint8_t*>(cacheClear());
+    uint8_t* cache = cacheClear();
     if (!cache) {
       return false;
     }

+ 3 - 4
src/common/BlockDeviceInterface.h

@@ -46,7 +46,7 @@ class BlockDeviceInterface {
    * \return true for success or false for failure.
    */
   virtual bool readSector(uint32_t sector, uint8_t* dst) = 0;
-#if USE_MULTI_SECTOR_IO
+
   /**
    * Read multiple sectors.
    *
@@ -56,7 +56,7 @@ class BlockDeviceInterface {
    * \return true for success or false for failure.
    */
   virtual bool readSectors(uint32_t sector, uint8_t* dst, size_t ns) = 0;
-#endif  // USE_MULTI_SECTOR_IO
+
   /** \return device size in sectors. */
   virtual uint32_t sectorCount() = 0;
 
@@ -73,7 +73,7 @@ class BlockDeviceInterface {
    * \return true for success or false for failure.
    */
   virtual bool writeSector(uint32_t sector, const uint8_t* src) = 0;
-#if USE_MULTI_SECTOR_IO
+
   /**
    * Write multiple sectors.
    *
@@ -83,6 +83,5 @@ class BlockDeviceInterface {
    * \return true for success or false for failure.
    */
   virtual bool writeSectors(uint32_t sector, const uint8_t* src, size_t ns) = 0;
-#endif  // USE_MULTI_SECTOR_IO
 };
 #endif  // BlockDeviceInterface_h

+ 74 - 0
src/common/FsCache.cpp

@@ -0,0 +1,74 @@
+/**
+ * Copyright (c) 2011-2020 Bill Greiman
+ * This file is part of the SdFat library for SD memory cards.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#include "DebugMacros.h"
+#include "FsCache.h"
+//------------------------------------------------------------------------------
+uint8_t* FsCache::get(uint32_t sector, uint8_t option) {
+  if (!m_blockDev) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  if (m_sector != sector) {
+    if (!sync()) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    if (!(option & CACHE_OPTION_NO_READ)) {
+      if (!m_blockDev->readSector(sector, m_buffer)) {
+        DBG_FAIL_MACRO;
+        goto fail;
+      }
+    }
+    m_status = 0;
+    m_sector = sector;
+  }
+  m_status |= option & CACHE_STATUS_MASK;
+  return m_buffer;
+
+ fail:
+  return nullptr;
+}
+//------------------------------------------------------------------------------
+bool FsCache::sync() {
+  if (m_status & CACHE_STATUS_DIRTY) {
+    if (!m_blockDev->writeSector(m_sector, m_buffer)) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    // mirror second FAT
+    if (m_status & CACHE_STATUS_MIRROR_FAT) {
+      uint32_t sector = m_sector + m_mirrorOffset;
+      if (!m_blockDev->writeSector(sector, m_buffer)) {
+        DBG_FAIL_MACRO;
+        goto fail;
+      }
+    }
+    m_status &= ~CACHE_STATUS_DIRTY;
+  }
+  return true;
+
+ fail:
+  return false;
+}

+ 184 - 0
src/common/FsCache.h

@@ -0,0 +1,184 @@
+/**
+ * Copyright (c) 2011-2020 Bill Greiman
+ * This file is part of the SdFat library for SD memory cards.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#ifndef FsCache_h
+#define FsCache_h
+/**
+ * \file
+ * \brief Common cache code for exFAT and FAT.
+ */
+#include "SysCall.h"
+#include "BlockDevice.h"
+#include "DebugMacros.h"
+/**
+ * \class FsCache
+ * \brief Sector cache.
+ */
+class FsCache {
+ public:
+  /** Cached sector is dirty */
+  static const uint8_t CACHE_STATUS_DIRTY = 1;
+  /** Cashed sector is FAT entry and must be mirrored in second FAT. */
+  static const uint8_t CACHE_STATUS_MIRROR_FAT = 2;
+  /** Cache sector status bits */
+  static const uint8_t CACHE_STATUS_MASK =
+    CACHE_STATUS_DIRTY | CACHE_STATUS_MIRROR_FAT;
+  /** Sync existing sector but do not read new sector. */
+  static const uint8_t CACHE_OPTION_NO_READ = 4;
+  /** Cache sector for read. */
+  static const uint8_t CACHE_FOR_READ = 0;
+  /** Cache sector for write. */
+  static const uint8_t CACHE_FOR_WRITE = CACHE_STATUS_DIRTY;
+  /** Reserve cache sector for write - do not read from sector device. */
+  static const uint8_t CACHE_RESERVE_FOR_WRITE =
+    CACHE_STATUS_DIRTY | CACHE_OPTION_NO_READ;
+  //----------------------------------------------------------------------------
+  /** \return Cache buffer address. */
+  uint8_t* cacheBuffer() {
+    return m_buffer;
+  }
+  /**
+   * Cache safe read of a sector.
+   *
+   * \param[in] sector Logical sector to be read.
+   * \param[out] dst Pointer to the location that will receive the data.
+   * \return true for success or false for failure.
+   */
+  bool cacheSafeRead(uint32_t sector, uint8_t* dst) {
+    if (isCached(sector)) {
+      memcpy(dst, m_buffer, 512);
+      return true;
+    }
+    return m_blockDev->readSector(sector, dst);
+  }
+  /**
+   * Cache safe read of multiple sectors.
+   *
+   * \param[in] sector Logical sector to be read.
+   * \param[in] count Number of sectors to be read.
+   * \param[out] dst Pointer to the location that will receive the data.
+   * \return true for success or false for failure.
+   */
+  bool cacheSafeRead(uint32_t sector, uint8_t* dst, size_t count) {
+    if (isCached(sector, count) && !sync()) {
+      return false;
+    }
+    return m_blockDev->readSectors(sector, dst, count);
+  }
+  /**
+   * Cache safe write of a sectors.
+   *
+   * \param[in] sector Logical sector to be written.
+   * \param[in] src Pointer to the location of the data to be written.
+   * \return true for success or false for failure.
+   */
+  bool cacheSafeWrite(uint32_t sector, const uint8_t* src) {
+    if (isCached(sector)) {
+      invalidate();
+    }
+    return m_blockDev->writeSector(sector, src);
+  }
+  /**
+   * Cache safe write of multiple sectors.
+   *
+   * \param[in] sector Logical sector to be written.
+   * \param[in] src Pointer to the location of the data to be written.
+   * \param[in] count Number of sectors to be written.
+   * \return true for success or false for failure.
+   */
+  bool cacheSafeWrite(uint32_t sector, const uint8_t* src, size_t count) {
+     if (isCached(sector, count)) {
+      invalidate();
+    }
+    return m_blockDev->writeSectors(sector, src, count);
+  }
+  /** \return Clear the cache and returns a pointer to the cache. */
+  uint8_t* clear() {
+    if (isDirty() && !sync()) {
+      return nullptr;
+    }
+    invalidate();
+    return m_buffer;
+  }
+  /** Set current sector dirty. */
+  void dirty() {
+    m_status |= CACHE_STATUS_DIRTY;
+  }
+  /** Initialize the cache.
+   * \param[in] blockDev Block device for this cache.
+   */
+  void init(BlockDevice* blockDev) {
+    m_blockDev = blockDev;
+    invalidate();
+  }
+  /** Invalidate current cache sector. */
+  void invalidate() {
+    m_status = 0;
+    m_sector = 0XFFFFFFFF;
+  }
+  /** Check if a sector is in the cache.
+   * \param[in] sector Sector to checked.
+   * \return true if the sector is cached.
+   */
+  bool isCached(uint32_t sector) {return sector == m_sector;}
+   /** Check if the cache contains a sector from a range.
+   * \param[in] sector Start sector of the range.
+   * \param[in] count Number of sectors in the range.
+   * \return true if a sector in the range is cached.
+   */
+  bool isCached(uint32_t sector, size_t count) {
+    return sector <= m_sector && m_sector < (sector + count);
+  }
+  /** \return dirty status */
+  bool isDirty() {
+    return m_status & CACHE_STATUS_DIRTY;
+  }
+  /** \return Logical sector number for cached sector. */
+  uint32_t sector() {
+    return m_sector;
+  }
+  /** Set the offset to the second FAT for mirroring.
+   * \param[in] offset Sector offset to second FAT.
+   */
+  void setMirrorOffset(uint32_t offset) {
+    m_mirrorOffset = offset;
+  }
+  /** Fill cache with sector data.
+   * \param[in] sector Sector to read.
+   * \param[in] option mode for cached sector.
+   * \return Address of cached sector. */
+  uint8_t* get(uint32_t sector, uint8_t option);
+  /** Write current sector if dirty.
+   * \return true for success or false for failure.
+   */
+  bool sync();
+
+ private:
+  uint8_t m_status;
+  BlockDevice* m_blockDev;
+  uint32_t m_mirrorOffset;
+  uint32_t m_sector;
+  uint8_t m_buffer[512];
+};
+#endif  // FsCache_h

+ 5 - 6
src/common/FsDateTime.cpp

@@ -74,8 +74,7 @@ char* fsFmtTime(char* str, uint16_t time) {
 }
 //------------------------------------------------------------------------------
 char* fsFmtTime(char* str, uint16_t time, uint8_t sec100) {
-  str = fsFmtField(str, sec100%100, 0);
-  str = fsFmtField(str, 2*(time & 31) + sec100/100, '.');
+  str = fsFmtField(str, 2*(time & 31) + (sec100 < 100 ? 0 : 1), 0);
   *--str = ':';
   return fsFmtTime(str, time);
 }
@@ -138,8 +137,8 @@ size_t fsPrintDateTime(print_t* pr, uint32_t dateTime) {
 //------------------------------------------------------------------------------
 size_t fsPrintDateTime(print_t* pr,
                        uint32_t dateTime, uint8_t s100, int8_t tz) {
-  // Allow YYYY-MM-DD hh:mm:ss.ss UTC+hh:mm
-  char buf[sizeof("YYYY-MM-DD hh:mm:ss.ss UTC+hh:mm") -1];
+  // Allow YYYY-MM-DD hh:mm:ss UTC+hh:mm
+  char buf[sizeof("YYYY-MM-DD hh:mm:ss UTC+hh:mm") -1];
   char* str = buf + sizeof(buf);
   if (tz) {
     str = fsFmtTimeZone(str, tz);
@@ -160,8 +159,8 @@ size_t fsPrintTime(print_t* pr, uint16_t time) {
 }
 //------------------------------------------------------------------------------
 size_t fsPrintTime(print_t* pr, uint16_t time, uint8_t sec100) {
-  // Allow hh:mm:ss.ss
-  char buf[sizeof("hh:mm:ss.ss") -1];
+  // Allow hh:mm:ss
+  char buf[sizeof("hh:mm:ss") -1];
   char* str = buf + sizeof(buf);
   str = fsFmtTime(str, time, sec100);
   return pr->write(reinterpret_cast<uint8_t*>(str), buf + sizeof(buf) - str);