Przeglądaj źródła

Restructure for RTOS use, getName mods, bug fixes

Bill Greiman 3 lat temu
rodzic
commit
bab0061a51
60 zmienionych plików z 678 dodań i 283 usunięć
  1. BIN
      doc/html.zip
  2. 3 3
      library.properties
  3. 3 3
      src/ExFatLib/ExFatConfig.h
  4. 6 6
      src/ExFatLib/ExFatFile.cpp
  5. 1 1
      src/ExFatLib/ExFatFile.h
  6. 3 3
      src/ExFatLib/ExFatFileWrite.cpp
  7. 1 1
      src/ExFatLib/ExFatFormatter.cpp
  8. 3 3
      src/ExFatLib/ExFatFormatter.h
  9. 7 3
      src/ExFatLib/ExFatName.cpp
  10. 1 1
      src/ExFatLib/ExFatPartition.cpp
  11. 16 6
      src/ExFatLib/ExFatPartition.h
  12. 1 1
      src/ExFatLib/ExFatVolume.h
  13. 1 9
      src/FatLib/FatFile.h
  14. 2 2
      src/FatLib/FatFileLFN.cpp
  15. 2 2
      src/FatLib/FatFileSFN.cpp
  16. 1 1
      src/FatLib/FatFormatter.cpp
  17. 3 3
      src/FatLib/FatFormatter.h
  18. 14 10
      src/FatLib/FatName.cpp
  19. 1 1
      src/FatLib/FatPartition.cpp
  20. 12 5
      src/FatLib/FatPartition.h
  21. 1 2
      src/FatLib/FatVolume.h
  22. 9 9
      src/FsLib/FsFile.h
  23. 57 0
      src/FsLib/FsFormatter.h
  24. 1 0
      src/FsLib/FsLib.h
  25. 6 4
      src/FsLib/FsVolume.cpp
  26. 11 5
      src/FsLib/FsVolume.h
  27. 16 2
      src/SdCard/SdCardInterface.h
  28. 13 6
      src/SdCard/SdSpiCard.cpp
  29. 33 4
      src/SdCard/SdSpiCard.h
  30. 2 2
      src/SdCard/SdioCard.h
  31. 1 2
      src/SdCard/SdioTeensy.cpp
  32. 75 61
      src/SdFat.h
  33. 3 16
      src/SdFatConfig.h
  34. 4 0
      src/SpiDriver/SdSpiArtemis.cpp
  35. 8 4
      src/SpiDriver/SdSpiAvr.h
  36. 4 4
      src/SpiDriver/SdSpiBareUnoDriver.h
  37. 2 0
      src/SpiDriver/SdSpiBaseClass.h
  38. 4 5
      src/SpiDriver/SdSpiDriver.h
  39. 23 19
      src/SpiDriver/SdSpiDue.cpp
  40. 4 0
      src/SpiDriver/SdSpiESP.cpp
  41. 4 0
      src/SpiDriver/SdSpiLibDriver.h
  42. 4 0
      src/SpiDriver/SdSpiParticle.cpp
  43. 4 0
      src/SpiDriver/SdSpiSTM32.cpp
  44. 4 0
      src/SpiDriver/SdSpiSTM32Core.cpp
  45. 2 0
      src/SpiDriver/SdSpiSoftDriver.h
  46. 4 0
      src/SpiDriver/SdSpiTeensy3.cpp
  47. 2 3
      src/common/ArduinoFiles.h
  48. 1 1
      src/common/DebugMacros.h
  49. 1 2
      src/common/FmtNumber.cpp
  50. 1 2
      src/common/FsApiConstants.h
  51. 5 5
      src/common/FsBlockDevice.h
  52. 12 10
      src/common/FsBlockDeviceInterface.h
  53. 9 9
      src/common/FsCache.h
  54. 1 1
      src/common/FsName.h
  55. 91 0
      src/common/PrintBasic.cpp
  56. 169 0
      src/common/PrintBasic.h
  57. 1 35
      src/common/SysCall.h
  58. 3 4
      src/iostream/ArduinoStream.h
  59. 1 1
      src/iostream/StdioStream.h
  60. 1 1
      src/iostream/ios.h

BIN
doc/html.zip


+ 3 - 3
library.properties

@@ -1,10 +1,10 @@
 name=SdFat
-version=2.1.1
+version=2.1.2
 license=MIT
 author=Bill Greiman <fat16lib@sbcglobal.net>
 maintainer=Bill Greiman <fat16lib@sbcglobal.net>
-sentence=FAT16/FAT32/exFAT file system.
-paragraph=FAT16/FAT32/exFAT file system.
+sentence=Provides access to SD memory cards.
+paragraph=The SdFat library supports FAT16, FAT32, and exFAT file systems on Standard SD, SDHC, and SDXC cards.
 category=Data Storage
 url=https://github.com/greiman/SdFat
 repository=https://github.com/greiman/SdFat.git

+ 3 - 3
src/ExFatLib/ExFatConfig.h

@@ -26,8 +26,8 @@
 #define ExFatConfig_h
 #include "SdFatConfig.h"
 
-#ifndef READ_ONLY
-#define READ_ONLY 0
-#endif  // READ_ONLY
+#ifndef EXFAT_READ_ONLY
+#define EXFAT_READ_ONLY 0
+#endif  // EXFAT_READ_ONLY
 
 #endif  // ExFatConfig_h

+ 6 - 6
src/ExFatLib/ExFatFile.cpp

@@ -361,13 +361,13 @@ bool ExFatFile::openPrivate(ExFatFile* dir, ExName_t* fname, oflag_t oflag) {
   }
   // Write, truncate, or at end is an error for a directory or read-only file.
   if ((oflag & (O_TRUNC | O_AT_END)) || (m_flags & FILE_FLAG_WRITE)) {
-    if (isSubDir() || isReadOnly() || READ_ONLY) {
+    if (isSubDir() || isReadOnly() || EXFAT_READ_ONLY) {
       DBG_FAIL_MACRO;
       goto fail;
     }
   }
 
-#if !READ_ONLY
+#if !EXFAT_READ_ONLY
   if (oflag & O_TRUNC) {
     if (!(m_flags & FILE_FLAG_WRITE)) {
       DBG_FAIL_MACRO;
@@ -381,14 +381,14 @@ bool ExFatFile::openPrivate(ExFatFile* dir, ExName_t* fname, oflag_t oflag) {
     DBG_FAIL_MACRO;
     goto fail;
   }
-#endif  // !READ_ONLY
+#endif  // !EXFAT_READ_ONLY
   return true;
 
  create:
-#if READ_ONLY
+#if EXFAT_READ_ONLY
   DBG_FAIL_MACRO;
   goto fail;
-#else  // READ_ONLY
+#else  // EXFAT_READ_ONLY
   // don't create unless O_CREAT and write
   if (!(oflag & O_CREAT) || !(modeFlags & FILE_FLAG_WRITE) || !fname) {
     DBG_WARN_MACRO;
@@ -471,7 +471,7 @@ bool ExFatFile::openPrivate(ExFatFile* dir, ExName_t* fname, oflag_t oflag) {
     }
   }
   return sync();
-#endif  // READ_ONLY
+#endif  // EXFAT_READ_ONLY
 
  fail:
   // close file

+ 1 - 1
src/ExFatLib/ExFatFile.h

@@ -250,7 +250,7 @@ class ExFatFile {
     return isOpen() ? m_error & WRITE_ERROR : true;
   }
   /**
-   * Check for BlockDevice busy.
+   * Check for FsBlockDevice busy.
    *
    * \return true if busy else false.
    */

+ 3 - 3
src/ExFatLib/ExFatFileWrite.cpp

@@ -26,7 +26,7 @@
 #include "../common/DebugMacros.h"
 #include "ExFatLib.h"
 //==============================================================================
-#if READ_ONLY
+#if EXFAT_READ_ONLY
 bool ExFatFile::mkdir(ExFatFile* parent, const char* path, bool pFlag) {
   (void) parent;
   (void)path;
@@ -58,7 +58,7 @@ size_t ExFatFile::write(const void* buf, size_t nbyte) {
   return false;
 }
 //==============================================================================
-#else  // READ_ONLY
+#else  // EXFAT_READ_ONLY
 //------------------------------------------------------------------------------
 static uint16_t exFatDirChecksum(const uint8_t* data, uint16_t checksum) {
   bool skip = data[0] == EXFAT_TYPE_FILE;
@@ -753,4 +753,4 @@ size_t ExFatFile::write(const void* buf, size_t nbyte) {
   m_error |= WRITE_ERROR;
   return 0;
 }
-#endif  // READ_ONLY
+#endif  // EXFAT_READ_ONLY

+ 1 - 1
src/ExFatLib/ExFatFormatter.cpp

@@ -46,7 +46,7 @@ const uint32_t ROOT_CLUSTER = 4;
 #define writeMsg(pr, str) if (pr) pr->write(str)
 #endif  // PRINT_FORMAT_PROGRESS
 //------------------------------------------------------------------------------
-bool ExFatFormatter::format(BlockDevice* dev, uint8_t* secBuf, print_t* pr) {
+bool ExFatFormatter::format(FsBlockDevice* dev, uint8_t* secBuf, print_t* pr) {
 #if !PRINT_FORMAT_PROGRESS
 (void)pr;
 #endif  //  !PRINT_FORMAT_PROGRESS

+ 3 - 3
src/ExFatLib/ExFatFormatter.h

@@ -24,7 +24,7 @@
  */
 #ifndef ExFatFormatter_h
 #define ExFatFormatter_h
-#include "../common/BlockDevice.h"
+#include "../common/FsBlockDevice.h"
 /**
  * \class ExFatFormatter
  * \brief Format an exFAT volume.
@@ -40,7 +40,7 @@ class ExFatFormatter {
    *
    * \return true for success or false for failure.
    */
-  bool format(BlockDevice* dev, uint8_t* secBuf, print_t* pr = nullptr);
+  bool format(FsBlockDevice* dev, uint8_t* secBuf, print_t* pr = nullptr);
  private:
   bool syncUpcase();
   bool writeUpcase(uint32_t sector);
@@ -49,7 +49,7 @@ class ExFatFormatter {
   uint32_t m_upcaseSector;
   uint32_t m_upcaseChecksum;
   uint32_t m_upcaseSize;
-  BlockDevice* m_dev;
+  FsBlockDevice* m_dev;
   uint8_t* m_secBuf;
 };
 #endif  // ExFatFormatter_h

+ 7 - 3
src/ExFatLib/ExFatName.cpp

@@ -83,9 +83,13 @@ size_t ExFatFile::getName7(char* name, size_t count) {
     }
     for (uint8_t in = 0; in < 15; in++) {
       uint16_t c = getLe16(dn->unicode + 2*in);
-      if (c == 0 || (n + 1) >= count) {
+      if (c == 0) {
         goto done;
       }
+      if ((n + 1) >= count) {
+        DBG_FAIL_MACRO;
+        goto fail;
+      }
       name[n++] = c < 0X7F ? c : '?';
     }
   }
@@ -140,8 +144,8 @@ size_t ExFatFile::getName8(char* name, size_t count) {
       // Save space for zero byte.
       ptr = FsUtf::cpToMb(cp, str, end - 1);
       if (!ptr) {
-        // Truncate name.  Could goto fail.
-        goto done;
+        DBG_FAIL_MACRO;
+        goto fail;
       }
       str = ptr;
     }

+ 1 - 1
src/ExFatLib/ExFatPartition.cpp

@@ -266,7 +266,7 @@ uint32_t ExFatPartition::freeClusterCount() {
   }
 }
 //------------------------------------------------------------------------------
-bool ExFatPartition::init(BlockDevice* dev, uint8_t part) {
+bool ExFatPartition::init(FsBlockDevice* dev, uint8_t part) {
   uint32_t volStart = 0;
   uint8_t* cache;
   pbs_t* pbs;

+ 16 - 6
src/ExFatLib/ExFatPartition.h

@@ -29,10 +29,13 @@
  * \brief ExFatPartition include file.
  */
 #include "../common/SysCall.h"
-#include "../common/BlockDevice.h"
+#include "../common/FsBlockDevice.h"
 #include "../common/FsCache.h"
 #include "../common/FsStructs.h"
-#include "ExFatConfig.h"
+/** Set EXFAT_READ_ONLY non-zero for read only */
+#ifndef EXFAT_READ_ONLY
+#define EXFAT_READ_ONLY 0
+#endif  // EXFAT_READ_ONLY
 /** Type for exFAT partition */
 const uint8_t FAT_TYPE_EXFAT = 64;
 
@@ -79,6 +82,13 @@ class ExFatPartition {
   uint32_t clusterCount() const {return m_clusterCount;}
   /** \return the cluster heap start sector. */
   uint32_t clusterHeapStartSector() const {return m_clusterHeapStartSector;}
+  /** End access to volume
+   * \return pointer to sector size buffer for format.
+   */
+  uint8_t* end() {
+    m_fatType = 0;
+    return cacheClear();
+  }
   /** \return the FAT length in sectors */
   uint32_t fatLength() const {return m_fatLength;}
   /** \return the FAT start sector number. */
@@ -96,9 +106,9 @@ class ExFatPartition {
    *
    * \return true for success or false for failure.
    */
-  bool init(BlockDevice* dev, uint8_t part);
+  bool init(FsBlockDevice* dev, uint8_t part);
   /**
-   * Check for BlockDevice busy.
+   * Check for device busy.
    *
    * \return true if busy else false.
    */
@@ -142,7 +152,7 @@ class ExFatPartition {
     return m_dataCache.prepare(sector, option);
 #endif  // USE_EXFAT_BITMAP_CACHE
   }
-  void cacheInit(BlockDevice* dev) {
+  void cacheInit(FsBlockDevice* dev) {
 #if USE_EXFAT_BITMAP_CACHE
     m_bitmapCache.init(dev);
 #endif  // USE_EXFAT_BITMAP_CACHE
@@ -213,7 +223,7 @@ class ExFatPartition {
   uint32_t m_rootDirectoryCluster;
   uint32_t m_clusterMask;
   uint32_t m_bytesPerCluster;
-  BlockDevice* m_blockDev;
+  FsBlockDevice* m_blockDev;
   uint8_t  m_fatType = 0;
   uint8_t  m_sectorsPerClusterShift;
 };

+ 1 - 1
src/ExFatLib/ExFatVolume.h

@@ -40,7 +40,7 @@ class ExFatVolume : public ExFatPartition {
    * \param[in] part partition to initialize.
    * \return true for success or false for failure.
    */
-  bool begin(BlockDevice* dev, bool setCwv = true, uint8_t part = 1) {
+  bool begin(FsBlockDevice* dev, bool setCwv = true, uint8_t part = 1) {
     if (!init(dev, part)) {
       return false;
     }

+ 1 - 9
src/FatLib/FatFile.h

@@ -377,7 +377,7 @@ class FatFile {
     return isOpen() ? m_error & WRITE_ERROR : true;
   }
   /**
-   * Check for BlockDevice busy.
+   * Check for device busy.
    *
    * \return true if busy else false.
    */
@@ -442,14 +442,6 @@ class FatFile {
    * \return true for success or false for failure.
    */
   bool mkdir(FatFile* dir, const char* path, bool pFlag = true);
-  /** No longer implemented due to Long File Names.
-   *
-   * Use getName(char* name, size_t size).
-   * \return a pointer to replacement suggestion.
-   */
-#ifndef DOXYGEN_SHOULD_SKIP_THIS
-  const char* __attribute__((error("use getName(name, size)"))) name();
-#endif  // DOXYGEN_SHOULD_SKIP_THIS
   /** Open a file in the volume root directory.
    *
    * \param[in] vol Volume where the file is located.

+ 2 - 2
src/FatLib/FatFileLFN.cpp

@@ -232,7 +232,7 @@ bool FatFile::makeUniqueSfn(FatLfn_t* fname) {
   const uint8_t FIRST_HASH_SEQ = 2;  // min value is 2
   uint8_t pos = fname->seqPos;
   DirFat_t* dir;
-  uint16_t hex;
+  uint16_t hex = 0;
 
   DBG_HALT_IF(!(fname->flags & FNAME_FLAG_LOST_CHARS));
   DBG_HALT_IF(fname->sfn[pos] != '~' && fname->sfn[pos + 1] != '1');
@@ -242,7 +242,7 @@ bool FatFile::makeUniqueSfn(FatLfn_t* fname) {
 #ifdef USE_LFN_HASH
     hex = Bernstein(fname->begin, fname->end, seq);
 #else
-    hex = micros();
+    hex += millis();
 #endif
     if (pos > 3) {
       // Make space in name for ~HHHH.

+ 2 - 2
src/FatLib/FatFileSFN.cpp

@@ -37,7 +37,7 @@ bool FatFile::open(FatFile* dirFile, FatSfn_t* fname, oflag_t oflag) {
   uint8_t checksum;
 #endif  // SFN_OPEN_USES_CHKSUM
   uint8_t lfnOrd = 0;
-  uint16_t emptyIndex;
+  uint16_t emptyIndex = 0;
   uint16_t index = 0;
   DirFat_t* dir;
   DirLfn_t* ldir;
@@ -53,7 +53,7 @@ bool FatFile::open(FatFile* dirFile, FatSfn_t* fname, oflag_t oflag) {
       // At EOF if no error.
       break;
     }
-    if (dir->name[0] == FAT_NAME_FREE || dir->name[0] == FAT_NAME_FREE) {
+    if (dir->name[0] == FAT_NAME_DELETED || dir->name[0] == FAT_NAME_FREE) {
       if (!emptyFound) {
         emptyIndex = index;
         emptyFound = true;

+ 1 - 1
src/FatLib/FatFormatter.cpp

@@ -45,7 +45,7 @@ const uint16_t FAT16_ROOT_SECTOR_COUNT =
 #define writeMsg(str) if (m_pr) m_pr->write(str)
 #endif  // PRINT_FORMAT_PROGRESS
 //------------------------------------------------------------------------------
-bool FatFormatter::format(BlockDevice* dev, uint8_t* secBuf, print_t* pr) {
+bool FatFormatter::format(FsBlockDevice* dev, uint8_t* secBuf, print_t* pr) {
   bool rtn;
   m_dev = dev;
   m_secBuf = secBuf;

+ 3 - 3
src/FatLib/FatFormatter.h

@@ -25,7 +25,7 @@
 #ifndef FatFormatter_h
 #define FatFormatter_h
 #include "../common/SysCall.h"
-#include "../common/BlockDevice.h"
+#include "../common/FsBlockDevice.h"
 /**
  * \class FatFormatter
  * \brief Format a FAT volume.
@@ -41,7 +41,7 @@ class FatFormatter {
    *
    * \return true for success or false for failure.
    */
-  bool format(BlockDevice* dev, uint8_t* secBuffer, print_t* pr = nullptr);
+  bool format(FsBlockDevice* dev, uint8_t* secBuffer, print_t* pr = nullptr);
 
  private:
   bool initFatDir(uint8_t fatType, uint32_t sectorCount);
@@ -56,7 +56,7 @@ class FatFormatter {
   uint32_t m_relativeSectors;
   uint32_t m_sectorCount;
   uint32_t m_totalSectors;
-  BlockDevice* m_dev;
+  FsBlockDevice* m_dev;
   print_t* m_pr;
   uint8_t* m_secBuf;
   uint16_t m_reservedSectorCount;

+ 14 - 10
src/FatLib/FatName.cpp

@@ -52,8 +52,7 @@ size_t FatFile::getName(char* name, size_t size) {
 size_t FatFile::getName7(char* name, size_t size) {
   FatFile dir;
   DirLfn_t* ldir;
-  char* ptr = name;
-  char* end = ptr + size;
+  size_t n = 0;
   if (!isOpen()) {
     DBG_FAIL_MACRO;
     goto fail;
@@ -78,15 +77,19 @@ size_t FatFile::getName7(char* name, size_t size) {
     }
     for (uint8_t i = 0; i < 13; i++) {
       uint16_t c = getLfnChar(ldir, i);
-      if (c == 0 || (ptr + 1) == end) {
+      if (c == 0) {
         goto done;
       }
-      *ptr++ = c >= 0X7F ? '?' : c;
+      if ((n + 1) >= size) {
+        DBG_FAIL_MACRO;
+        goto fail;
+      }
+      name[n++] = c >= 0X7F ? '?' : c;
     }
   }
  done:
-  *ptr = '\0';
-  return ptr - name;
+  name[n] = 0;
+  return n;
 
  fail:
   name[0] = '\0';
@@ -147,8 +150,8 @@ size_t FatFile::getName8(char* name, size_t size) {
       // Save space for zero byte.
       ptr = FsUtf::cpToMb(cp, str, end - 1);
       if (!ptr) {
-        // Truncate name.  Could goto fail.
-        goto done;
+        DBG_FAIL_MACRO;
+        goto fail;
       }
       str = ptr;
     }
@@ -205,8 +208,9 @@ size_t FatFile::getSFN(char* name, size_t size) {
         continue;
       }
     }
-    if ((j + 1u) == size) {
-      break;
+    if ((j + 1u) >= size) {
+      DBG_FAIL_MACRO;
+      goto fail;
     }
     name[j++] = c;
   }

+ 1 - 1
src/FatLib/FatPartition.cpp

@@ -390,7 +390,7 @@ int32_t FatPartition::freeClusterCount() {
   return -1;
 }
 //------------------------------------------------------------------------------
-bool FatPartition::init(BlockDevice* dev, uint8_t part) {
+bool FatPartition::init(FsBlockDevice* dev, uint8_t part) {
   uint32_t clusterCount;
   uint32_t totalSectors;
   uint32_t volumeStartSector = 0;

+ 12 - 5
src/FatLib/FatPartition.h

@@ -30,7 +30,7 @@
  */
 #include <stddef.h>
 #include "../common/SysCall.h"
-#include "../common/BlockDevice.h"
+#include "../common/FsBlockDevice.h"
 #include "../common/FsCache.h"
 #include "../common/FsStructs.h"
 
@@ -107,6 +107,13 @@ class FatPartition {
   uint32_t dataStartSector() const {
     return m_dataStartSector;
   }
+  /** End access to volume
+   * \return pointer to sector size buffer for format.
+   */
+  uint8_t* end() {
+    m_fatType = 0;
+    return cacheClear();
+  }
   /** \return The number of File Allocation Tables. */
   uint8_t fatCount() const {
     return 2;
@@ -126,7 +133,7 @@ class FatPartition {
   int32_t freeClusterCount();
   /** Initialize a FAT partition.
    *
-   * \param[in] dev BlockDevice for this partition.
+   * \param[in] dev FsBlockDevice for this partition.
    * \param[in] part The partition to be used.  Legal values for \a part are
    * 1-4 to use the corresponding partition on a device formatted with
    * a MBR, Master Boot Record, or zero if the device is formatted as
@@ -134,7 +141,7 @@ class FatPartition {
    *
    * \return true for success or false for failure.
    */
-  bool init(BlockDevice* dev, uint8_t part = 1);
+  bool init(FsBlockDevice* dev, uint8_t part = 1);
   /** \return The number of entries in the root directory for FAT16 volumes. */
   uint16_t rootDirEntryCount() const {
     return m_rootDirEntryCount;
@@ -158,7 +165,7 @@ class FatPartition {
     return fatGet(n, v);
   }
   /**
-   * Check for BlockDevice busy.
+   * Check for FsBlockDevice busy.
    *
    * \return true if busy else false.
    */
@@ -179,7 +186,7 @@ class FatPartition {
   static const uint16_t m_bytesPerSector = 1 << m_bytesPerSectorShift;
   static const uint16_t m_sectorMask = m_bytesPerSector - 1;
   //----------------------------------------------------------------------------
-  BlockDevice* m_blockDev;            // sector device
+  FsBlockDevice* m_blockDev;            // sector device
   uint8_t  m_sectorsPerCluster;       // Cluster size in sectors.
   uint8_t  m_clusterSectorMask;       // Mask to extract sector of cluster.
   uint8_t  m_sectorsPerClusterShift;  // Cluster count to sector count shift.

+ 1 - 2
src/FatLib/FatVolume.h

@@ -43,7 +43,7 @@ class FatVolume : public  FatPartition {
    * \param[in] part partition to initialize.
    * \return true for success or false for failure.
    */
-  bool begin(BlockDevice* dev, bool setCwv = true, uint8_t part = 1) {
+  bool begin(FsBlockDevice* dev, bool setCwv = true, uint8_t part = 1) {
     if (!init(dev, part)) {
       return false;
     }
@@ -72,7 +72,6 @@ class FatVolume : public  FatPartition {
    * \return true for success or false for failure.
    */
   bool chdir(const char *path);
-
   //----------------------------------------------------------------------------
   /**
    * Test for the existence of a file.

+ 9 - 9
src/FsLib/FsFile.h

@@ -239,7 +239,7 @@ class FsBaseFile {
            m_xFile ? m_xFile->getWriteError() : true;
   }
   /**
-   * Check for BlockDevice busy.
+   * Check for FsBlockDevice busy.
    *
    * \return true if busy else false.
    */
@@ -355,14 +355,6 @@ class FsBaseFile {
    * \return true for success or false for failure.
    */
   bool mkdir(FsBaseFile* dir, const char* path, bool pFlag = true);
-  /** No longer implemented due to Long File Names.
-   *
-   * Use getName(char* name, size_t size).
-   * \return a pointer to replacement suggestion.
-   */
-#ifndef DOXYGEN_SHOULD_SKIP_THIS
-  const char* __attribute__((error("use getName(name, size)"))) name();
-#endif  // DOXYGEN_SHOULD_SKIP_THIS
   /** Open a file or directory by name.
    *
    * \param[in] dir An open file instance for the directory containing
@@ -761,6 +753,14 @@ class FsBaseFile {
     return m_fFile ? length < (1ULL << 32) && m_fFile->truncate(length) :
            m_xFile ? m_xFile->truncate(length) : false;
   }
+  /** Write a string to a file. Used by the Arduino Print class.
+   * \param[in] str Pointer to the string.
+   * Use getWriteError to check for errors.
+   * \return count of characters written for success or -1 for failure.
+   */
+  size_t write(const char* str) {
+    return write(str, strlen(str));
+  }
   /** Write a byte to a file. Required by the Arduino Print class.
    * \param[in] b the byte to be written.
    * Use getWriteError to check for errors.

+ 57 - 0
src/FsLib/FsFormatter.h

@@ -0,0 +1,57 @@
+/**
+ * Copyright (c) 2011-2021 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 FsFormatter_h
+#define FsFormatter_h
+#include "FatLib/FatLib.h"
+#include "ExFatLib/ExFatLib.h"
+/**
+ * \class FsFormatter
+ * \brief Format a exFAT/FAT volume.
+ */
+class FsFormatter {
+ public:
+  /**
+   * Format a FAT volume.
+   *
+   * \param[in] dev Block device for volume.
+   * \param[in] secBuffer buffer for writing to volume.
+   * \param[in] pr Print device for progress output.
+   *
+   * \return true for success or false for failure.
+   */
+  bool format(FsBlockDevice* dev, uint8_t* secBuffer, print_t* pr = nullptr) {
+    uint32_t sectorCount = dev->sectorCount();
+    if (sectorCount == 0) {
+      return false;
+    }
+    return sectorCount <= 67108864 ?
+      m_fFmt.format(dev, secBuffer, pr) :
+      m_xFmt.format(dev, secBuffer, pr);
+  }
+ private:
+  FatFormatter m_fFmt;
+  ExFatFormatter m_xFmt;
+};
+#endif  // FsFormatter_h

+ 1 - 0
src/FsLib/FsLib.h

@@ -30,4 +30,5 @@
  */
 #include "FsVolume.h"
 #include "FsFile.h"
+#include "FsFormatter.h"
 #endif  // FsLib_h

+ 6 - 4
src/FsLib/FsVolume.cpp

@@ -25,16 +25,16 @@
 #include "FsLib.h"
 FsVolume* FsVolume::m_cwv = nullptr;
 //------------------------------------------------------------------------------
-bool FsVolume::begin(BlockDevice* blockDev) {
+bool FsVolume::begin(FsBlockDevice* blockDev, bool setCwv, uint8_t part) {
   m_blockDev = blockDev;
   m_fVol = nullptr;
   m_xVol = new (m_volMem) ExFatVolume;
-  if (m_xVol && m_xVol->begin(m_blockDev, false)) {
+  if (m_xVol && m_xVol->begin(m_blockDev, false, part)) {
     goto done;
   }
   m_xVol = nullptr;
   m_fVol = new (m_volMem) FatVolume;
-  if (m_fVol && m_fVol->begin(m_blockDev, false)) {
+  if (m_fVol && m_fVol->begin(m_blockDev, false, part)) {
     goto done;
   }
   m_cwv = nullptr;
@@ -42,7 +42,9 @@ bool FsVolume::begin(BlockDevice* blockDev) {
   return false;
 
  done:
-  m_cwv = this;
+  if (setCwv || !m_cwv) {
+    m_cwv = this;
+  }
   return true;
 }
 //------------------------------------------------------------------------------

+ 11 - 5
src/FsLib/FsVolume.h

@@ -46,9 +46,11 @@ class FsVolume {
   /**
    * Initialize an FatVolume object.
    * \param[in] blockDev Device block driver.
+   * \param[in] setCwv Set current working volume if true.
+   * \param[in] part partition to initialize.
    * \return true for success or false for failure.
    */
-  bool begin(BlockDevice* blockDev);
+  bool begin(FsBlockDevice* blockDev, bool setCwv = true, uint8_t part = 1);
 #ifndef DOXYGEN_SHOULD_SKIP_THIS
   uint32_t __attribute__((error("use sectorsPerCluster()"))) blocksPerCluster();
 #endif  // DOXYGEN_SHOULD_SKIP_THIS
@@ -86,10 +88,14 @@ class FsVolume {
     return m_fVol ? m_fVol->dataStartSector() :
            m_xVol ? m_xVol->clusterHeapStartSector() : 0;
   }
-  /** free dynamic memory and end access to volume */
-  void end() {
+  /** End access to volume
+   * \return pointer to sector size buffer for format.
+   */
+  uint8_t* end() {
     m_fVol = nullptr;
     m_xVol = nullptr;
+    static_assert(sizeof(m_volMem) >= 512, "m_volMem too small");
+    return reinterpret_cast<uint8_t*>(m_volMem);
   }
   /** Test for the existence of a file in a directory
    *
@@ -119,7 +125,7 @@ class FsVolume {
            m_xVol ? m_xVol->freeClusterCount() : 0;
   }
   /**
-   * Check for BlockDevice busy.
+   * Check for device busy.
    *
    * \return true if busy else false.
    */
@@ -379,6 +385,6 @@ class FsVolume {
   static FsVolume* m_cwv;
   FatVolume*   m_fVol = nullptr;
   ExFatVolume* m_xVol = nullptr;
-  BlockDevice* m_blockDev;
+  FsBlockDevice* m_blockDev;
 };
 #endif  // FsVolume_h

+ 16 - 2
src/SdCard/SdCardInterface.h

@@ -24,14 +24,16 @@
  */
 #ifndef SdCardInterface_h
 #define SdCardInterface_h
-#include "../common/BlockDeviceInterface.h"
+#include "../common/FsBlockDeviceInterface.h"
 #include "SdCardInfo.h"
 /**
  * \class SdCardInterface
  * \brief Abstract interface for an SD card.
  */
-class SdCardInterface : public BlockDeviceInterface {
+class SdCardInterface : public FsBlockDeviceInterface {
  public:
+  /** end use of card */
+  virtual void end() = 0;
    /** Erase a range of sectors.
    *
    * \param[in] firstSector The address of the first sector in the range.
@@ -46,6 +48,18 @@ class SdCardInterface : public BlockDeviceInterface {
   virtual uint32_t errorData() const = 0;
   /** \return true if card is busy. */
   virtual bool isBusy() = 0;
+  /** \return false by default */
+  virtual bool hasDedicatedSpi() {return false;}
+  /** \return false by default */
+  bool virtual isDedicatedSpi() {return false;}
+  /** Set SPI sharing state
+   * \param[in] value desired state.
+   * \return false by default.
+   */
+  virtual bool setDedicatedSpi(bool value) {
+    (void)value;
+    return false;
+  }
   /**
    * Read a card's CID register.
    *

+ 13 - 6
src/SdCard/SdSpiCard.cpp

@@ -665,7 +665,7 @@ bool DedicatedSpiCard::begin(SdSpiConfig spiConfig) {
   if (!SharedSpiCard::begin(spiConfig)) {
     return false;
   }
-  m_sharedSpi = spiOptionShared(spiConfig.options);
+  m_dedicatedSpi = spiOptionDedicated(spiConfig.options);
   return true;
 }
 //------------------------------------------------------------------------------
@@ -687,18 +687,25 @@ bool DedicatedSpiCard::readSectors(
     }
   }
   m_curSector += ns;
-  return m_sharedSpi ? readStop() : true;
+  return m_dedicatedSpi ? true : readStop();
 
  fail:
   return false;
 }
 //------------------------------------------------------------------------------
+bool DedicatedSpiCard::setDedicatedSpi(bool value) {
+  if (!syncDevice()) {
+    return false;
+  }
+  m_dedicatedSpi = value;
+  return true;
+}
+//------------------------------------------------------------------------------
 bool DedicatedSpiCard::writeSector(uint32_t sector, const uint8_t* src) {
-  if (m_sharedSpi) {
-    return SharedSpiCard::writeSector(sector, src);
-  } else {
+  if (m_dedicatedSpi) {
     return writeSectors(sector, src, 1);
   }
+  return SharedSpiCard::writeSector(sector, src);
 }
 //------------------------------------------------------------------------------
 bool DedicatedSpiCard::writeSectors(
@@ -715,7 +722,7 @@ bool DedicatedSpiCard::writeSectors(
     }
   }
   m_curSector += ns;
-  return m_sharedSpi ? writeStop() : true;
+  return m_dedicatedSpi ? true : writeStop();
 
 fail:
   return false;

+ 33 - 4
src/SdCard/SdSpiCard.h

@@ -41,7 +41,7 @@
 #if HAS_SDIO_CLASS
 class SharedSpiCard : public SdCardInterface {
 #elif USE_BLOCK_DEVICE_INTERFACE
-class SharedSpiCard : public BlockDeviceInterface {
+class SharedSpiCard : public FsBlockDeviceInterface {
 #else  // HAS_SDIO_CLASS
 class SharedSpiCard {
 #endif  // HAS_SDIO_CLASS
@@ -59,6 +59,10 @@ class SharedSpiCard {
    * \return true for success or false for failure.
    */
   bool begin(SdSpiConfig spiConfig);
+  /** End use of card */
+  void end() {
+    spiEnd();
+  }
   /** Erase a range of sectors.
    *
    * \param[in] firstSector The address of the first sector in the range.
@@ -96,12 +100,16 @@ class SharedSpiCard {
   uint32_t errorData() const {
     return m_status;
   }
+  /** \return false for shared class. */
+  bool hasDedicatedSpi() {return false;}
   /**
    * Check for busy.  MISO low indicates the card is busy.
    *
    * \return true if busy else false.
    */
   bool isBusy();
+  /** \return false, can't be in dedicated state. */
+  bool isDedicatedSpi() {return false;}
   /**
    * Read a card's CID register. The CID contains card identification
    * information such as Manufacturer ID, Product name, Product serial
@@ -189,6 +197,14 @@ class SharedSpiCard {
   // Use sectorCount(). cardSize() will be removed in the future.
   uint32_t __attribute__((error("use sectorCount()"))) cardSize();
 #endif  // DOXYGEN_SHOULD_SKIP_THIS
+  /** Set SPI sharing state
+   * \param[in] value desired state.
+   * \return false for shared card
+   */
+  bool setDedicatedSpi(bool value) {
+    (void)value;
+    return false;
+  }
   /** end a mult-sector transfer.
    *
    * \return true for success or false for failure.
@@ -263,7 +279,6 @@ class SharedSpiCard {
   }
   bool waitReady(uint16_t ms);
   bool writeData(uint8_t token, const uint8_t* src);
-
 #if SPI_DRIVER_SELECT < 2
   void spiActivate() {
     m_spiDriver.activate();
@@ -274,6 +289,9 @@ class SharedSpiCard {
   void spiDeactivate() {
     m_spiDriver.deactivate();
   }
+  void spiEnd() {
+    m_spiDriver.end();
+  }
   uint8_t spiReceive() {
     return m_spiDriver.receive();
   }
@@ -300,6 +318,9 @@ class SharedSpiCard {
   void spiDeactivate() {
     m_spiDriverPtr->deactivate();
   }
+  void spiEnd() {
+    m_spiDriverPtr->end();
+  }
   uint8_t spiReceive() {
     return m_spiDriverPtr->receive();
   }
@@ -317,7 +338,6 @@ class SharedSpiCard {
   }
   SdSpiDriver* m_spiDriverPtr;
 #endif  // SPI_DRIVER_SELECT < 2
-
   SdCsPin_t m_csPin;
   uint8_t m_errorCode = SD_CARD_ERROR_INIT_NOT_CALLED;
   bool    m_spiActive;
@@ -340,6 +360,10 @@ class DedicatedSpiCard : public SharedSpiCard {
    * \return true for success or false for failure.
    */
   bool begin(SdSpiConfig spiConfig);
+  /** \return true, can be in dedicaded state. */
+  bool hasDedicatedSpi() {return true;}
+  /** \return true if in dedicated SPI state. */
+  bool isDedicatedSpi() {return m_dedicatedSpi;}
   /**
    * Read a 512 byte sector from an SD card.
    *
@@ -357,6 +381,11 @@ class DedicatedSpiCard : public SharedSpiCard {
    * \return true for success or false for failure.
    */
   bool readSectors(uint32_t sector, uint8_t* dst, size_t ns);
+  /** Set SPI sharing state
+   * \param[in] value desired state.
+   * \return true for success else false;
+   */
+  bool setDedicatedSpi(bool value);
   /**
    * Write a 512 byte sector to an SD card.
    *
@@ -377,7 +406,7 @@ class DedicatedSpiCard : public SharedSpiCard {
 
  private:
   uint32_t m_curSector;
-  bool m_sharedSpi = true;
+  bool m_dedicatedSpi = false;
 };
 //==============================================================================
 #if ENABLE_DEDICATED_SPI

+ 2 - 2
src/SdCard/SdioCard.h

@@ -61,9 +61,9 @@ class SdioCard : public SdCardInterface {
    */
   bool begin(SdioConfig sdioConfig);
   /** Disable an SDIO card.
-   * \return false - not implemented.
+   * not implemented.
    */
-  bool end() {return false;}
+  void end() {}
 
 #ifndef DOXYGEN_SHOULD_SKIP_THIS
     uint32_t __attribute__((error("use sectorCount()"))) cardSize();

+ 1 - 2
src/SdCard/SdioTeensy.cpp

@@ -572,7 +572,6 @@ static bool transferStop() {
   if (!cardCommand(CMD12_XFERTYP, 0)) {
     return sdError(SD_CARD_ERROR_CMD12);
   }
-//  if (yieldTimeout(isBusyCMD13)) {
   if (yieldTimeout(isBusyDat)) {
     return sdError(SD_CARD_ERROR_CMD13);
   }
@@ -599,7 +598,7 @@ static bool yieldTimeout(bool (*fcn)()) {
       m_busyFcn = 0;
       return true;
     }
-    SysCall::yield();
+    yield();
   }
   m_busyFcn = 0;
   return false;  // Caller will set errorCode.

+ 75 - 61
src/SdFat.h

@@ -38,15 +38,15 @@
 #endif  // INCLUDE_SDIOS
 //------------------------------------------------------------------------------
 /** SdFat version for cpp use. */
-#define SD_FAT_VERSION 20101
+#define SD_FAT_VERSION 20102
 /** SdFat version as string. */
-#define SD_FAT_VERSION_STR "2.1.1"
+#define SD_FAT_VERSION_STR "2.1.2"
 //==============================================================================
 /**
  * \class SdBase
  * \brief base SD file system template class.
  */
-template <class Vol>
+template <class Vol, class Fmt>
 class SdBase : public Vol {
  public:
   //----------------------------------------------------------------------------
@@ -115,6 +115,14 @@ class SdBase : public Vol {
     return m_card && !m_card->errorCode();
   }
   //----------------------------------------------------------------------------
+  /** End use of card. */
+  void end() {
+    Vol::end();
+    if (m_card) {
+      m_card->end();
+    }
+  }
+  //----------------------------------------------------------------------------
   /** %Print error info and halt.
    *
    * \param[in] pr Print destination.
@@ -128,7 +136,7 @@ class SdBase : public Vol {
     } else if (!Vol::fatType()) {
       pr->println(F("Check SD format."));
     }
-    SysCall::halt();
+    while (true) {}
   }
   //----------------------------------------------------------------------------
   /** %Print error info and halt.
@@ -153,13 +161,51 @@ class SdBase : public Vol {
     errorHalt(pr);
   }
   //----------------------------------------------------------------------------
+  /** Format SD card
+   *
+   * \param[in] pr Print destination.
+   * \return true for success else false.
+   */
+  bool format(print_t* pr = nullptr) {
+    Fmt fmt;
+    uint8_t* mem = Vol::end();
+    if (!mem) {
+      return false;
+    }
+    bool switchSpi = hasDedicatedSpi() && !isDedicatedSpi();
+    if (switchSpi && !setDedicatedSpi(true)) {
+      return 0;
+    }
+    bool rtn = fmt.format(card(), mem, pr);
+    if (switchSpi && !setDedicatedSpi(false)) {
+      return 0;
+    }
+    return rtn;
+  }
+  //----------------------------------------------------------------------------
+  /** \return the free cluster count. */
+  uint32_t freeClusterCount() {
+    bool switchSpi = hasDedicatedSpi() && !isDedicatedSpi();
+    if (switchSpi && !setDedicatedSpi(true)) {
+      return 0;
+    }
+    uint32_t rtn = Vol::freeClusterCount();
+    if (switchSpi && !setDedicatedSpi(false)) {
+      return 0;
+    }
+    return rtn;
+  }
+  //----------------------------------------------------------------------------
+  /** \return true if can be in dedicated SPI state */
+  bool hasDedicatedSpi() {return m_card ? m_card->hasDedicatedSpi() : false;}
+  //----------------------------------------------------------------------------
   /** %Print error info and halt.
    *
    * \param[in] pr Print destination.
    */
   void initErrorHalt(print_t* pr) {
     initErrorPrint(pr);
-    SysCall::halt();
+    while (true) {}
   }
   //----------------------------------------------------------------------------
   /** %Print error info and halt.
@@ -177,7 +223,7 @@ class SdBase : public Vol {
    * \param[in] pr Print destination.
    * \param[in] msg Message to print.
    */
-  void initErrorHalt(Print* pr, const __FlashStringHelper* msg) {
+  void initErrorHalt(print_t* pr, const __FlashStringHelper* msg) {
     pr->println(msg);
     initErrorHalt(pr);
   }
@@ -186,7 +232,7 @@ class SdBase : public Vol {
    *
    * \param[in] pr Print destination.
    */
-  void initErrorPrint(Print* pr) {
+  void initErrorPrint(print_t* pr) {
     pr->println(F("begin() failed"));
     if (sdErrorCode()) {
       pr->println(F("Do not reformat the SD."));
@@ -197,6 +243,9 @@ class SdBase : public Vol {
     errorPrint(pr);
   }
   //----------------------------------------------------------------------------
+  /** \return true if in dedicated SPI state. */
+  bool isDedicatedSpi() {return m_card ? m_card->isDedicatedSpi() : false;}
+  //----------------------------------------------------------------------------
   /** %Print volume FAT/exFAT type.
    *
    * \param[in] pr Print destination.
@@ -241,7 +290,7 @@ class SdBase : public Vol {
    * \param[in] pr Print destination.
    * \param[in] msg Message to print.
    */
-  void errorPrint(Print* pr, const __FlashStringHelper* msg) {
+  void errorPrint(print_t* pr, const __FlashStringHelper* msg) {
     pr->print(F("error: "));
     pr->println(msg);
     errorPrint(pr);
@@ -278,6 +327,17 @@ class SdBase : public Vol {
   /** \return SD card error data. */
   uint8_t sdErrorData() {return m_card ? m_card->errorData() : 0;}
   //----------------------------------------------------------------------------
+  /** Set SPI sharing state
+   * \param[in] value desired state.
+   * \return true for success else false;
+   */
+  bool setDedicatedSpi(bool value) {
+    if (m_card) {
+      return m_card->setDedicatedSpi(value);
+    }
+    return false;
+  }
+  //----------------------------------------------------------------------------
   /** \return pointer to base volume */
   Vol* vol() {return reinterpret_cast<Vol*>(this);}
   //----------------------------------------------------------------------------
@@ -341,7 +401,7 @@ class SdBase : public Vol {
 #endif  // ENABLE_ARDUINO_SERIAL
   //----------------------------------------------------------------------------
  private:
-  SdCard* m_card;
+  SdCard* m_card = nullptr;
   SdCardFactory m_cardFactory;
 };
 //------------------------------------------------------------------------------
@@ -349,73 +409,27 @@ class SdBase : public Vol {
  * \class SdFat32
  * \brief SD file system class for FAT volumes.
  */
-class SdFat32 : public SdBase<FatVolume> {
+class SdFat32 : public SdBase<FatVolume, FatFormatter> {
  public:
-  /** Format a SD card FAT32/FAT16.
-   *
-   * \param[in] pr Optional Print information.
-   * \return true for success or false for failure.
-   */
-  bool format(print_t* pr = nullptr) {
-    FatFormatter fmt;
-    uint8_t* cache = cacheClear();
-    if (!cache) {
-      return false;
-    }
-    return fmt.format(card(), cache, pr);
-  }
 };
 //------------------------------------------------------------------------------
 /**
  * \class SdExFat
  * \brief SD file system class for exFAT volumes.
  */
-class SdExFat : public SdBase<ExFatVolume> {
+class SdExFat : public SdBase<ExFatVolume, ExFatFormatter> {
  public:
-  /** Format a SD card exFAT.
-   *
-   * \param[in] pr Optional Print information.
-   * \return true for success or false for failure.
-   */
-  bool format(print_t* pr = nullptr) {
-    ExFatFormatter fmt;
-    uint8_t* cache = cacheClear();
-    if (!cache) {
-      return false;
-    }
-    return fmt.format(card(), cache, pr);
-  }
 };
 //------------------------------------------------------------------------------
 /**
  * \class SdFs
  * \brief SD file system class for FAT16, FAT32, and exFAT volumes.
  */
-class SdFs : public SdBase<FsVolume> {
+class SdFs : public SdBase<FsVolume, FsFormatter> {
  public:
-  /** Format a SD card FAT or exFAT.
-   *
-   * \param[in] pr Optional Print information.
-   * \return true for success or false for failure.
-   */
-  bool format(print_t* pr = nullptr) {
-    static_assert(sizeof(m_volMem) >= 512, "m_volMem too small");
-    uint32_t sectorCount = card()->sectorCount();
-    if (sectorCount == 0) {
-      return false;
-    }
-    end();
-    if (sectorCount > 67108864) {
-      ExFatFormatter fmt;
-      return fmt.format(card(), reinterpret_cast<uint8_t*>(m_volMem), pr);
-    } else {
-      FatFormatter fmt;
-      return fmt.format(card(), reinterpret_cast<uint8_t*>(m_volMem), pr);
-    }
-  }
 };
 //------------------------------------------------------------------------------
-#if SDFAT_FILE_TYPE == 1
+#if SDFAT_FILE_TYPE == 1 || defined(DOXYGEN)
 /** Select type for SdFat. */
 typedef SdFat32 SdFat;
 /** Select type for SdBaseFile. */
@@ -435,11 +449,11 @@ typedef FsBaseFile SdBaseFile;
 #if defined __has_include
 #if __has_include(<FS.h>)
 #define HAS_INCLUDE_FS_H
-#warning File not defined because __has__include(FS.h)
+#warning File not defined because __has_include(FS.h)
 #endif  // __has_include(<FS.h>)
 #endif  // defined __has_include
 #ifndef HAS_INCLUDE_FS_H
-#if SDFAT_FILE_TYPE == 1
+#if SDFAT_FILE_TYPE == 1 || defined(DOXYGEN)
 /** Select type for File. */
 typedef File32 File;
 #elif SDFAT_FILE_TYPE == 2

+ 3 - 16
src/SdFatConfig.h

@@ -137,10 +137,10 @@
 #endif  // SD_MAX_INIT_RATE_KHZ
 /**
  * Set USE_BLOCK_DEVICE_INTERFACE nonzero to use a generic block device.
- * This allow use of an external BlockDevice driver that is derived from
- * the BlockDeviceInterface like this:
+ * This allow use of an external FsBlockDevice driver that is derived from
+ * the FsBlockDeviceInterface like this:
  *
- * class UsbMscDriver : public BlockDeviceInterface {
+ * class UsbMscDriver : public FsBlockDeviceInterface {
  *   ... code for USB mass storage class driver.
  * };
  *
@@ -341,19 +341,6 @@ typedef uint8_t SdCsPin_t;
 #define ENDL_CALLS_FLUSH 0
 #endif  // ENDL_CALLS_FLUSH
 //------------------------------------------------------------------------------
-/**
- * Handle Watchdog Timer for WiFi modules.
- *
- * Yield will be called before accessing the SPI bus if it has been more
- * than WDT_YIELD_TIME_MILLIS milliseconds since the last yield call by SdFat.
- */
-#if defined(PLATFORM_ID) || defined(ESP8266)
-// If Particle device or ESP8266 call yield.
-#define WDT_YIELD_TIME_MILLIS 100
-#else  // defined(PLATFORM_ID) || defined(ESP8266)
-#define WDT_YIELD_TIME_MILLIS 0
-#endif  // defined(PLATFORM_ID) || defined(ESP8266)
-//------------------------------------------------------------------------------
 /**
  * Set USE_SIMPLE_LITTLE_ENDIAN nonzero for little endian processors
  * with no memory alignment restrictions.

+ 4 - 0
src/SpiDriver/SdSpiArtemis.cpp

@@ -42,6 +42,10 @@ void SdSpiArduinoDriver::deactivate() {
   m_spi->endTransaction();
 }
 //------------------------------------------------------------------------------
+void SdSpiArduinoDriver::end() {
+  m_spi->end();
+}
+//------------------------------------------------------------------------------
 uint8_t SdSpiArduinoDriver::receive() {
   return m_spi->transfer(0XFF);
 }

+ 8 - 4
src/SpiDriver/SdSpiAvr.h

@@ -27,19 +27,23 @@
 // Use of in-line for AVR to save flash.
 #define nop asm volatile ("nop\n\t")
 //------------------------------------------------------------------------------
+inline void SdSpiArduinoDriver::activate() {
+  SPI.beginTransaction(m_spiSettings);
+}
+//------------------------------------------------------------------------------
 inline void SdSpiArduinoDriver::begin(SdSpiConfig spiConfig) {
   (void)spiConfig;
   SPI.begin();
 }
 //------------------------------------------------------------------------------
-inline void SdSpiArduinoDriver::activate() {
-  SPI.beginTransaction(m_spiSettings);
-}
-//------------------------------------------------------------------------------
 inline void SdSpiArduinoDriver::deactivate() {
   SPI.endTransaction();
 }
 //------------------------------------------------------------------------------
+inline void SdSpiArduinoDriver::end() {
+  SPI.end();
+}
+//------------------------------------------------------------------------------
 inline uint8_t SdSpiArduinoDriver::receive() {
   return SPI.transfer(0XFF);
 }

+ 4 - 4
src/SpiDriver/SdSpiBareUnoDriver.h

@@ -92,10 +92,6 @@ class SdSpiDriverBareUno {
  public:
   /** Activate SPI hardware. */
   void activate() {}
-  /** deactivate SPI driver. */
-  void end() {}
-  /** Deactivate SPI hardware. */
-  void deactivate() {}
   /** Initialize the SPI bus.
    *
    * \param[in] spiConfig SD card configuration.
@@ -112,6 +108,10 @@ class SdSpiDriverBareUno {
     unoPinMode(UNO_SCK, OUTPUT);
     unoPinMode(UNO_MOSI, OUTPUT);
   }
+  /** Deactivate SPI hardware. */
+  void deactivate() {}
+  /** deactivate SPI driver. */
+  void end() {}
   /** Receive a byte.
    *
    * \return The byte.

+ 2 - 0
src/SpiDriver/SdSpiBaseClass.h

@@ -43,6 +43,8 @@ class SdSpiBaseClass {
   virtual void begin(SdSpiConfig config) = 0;
   /** Deactivate SPI hardware. */
   virtual void deactivate() {}
+  /** deactivate SPI driver. */
+  virtual void end() {}
   /** Receive a byte.
    *
    * \return The byte.

+ 4 - 5
src/SpiDriver/SdSpiDriver.h

@@ -50,16 +50,15 @@ const uint8_t SHARED_SPI = 0;
 const uint8_t DEDICATED_SPI = 1;
 /**
  * \param[in] opt option field of SdSpiConfig.
- * \return true for shared SPI.
+ * \return true for dedicated SPI.
  */
-inline bool spiOptionShared(uint8_t opt) {return !(opt & DEDICATED_SPI);}
-
+inline bool spiOptionDedicated(uint8_t opt) {return opt & DEDICATED_SPI;}
 #else  // ENABLE_DEDICATED_SPI
 /**
  * \param[in] opt option field of SdSpiConfig.
- * \return true for shared SPI.
+ * \return true for dedicated SPI.
  */
-inline bool spiOptionShared(uint8_t opt) {(void)opt; return true;}
+inline bool spiOptionDedicated(uint8_t opt) {(void)opt; return false;}
 #endif  // ENABLE_DEDICATED_SPI
 //------------------------------------------------------------------------------
 /** SPISettings for SCK frequency in Hz. */

+ 23 - 19
src/SpiDriver/SdSpiDue.cpp

@@ -62,25 +62,6 @@ static bool dmac_channel_transfer_done(uint32_t ul_num) {
   return (DMAC->DMAC_CHSR & (DMAC_CHSR_ENA0 << ul_num)) ? false : true;
 }
 //------------------------------------------------------------------------------
-void SdSpiArduinoDriver::begin(SdSpiConfig spiConfig) {
-  (void)spiConfig;
-  SPI.begin();
-#if USE_SAM3X_DMAC
-  pmc_enable_periph_clk(ID_DMAC);
-  dmac_disable();
-  DMAC->DMAC_GCFG = DMAC_GCFG_ARB_CFG_FIXED;
-  dmac_enable();
-#if USE_SAM3X_BUS_MATRIX_FIX
-  MATRIX->MATRIX_WPMR = 0x4d415400;
-  MATRIX->MATRIX_MCFG[1] = 1;
-  MATRIX->MATRIX_MCFG[2] = 1;
-  MATRIX->MATRIX_SCFG[0] = 0x01000010;
-  MATRIX->MATRIX_SCFG[1] = 0x01000010;
-  MATRIX->MATRIX_SCFG[7] = 0x01000010;
-#endif  // USE_SAM3X_BUS_MATRIX_FIX
-#endif  // USE_SAM3X_DMAC
-}
-//------------------------------------------------------------------------------
 // start RX DMA
 static void spiDmaRX(uint8_t* dst, uint16_t count) {
   dmac_channel_disable(SPI_DMAC_RX_CH);
@@ -141,10 +122,33 @@ void SdSpiArduinoDriver::activate() {
   pSpi->SPI_CR |= SPI_CR_SPIEN;
 }
 //------------------------------------------------------------------------------
+void SdSpiArduinoDriver::begin(SdSpiConfig spiConfig) {
+  (void)spiConfig;
+  SPI.begin();
+#if USE_SAM3X_DMAC
+  pmc_enable_periph_clk(ID_DMAC);
+  dmac_disable();
+  DMAC->DMAC_GCFG = DMAC_GCFG_ARB_CFG_FIXED;
+  dmac_enable();
+#if USE_SAM3X_BUS_MATRIX_FIX
+  MATRIX->MATRIX_WPMR = 0x4d415400;
+  MATRIX->MATRIX_MCFG[1] = 1;
+  MATRIX->MATRIX_MCFG[2] = 1;
+  MATRIX->MATRIX_SCFG[0] = 0x01000010;
+  MATRIX->MATRIX_SCFG[1] = 0x01000010;
+  MATRIX->MATRIX_SCFG[7] = 0x01000010;
+#endif  // USE_SAM3X_BUS_MATRIX_FIX
+#endif  // USE_SAM3X_DMAC
+}
+//------------------------------------------------------------------------------
 void SdSpiArduinoDriver::deactivate() {
   SPI.endTransaction();
 }
 //------------------------------------------------------------------------------
+void SdSpiArduinoDriver::end() {
+  SPI.end();
+}
+//------------------------------------------------------------------------------
 static inline uint8_t spiTransfer(uint8_t b) {
   Spi* pSpi = SPI0;
 

+ 4 - 0
src/SpiDriver/SdSpiESP.cpp

@@ -48,6 +48,10 @@ void SdSpiArduinoDriver::deactivate() {
   m_spi->endTransaction();
 }
 //------------------------------------------------------------------------------
+void SdSpiArduinoDriver::end() {
+  m_spi->end();
+}
+//------------------------------------------------------------------------------
 uint8_t SdSpiArduinoDriver::receive() {
   return m_spi->transfer(0XFF);
 }

+ 4 - 0
src/SpiDriver/SdSpiLibDriver.h

@@ -46,6 +46,10 @@ inline void SdSpiArduinoDriver::begin(SdSpiConfig spiConfig) {
   m_spi->begin();
 }
 //------------------------------------------------------------------------------
+inline void SdSpiArduinoDriver::end() {
+  m_spi->end();
+}
+//------------------------------------------------------------------------------
 inline void SdSpiArduinoDriver::deactivate() {
   m_spi->endTransaction();
 }

+ 4 - 0
src/SpiDriver/SdSpiParticle.cpp

@@ -47,6 +47,10 @@ void SdSpiArduinoDriver::deactivate() {
   m_spi->endTransaction();
 }
 //------------------------------------------------------------------------------
+void SdSpiArduinoDriver::end() {
+  m_spi->end();
+}
+//------------------------------------------------------------------------------
 uint8_t SdSpiArduinoDriver::receive() {
   return m_spi->transfer(0XFF);
 }

+ 4 - 0
src/SpiDriver/SdSpiSTM32.cpp

@@ -51,6 +51,10 @@ void SdSpiArduinoDriver::deactivate() {
   m_spi->endTransaction();
 }
 //------------------------------------------------------------------------------
+void SdSpiArduinoDriver::end() {
+  m_spi->end();
+}
+//------------------------------------------------------------------------------
 uint8_t SdSpiArduinoDriver::receive() {
   return m_spi->transfer(0XFF);
 }

+ 4 - 0
src/SpiDriver/SdSpiSTM32Core.cpp

@@ -43,6 +43,10 @@ void SdSpiArduinoDriver::deactivate() {
   m_spi->endTransaction();
 }
 //------------------------------------------------------------------------------
+void SdSpiArduinoDriver::end() {
+  m_spi->end();
+}
+//------------------------------------------------------------------------------
 uint8_t SdSpiArduinoDriver::receive() {
   return m_spi->transfer(0XFF);
 }

+ 2 - 0
src/SpiDriver/SdSpiSoftDriver.h

@@ -49,6 +49,8 @@ class SdSpiSoftDriver {
   }
   /** Deactivate SPI hardware. */
   void deactivate() {}
+  /** deactivate SPI driver. */
+  void end() {}
   /** Receive a byte.
    *
    * \return The byte.

+ 4 - 0
src/SpiDriver/SdSpiTeensy3.cpp

@@ -50,6 +50,10 @@ void SdSpiArduinoDriver::deactivate() {
   m_spi->endTransaction();
 }
 //------------------------------------------------------------------------------
+void SdSpiArduinoDriver::end() {
+  m_spi->end();
+}
+//------------------------------------------------------------------------------
 uint8_t SdSpiArduinoDriver::receive() {
   return m_spi->transfer(0XFF);
 }

+ 2 - 3
src/common/ArduinoFiles.h

@@ -24,7 +24,7 @@
  */
 #ifndef ArduinoFiles_h
 #define ArduinoFiles_h
-#include "SdFatConfig.h"
+#include "SysCall.h"
 //------------------------------------------------------------------------------
 /** Arduino SD.h style flag for open for read. */
 #ifndef FILE_READ
@@ -91,9 +91,8 @@ class StreamFile : public stream_t, public BaseFile {
    * \return a pointer to replacement suggestion.
    */
 #ifndef DOXYGEN_SHOULD_SKIP_THIS
-  const char* __attribute__((error("use getName(name, size)"))) name();
+  char* __attribute__((error("use getName(name, size)"))) name();
 #endif  // DOXYGEN_SHOULD_SKIP_THIS
-  const char* name() const {return "use getName()";}
   /** Return the next available byte without consuming it.
    *
    * \return The byte if no error and not at eof else -1;

+ 1 - 1
src/common/DebugMacros.h

@@ -24,7 +24,7 @@
  */
 #ifndef DebugMacros_h
 #define DebugMacros_h
-#include "SdFatConfig.h"
+#include "SysCall.h"
 
 // 0 - disable, 1 - fail, halt 2 - fail, halt, warn
 #define USE_DBG_MACROS 0

+ 1 - 2
src/common/FmtNumber.cpp

@@ -139,8 +139,7 @@ void divmod10(uint32_t in, uint32_t &div, uint32_t &mod)
   if (r > 9) mod = r - 10;
   else mod = r;
 }
-// Hackers delight function is here:
-// http://www.hackersdelight.org/hdcodetxt/divuc.c.txt
+// See: https://github.com/hcs0/Hackers-Delight
 // Code below uses 8/10 = 0.1100 1100 1100 1100 1100 1100 1100 1100.
 // 15 ops including the multiply, or 17 elementary ops.
 unsigned divu10(unsigned n) {

+ 1 - 2
src/common/FsApiConstants.h

@@ -24,8 +24,7 @@
  */
 #ifndef FsApiConstants_h
 #define FsApiConstants_h
-#include "SdFatConfig.h"
-
+#include "SysCall.h"
 #if USE_FCNTL_H
 #include <fcntl.h>
 /* values for GNU Arm Embedded Toolchain.

+ 5 - 5
src/common/BlockDevice.h → src/common/FsBlockDevice.h

@@ -22,12 +22,12 @@
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  */
-#ifndef BlockDevice_h
-#define BlockDevice_h
+#ifndef FsBlockDevice_h
+#define FsBlockDevice_h
 #include "SdCard/SdCard.h"
 #if HAS_SDIO_CLASS || USE_BLOCK_DEVICE_INTERFACE
-typedef BlockDeviceInterface BlockDevice;
+typedef FsBlockDeviceInterface FsBlockDevice;
 #else
-typedef SdCard BlockDevice;
+typedef SdCard FsBlockDevice;
 #endif
-#endif  // BlockDevice_h
+#endif  // FsBlockDevice_h

+ 12 - 10
src/common/BlockDeviceInterface.h → src/common/FsBlockDeviceInterface.h

@@ -24,22 +24,24 @@
  */
 /**
  * \file
- * \brief BlockDeviceInterface include file.
+ * \brief FsBlockDeviceInterface include file.
  */
-#ifndef BlockDeviceInterface_h
-#define BlockDeviceInterface_h
+#ifndef FsBlockDeviceInterface_h
+#define FsBlockDeviceInterface_h
 #include <stdint.h>
 #include <stddef.h>
-#include "SdFatConfig.h"
 /**
- * \class BlockDeviceInterface
- * \brief BlockDeviceInterface class.
+ * \class FsBlockDeviceInterface
+ * \brief FsBlockDeviceInterface class.
  */
-class BlockDeviceInterface {
+class FsBlockDeviceInterface {
  public:
-  virtual ~BlockDeviceInterface() {}
+  virtual ~FsBlockDeviceInterface() {}
+
+  /** end use of device */
+  virtual void end() {}
   /**
-   * Check for BlockDevice busy.
+   * Check for FsBlockDevice busy.
    *
    * \return true if busy else false.
    */
@@ -90,4 +92,4 @@ class BlockDeviceInterface {
    */
   virtual bool writeSectors(uint32_t sector, const uint8_t* src, size_t ns) = 0;
 };
-#endif  // BlockDeviceInterface_h
+#endif  // FsBlockDeviceInterface_h

+ 9 - 9
src/common/FsCache.h

@@ -29,7 +29,7 @@
  * \brief Common cache code for exFAT and FAT.
  */
 #include "SysCall.h"
-#include "BlockDevice.h"
+#include "FsBlockDevice.h"
 /**
  * \class FsCache
  * \brief Sector cache.
@@ -127,7 +127,7 @@ class FsCache {
   /** Initialize the cache.
    * \param[in] blockDev Block device for this cache.
    */
-  void init(BlockDevice* blockDev) {
+  void init(FsBlockDevice* blockDev) {
     m_blockDev = blockDev;
     invalidate();
   }
@@ -153,6 +153,12 @@ class FsCache {
   bool isDirty() {
     return m_status & CACHE_STATUS_DIRTY;
   }
+  /** Prepare cache to access sector.
+   * \param[in] sector Sector to read.
+   * \param[in] option mode for cached sector.
+   * \return Address of cached sector.
+   */
+  uint8_t* prepare(uint32_t sector, uint8_t option);
   /** \return Logical sector number for cached sector. */
   uint32_t sector() {
     return m_sector;
@@ -163,12 +169,6 @@ class FsCache {
   void setMirrorOffset(uint32_t offset) {
     m_mirrorOffset = offset;
   }
-  /** Prepare cache to access sector.
-   * \param[in] sector Sector to read.
-   * \param[in] option mode for cached sector.
-   * \return Address of cached sector. 
-   */
-  uint8_t* prepare(uint32_t sector, uint8_t option);
   /** Write current sector if dirty.
    * \return true for success or false for failure.
    */
@@ -176,7 +176,7 @@ class FsCache {
 
  private:
   uint8_t m_status;
-  BlockDevice* m_blockDev;
+  FsBlockDevice* m_blockDev;
   uint32_t m_mirrorOffset;
   uint32_t m_sector;
   uint8_t m_buffer[512];

+ 1 - 1
src/common/FsName.h

@@ -24,7 +24,7 @@
  */
 #ifndef FsName_h
 #define FsName_h
-#include "SdFatConfig.h"
+#include "SysCall.h"
 #include <stdint.h>
 /**
  * \file

+ 91 - 0
src/common/PrintBasic.cpp

@@ -0,0 +1,91 @@
+/**
+ * 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 "PrintBasic.h"
+#if ENABLE_ARDUINO_FEATURES == 0
+#include <math.h>
+
+size_t PrintBasic::print(long n, uint8_t base) {
+  if (n < 0 && base == 10) {
+    return print('-') + printNum(-n, base);
+  }
+  return printNum(n, base);
+}
+size_t PrintBasic::printNum(unsigned long n, uint8_t base) {
+  const uint8_t DIM = 8*sizeof(long);
+  char buf[DIM];
+  char *str = &buf[DIM];
+
+  if (base < 2) return 0;
+
+  do {
+    char c = n%base;
+    n /= base;
+    *--str = c + (c < 10 ? '0' : 'A' - 10);
+  } while (n);
+  return write(str, &buf[DIM] - str);
+}
+
+size_t PrintBasic::printDouble(double n, uint8_t prec) {
+  // Max printable 32-bit floating point number. AVR uses 32-bit double.
+  const double maxfp = static_cast<double>(0XFFFFFF00UL);
+  size_t rtn = 0;
+
+  if (isnan(n)) {
+    return write("NaN");
+  }
+  if (n < 0) {
+    n = -n;
+    rtn += print('-');
+  }
+  if (isinf(n)) {
+    return rtn + write("Inf");
+  }
+  if (n > maxfp) {
+    return rtn + write("Ovf");
+  }
+
+  double round = 0.5;
+  for (uint8_t i  = 0; i < prec; ++i) {
+    round *= 0.1;
+  }
+
+  n += round;
+
+  uint32_t whole = (uint32_t)n;
+  rtn += print(whole);
+
+  if (prec) {
+    rtn += print('.');
+    double fraction = n - static_cast<double>(whole);
+    for (uint8_t i = 0; i < prec; i++) {
+      fraction *= 10.0;
+      uint8_t digit = fraction;
+      rtn += print(digit);
+      fraction -= digit;
+    }
+  }
+  return rtn;
+}
+#endif //  ENABLE_ARDUINO_FEATURES == 0

+ 169 - 0
src/common/PrintBasic.h

@@ -0,0 +1,169 @@
+/**
+ * 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 PrintBasic_h
+#define PrintBasic_h
+/**
+ * \file
+ * \brief Stream/Print like replacement for non-Arduino systems.
+ */
+#include <stdint.h>
+#include <stddef.h>
+#include <string.h>
+#include "../SdFatConfig.h"
+
+#ifndef F
+#if defined(__AVR__)
+#include <avr/pgmspace.h>
+class __FlashStringHelper;
+#define F(string_literal) (reinterpret_cast<const __FlashStringHelper *>(PSTR(string_literal)))
+#else  // defined(__AVR__)
+#define F(str) (str)
+#endif  // defined(__AVR__)
+#endif  // F
+
+#ifdef BIN
+#undef BIN
+#endif  // BIN
+#define BIN 2
+#define OCT 8
+#define DEC 10
+#define HEX 16
+
+class PrintBasic {
+ public:
+  PrintBasic() : m_error(0) {}
+
+  void clearWriteError() {
+    setWriteError(0);
+  }
+  int getWriteError() {
+    return m_error;
+  }
+  size_t print(char c) {
+    return write(c);
+  }
+  size_t print(const char* str) {
+    return write(str);
+  }
+  size_t print(const __FlashStringHelper *str) {
+#ifdef __AVR__
+  PGM_P p = reinterpret_cast<PGM_P>(str);
+  size_t n = 0;
+  for (uint8_t c; (c = pgm_read_byte(p + n)) && write(c); n++) {}
+  return n;
+#else  // __AVR__
+  return print(reinterpret_cast<const char *>(str));
+#endif  // __AVR__
+  }
+  size_t println(const __FlashStringHelper *str) {
+#ifdef __AVR__
+    return print(str) + println();
+#else  // __AVR__
+    return println(reinterpret_cast<const char *>(str));
+#endif  // __AVR__
+  }
+  size_t print(double n, uint8_t prec = 2) {
+    return printDouble(n, prec);
+  }
+  size_t print(signed char n, uint8_t base = 10) {
+    return print((long)n, base);
+  }
+  size_t print(unsigned char n, uint8_t base = 10) {
+    return print((unsigned long)n, base);
+  }
+  size_t print(int n, uint8_t base = 10) {
+    return print((long)n, base);
+  }
+  size_t print(unsigned int n, uint8_t base = 10) {
+    return print((unsigned long)n, base);
+  }
+  size_t print(long n, uint8_t base = 10);
+  size_t print(unsigned long n, uint8_t base = 10) {
+    return printNum(n, base);
+  }
+  size_t println() {
+    return write("\r\n");
+  }
+  size_t println(char c) {
+    return write(c) + println();
+  }
+  size_t println(const char* str) {
+    return print(str) + println();
+  }
+  size_t println(double n, uint8_t prec = 2) {
+    return print(n, prec) + println();
+  }
+  size_t println(signed char n, uint8_t base = 10) {
+    return print(n, base) + println();
+  }
+  size_t println(unsigned char n, uint8_t base = 10) {
+    return print(n, base) + println();
+  }
+  size_t println(int n, uint8_t base = 10) {
+    return print(n, base) + println();
+  }
+  size_t println(unsigned int n, uint8_t base = 10) {
+    return print(n, base) + println();
+  }
+  size_t println(long n, uint8_t base = 10) {
+    return print(n, base) + println();
+  }
+  size_t println(unsigned long n, uint8_t base = 10) {
+    return print(n, base) + println();
+  }
+  size_t write(const char *str) {
+    return write(str, strlen(str));
+  }
+  virtual size_t write(uint8_t b) = 0;
+
+  virtual size_t write(const uint8_t* buffer, size_t size) {
+    size_t i;
+    for (i = 0; i < size; i++) {
+      if (!write(buffer[i])) break;
+    }
+    return i;
+  }
+  size_t write(const char *buffer, size_t size) {
+    return write((const uint8_t*)buffer, size);
+  }
+
+ protected:
+  void setWriteError(int err = 1) {
+    m_error = err;
+  }
+
+ private:
+  size_t printDouble(double n, uint8_t prec);
+  size_t printNum(unsigned long n, uint8_t base);
+  int m_error;
+};
+//------------------------------------------------------------------------------
+class StreamBasic : public PrintBasic {
+ public:
+  virtual int available() = 0;
+  virtual int peek() = 0;
+  virtual int read() = 0;
+};
+#endif  // PrintBasic_h

+ 1 - 35
src/common/SysCall.h

@@ -30,30 +30,13 @@
 #define SysCall_h
 #include <stdint.h>
 #include <stddef.h>
-#include "SdFatConfig.h"
+#include "../SdFatConfig.h"
 #if __cplusplus < 201103
 #warning nullptr defined
 /** Define nullptr if not C++11 */
 #define nullptr NULL
 #endif  // __cplusplus < 201103
 //------------------------------------------------------------------------------
-
-//------------------------------------------------------------------------------
-/**
- * \class SysCall
- * \brief SysCall - Class to wrap system calls.
- */
-class SysCall {
- public:
-  /** Halt execution of this thread. */
-  static void halt() {
-    while (1) {
-      yield();
-    }
-  }
-  /** Yield to other threads. */
-  static void yield();
-};
 #if ENABLE_ARDUINO_FEATURES
 #if defined(ARDUINO)
 /** Use Arduino Print. */
@@ -69,28 +52,11 @@ typedef Stream stream_t;
 #define F(str) (str)
 #endif  // F
 //------------------------------------------------------------------------------
-#if defined(PLATFORM_ID)  // Only defined if a Particle device
-inline void SysCall::yield() {
-  // Recommended to only call Particle.process() if system threading is disabled
-  if (system_thread_get_state(NULL) == spark::feature::DISABLED) {
-    Particle.process();
-  }
-}
-#elif defined(ARDUINO)
-inline void SysCall::yield() {
-  // Use the external Arduino yield() function.
-  ::yield();
-}
-#else  // defined(PLATFORM_ID)
-inline void SysCall::yield() {}
-#endif  // defined(PLATFORM_ID)
-//------------------------------------------------------------------------------
 #else  // ENABLE_ARDUINO_FEATURES
 #include "PrintBasic.h"
 /** If not Arduino */
 typedef PrintBasic print_t;
 /** If not Arduino */
 typedef PrintBasic stream_t;
-inline void SysCall::yield() {}
 #endif  // ENABLE_ARDUINO_FEATURES
 #endif  // SysCall_h

+ 3 - 4
src/iostream/ArduinoStream.h

@@ -28,7 +28,6 @@
  * \file
  * \brief ArduinoInStream and ArduinoOutStream classes
  */
-#include "SdFatConfig.h"
 #include "bufstream.h"
 //==============================================================================
 /**
@@ -54,7 +53,7 @@ class ArduinoInStream : public ibufstream {
     uint32_t t;
     m_line[0] = '\0';
     while (!m_hw->available()) {
-      SysCall::yield();
+      yield();
     }
 
     while (1) {
@@ -111,7 +110,7 @@ class ArduinoOutStream : public ostream {
    *
    * \param[in] pr Print object for this ArduinoOutStream.
    */
-  explicit ArduinoOutStream(Print& pr) : m_pr(&pr) {}
+  explicit ArduinoOutStream(print_t& pr) : m_pr(&pr) {}
 
  protected:
   /// @cond SHOW_PROTECTED
@@ -146,6 +145,6 @@ class ArduinoOutStream : public ostream {
   /// @endcond
  private:
   ArduinoOutStream() {}
-  Print* m_pr;
+  print_t* m_pr;
 };
 #endif  // ArduinoStream_h

+ 1 - 1
src/iostream/StdioStream.h

@@ -440,7 +440,7 @@ class StdioStream : private StreamBaseFile {
     return n > 0 ? n : 0;
   }
   //----------------------------------------------------------------------------
-  /**  Print a number.
+  /** Print a number.
    *
    * \param[in] val the number to be printed.
    *

+ 1 - 1
src/iostream/ios.h

@@ -33,7 +33,7 @@
 /** For internal use in c++ streams */
 typedef fspos_t pos_t;
 //==============================================================================
-#if SDFAT_FILE_TYPE == 1
+#if SDFAT_FILE_TYPE == 1 || defined(DOXYGEN)
 /** Set File type for iostreams. */
 typedef FatFile StreamBaseFile;
 #elif SDFAT_FILE_TYPE == 2