فهرست منبع

New features, major internal changes, bug fixes

Bill Greiman 2 سال پیش
والد
کامیت
628effa1c2
100فایلهای تغییر یافته به همراه932 افزوده شده و 2287 حذف شده
  1. 23 22
      doc/SdErrorCodes.txt
  2. BIN
      doc/html.zip
  3. 12 12
      doc/mainpage.h
  4. 1 0
      examples/AvrAdcLogger/AvrAdcLogger.ino
  5. 49 45
      examples/SdInfo/SdInfo.ino
  6. 6 8
      examples/bench/bench.ino
  7. 0 129
      examples/examplesV1/DirectoryFunctions/DirectoryFunctions.ino
  8. 0 60
      examples/examplesV1/OpenNext/OpenNext.ino
  9. 0 161
      examples/examplesV1/QuickStart/QuickStart.ino
  10. 0 552
      examples/examplesV1/SdFormatter/SdFormatter.ino
  11. 0 248
      examples/examplesV1/SdInfo/SdInfo.ino
  12. 0 59
      examples/examplesV1/SoftwareSpi/SoftwareSpi.ino
  13. 0 169
      examples/examplesV1/TeensySdioDemo/TeensySdioDemo.ino
  14. 0 222
      examples/examplesV1/bench/bench.ino
  15. 0 106
      examples/examplesV1/rename/rename.ino
  16. 1 1
      library.properties
  17. 1 1
      src/BufferedPrint.h
  18. 1 1
      src/ExFatLib/ExFatConfig.h
  19. 3 3
      src/ExFatLib/ExFatDbg.cpp
  20. 50 6
      src/ExFatLib/ExFatFile.cpp
  21. 48 23
      src/ExFatLib/ExFatFile.h
  22. 1 1
      src/ExFatLib/ExFatFilePrint.cpp
  23. 4 4
      src/ExFatLib/ExFatFileWrite.cpp
  24. 1 1
      src/ExFatLib/ExFatFormatter.cpp
  25. 1 1
      src/ExFatLib/ExFatFormatter.h
  26. 1 1
      src/ExFatLib/ExFatLib.h
  27. 1 1
      src/ExFatLib/ExFatName.cpp
  28. 23 21
      src/ExFatLib/ExFatPartition.cpp
  29. 6 5
      src/ExFatLib/ExFatPartition.h
  30. 1 1
      src/ExFatLib/ExFatVolume.cpp
  31. 25 4
      src/ExFatLib/ExFatVolume.h
  32. 3 3
      src/FatLib/FatDbg.cpp
  33. 53 11
      src/FatLib/FatFile.cpp
  34. 45 21
      src/FatLib/FatFile.h
  35. 5 4
      src/FatLib/FatFileLFN.cpp
  36. 1 1
      src/FatLib/FatFilePrint.cpp
  37. 12 8
      src/FatLib/FatFileSFN.cpp
  38. 1 1
      src/FatLib/FatFormatter.cpp
  39. 1 1
      src/FatLib/FatFormatter.h
  40. 1 1
      src/FatLib/FatLib.h
  41. 1 1
      src/FatLib/FatName.cpp
  42. 16 11
      src/FatLib/FatPartition.cpp
  43. 5 7
      src/FatLib/FatPartition.h
  44. 1 1
      src/FatLib/FatVolume.cpp
  45. 24 3
      src/FatLib/FatVolume.h
  46. 1 1
      src/FreeStack.cpp
  47. 1 1
      src/FreeStack.h
  48. 24 11
      src/FsLib/FsFile.cpp
  49. 57 8
      src/FsLib/FsFile.h
  50. 1 1
      src/FsLib/FsFormatter.h
  51. 1 1
      src/FsLib/FsLib.h
  52. 1 1
      src/FsLib/FsNew.cpp
  53. 1 1
      src/FsLib/FsNew.h
  54. 5 5
      src/FsLib/FsVolume.cpp
  55. 26 6
      src/FsLib/FsVolume.h
  56. 1 1
      src/MinimumSerial.cpp
  57. 1 1
      src/MinimumSerial.h
  58. 1 1
      src/RingBuf.h
  59. 1 1
      src/SdCard/SdCard.h
  60. 1 1
      src/SdCard/SdCardInfo.cpp
  61. 127 197
      src/SdCard/SdCardInfo.h
  62. 14 1
      src/SdCard/SdCardInterface.h
  63. 62 14
      src/SdCard/SdSpiCard.cpp
  64. 37 5
      src/SdCard/SdSpiCard.h
  65. 14 1
      src/SdCard/SdioCard.h
  66. 45 13
      src/SdCard/SdioTeensy.cpp
  67. 5 5
      src/SdFat.h
  68. 3 2
      src/SdFatConfig.h
  69. 1 1
      src/SpiDriver/SdSpiArduinoDriver.h
  70. 1 1
      src/SpiDriver/SdSpiArtemis.cpp
  71. 1 1
      src/SpiDriver/SdSpiAvr.h
  72. 1 1
      src/SpiDriver/SdSpiBareUnoDriver.h
  73. 1 1
      src/SpiDriver/SdSpiBaseClass.h
  74. 1 1
      src/SpiDriver/SdSpiChipSelect.cpp
  75. 1 1
      src/SpiDriver/SdSpiDriver.h
  76. 1 1
      src/SpiDriver/SdSpiDue.cpp
  77. 1 1
      src/SpiDriver/SdSpiESP.cpp
  78. 1 1
      src/SpiDriver/SdSpiLibDriver.h
  79. 1 1
      src/SpiDriver/SdSpiParticle.cpp
  80. 1 1
      src/SpiDriver/SdSpiSTM32.cpp
  81. 1 1
      src/SpiDriver/SdSpiSTM32Core.cpp
  82. 1 1
      src/SpiDriver/SdSpiSoftDriver.h
  83. 1 1
      src/SpiDriver/SdSpiTeensy3.cpp
  84. 1 1
      src/common/ArduinoFiles.h
  85. 1 1
      src/common/CompileDateTime.h
  86. 1 1
      src/common/DebugMacros.h
  87. 1 1
      src/common/FmtNumber.cpp
  88. 1 1
      src/common/FmtNumber.h
  89. 1 1
      src/common/FsApiConstants.h
  90. 1 1
      src/common/FsBlockDevice.h
  91. 1 1
      src/common/FsBlockDeviceInterface.h
  92. 1 1
      src/common/FsCache.cpp
  93. 1 1
      src/common/FsCache.h
  94. 1 1
      src/common/FsDateTime.cpp
  95. 1 1
      src/common/FsDateTime.h
  96. 1 1
      src/common/FsName.cpp
  97. 1 1
      src/common/FsName.h
  98. 1 1
      src/common/FsStructs.cpp
  99. 43 35
      src/common/FsStructs.h
  100. 1 1
      src/common/FsUtf.cpp

+ 23 - 22
doc/SdErrorCodes.txt

@@ -1,4 +1,4 @@
-2021-01-06
+2022-07-01
 
 Run the SdErrorCode example to produce an updated list.
 
@@ -27,24 +27,25 @@ Code,Symbol - failed operation
 0X15,SD_CARD_ERROR_ACMD13 - Read extended status
 0X16,SD_CARD_ERROR_ACMD23 - Set pre-erased count
 0X17,SD_CARD_ERROR_ACMD41 - Activate card initialization
-0X18,SD_CARD_ERROR_READ_TOKEN - Bad read data token
-0X19,SD_CARD_ERROR_READ_CRC - Read CRC error
-0X1A,SD_CARD_ERROR_READ_FIFO - SDIO fifo read timeout
-0X1B,SD_CARD_ERROR_READ_REG - Read CID or CSD failed.
-0X1C,SD_CARD_ERROR_READ_START - Bad readStart argument
-0X1D,SD_CARD_ERROR_READ_TIMEOUT - Read data timeout
-0X1E,SD_CARD_ERROR_STOP_TRAN - Multiple block stop failed
-0X1F,SD_CARD_ERROR_TRANSFER_COMPLETE - SDIO transfer complete
-0X20,SD_CARD_ERROR_WRITE_DATA - Write data not accepted
-0X21,SD_CARD_ERROR_WRITE_FIFO - SDIO fifo write timeout
-0X22,SD_CARD_ERROR_WRITE_START - Bad writeStart argument
-0X23,SD_CARD_ERROR_WRITE_PROGRAMMING - Flash programming
-0X24,SD_CARD_ERROR_WRITE_TIMEOUT - Write timeout
-0X25,SD_CARD_ERROR_DMA - DMA transfer failed
-0X26,SD_CARD_ERROR_ERASE - Card did not accept erase commands
-0X27,SD_CARD_ERROR_ERASE_SINGLE_SECTOR - Card does not support erase
-0X28,SD_CARD_ERROR_ERASE_TIMEOUT - Erase command timeout
-0X29,SD_CARD_ERROR_INIT_NOT_CALLED - Card has not been initialized
-0X2A,SD_CARD_ERROR_INVALID_CARD_CONFIG - Invalid card config
-0X2B,SD_CARD_ERROR_FUNCTION_NOT_SUPPORTED - Unsupported SDIO command
-0X2C,SD_CARD_ERROR_UNKNOWN - Unknown error
+0X18,SD_CARD_ERROR_ACMD51 - Read SCR data
+0X19,SD_CARD_ERROR_READ_TOKEN - Bad read data token
+0X1A,SD_CARD_ERROR_READ_CRC - Read CRC error
+0X1B,SD_CARD_ERROR_READ_FIFO - SDIO fifo read timeout
+0X1C,SD_CARD_ERROR_READ_REG - Read CID or CSD failed.
+0X1D,SD_CARD_ERROR_READ_START - Bad readStart argument
+0X1E,SD_CARD_ERROR_READ_TIMEOUT - Read data timeout
+0X1F,SD_CARD_ERROR_STOP_TRAN - Multiple block stop failed
+0X20,SD_CARD_ERROR_TRANSFER_COMPLETE - SDIO transfer complete
+0X21,SD_CARD_ERROR_WRITE_DATA - Write data not accepted
+0X22,SD_CARD_ERROR_WRITE_FIFO - SDIO fifo write timeout
+0X23,SD_CARD_ERROR_WRITE_START - Bad writeStart argument
+0X24,SD_CARD_ERROR_WRITE_PROGRAMMING - Flash programming
+0X25,SD_CARD_ERROR_WRITE_TIMEOUT - Write timeout
+0X26,SD_CARD_ERROR_DMA - DMA transfer failed
+0X27,SD_CARD_ERROR_ERASE - Card did not accept erase commands
+0X28,SD_CARD_ERROR_ERASE_SINGLE_SECTOR - Card does not support erase
+0X29,SD_CARD_ERROR_ERASE_TIMEOUT - Erase command timeout
+0X2A,SD_CARD_ERROR_INIT_NOT_CALLED - Card has not been initialized
+0X2B,SD_CARD_ERROR_INVALID_CARD_CONFIG - Invalid card config
+0X2C,SD_CARD_ERROR_FUNCTION_NOT_SUPPORTED - Unsupported SDIO command
+0X2D,SD_CARD_ERROR_UNKNOWN - Unknown error

BIN
doc/html.zip


+ 12 - 12
doc/mainpage.h

@@ -31,19 +31,19 @@ This is a major new version of SdFat. It is mostly
 backward compatible with SdFat Version 1 for FAT16/FAT32 cards.
 
 You should edit SdFatConfig.h to select features. The default version of
-SdFatConfig.h is suitable for UNO and other small AVR boards. 
+SdFatConfig.h is suitable for UNO and other small AVR boards.
 
 \section Intro Introduction
- 
-The Arduino %SdFat library supports FAT16, FAT32, and exFAT file systems 
+
+The Arduino %SdFat library supports FAT16, FAT32, and exFAT file systems
 on Standard SD, SDHC, and SDXC cards.
- 
+
 In %SdFat version 1, SdFat and File are the main classes.
 
 In %SdFat version 2, SdFat and File are defined by typedefs in terms of the
-following classes. 
+following classes.
 
-The file system classes in the %SdFat library are SdFat32, SdExFat, and SdFs. 
+The file system classes in the %SdFat library are SdFat32, SdExFat, and SdFs.
 SdFat32 supports FAT16 and FAT32. SdExFat supports exFAT, SdFs supports
 FAT16, FAT32, and exFAT.
 
@@ -69,7 +69,7 @@ boards.
 #endif  // defined(__AVR__) && FLASHEND < 0X8000
 \endcode
 
-It is possible to use option three, support or FAT16/FAT32 and exFat 
+It is possible to use option three, support or FAT16/FAT32 and exFat
 on an Uno or other AVR board with 32KB flash and 2KB SRAM but memory
 will be very limited.
 
@@ -79,7 +79,7 @@ Uno memory use for a simple data logger is:
 >
 > option 2, exFAT, 14942 bytes of flash and 895 bytes of SRAM.
 >
-> option 3, FAT16/FAT32 and exFAT, 21834 bytes of flash and 908 bytes of SRAM. 
+> option 3, FAT16/FAT32 and exFAT, 21834 bytes of flash and 908 bytes of SRAM.
 
 Please read documentation under the above classes tab for more information.
 
@@ -113,8 +113,8 @@ multi-block write.
 
 Relative paths in %SdFat are resolved in a manner similar to Windows.
 
-Each instance of SdFat32, SdExFat, and SdFs has a current directory.  
-This directory is called the volume working directory, vwd.  
+Each instance of SdFat32, SdExFat, and SdFs has a current directory.
+This directory is called the volume working directory, vwd.
 Initially this directory is the root directory for the volume.
 
 The volume working directory is changed by calling the chdir(path).
@@ -149,9 +149,9 @@ will open "/music/BigBand.wav" on sd2.
 
 \section Install Installation
 
-You must manually install %SdFat by renaming the download folder %SdFat 
+You must manually install %SdFat by renaming the download folder %SdFat
 and copy the %SdFat folder to the Arduino libraries folder in your
-sketchbook folder. 
+sketchbook folder.
 
 It will be necessary to unzip and rename the folder if you download a zip
 file from GitHub.

+ 1 - 0
examples/AvrAdcLogger/AvrAdcLogger.ino

@@ -151,6 +151,7 @@ const uint32_t MAX_FILE_SIZE = MAX_FILE_SIZE_MiB << 20;
 
 // Max SPI rate for AVR is 10 MHz for F_CPU 20 MHz, 8 MHz for F_CPU 16 MHz.
 #define SPI_CLOCK SD_SCK_MHZ(10)
+
 // Select fastest interface.
 #if ENABLE_DEDICATED_SPI
 #define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK)

+ 49 - 45
examples/SdInfo/SdInfo.ino

@@ -35,28 +35,27 @@ const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
 
 //------------------------------------------------------------------------------
 SdFs sd;
-cid_t m_cid;
-csd_t m_csd;
-uint32_t m_eraseSize;
-uint32_t m_ocr;
+cid_t cid;
+csd_t csd;
+scr_t scr;
+uint8_t cmd6Data[64];
+uint32_t eraseSize;
+uint32_t ocr;
 static ArduinoOutStream cout(Serial);
 //------------------------------------------------------------------------------
-bool cidDmp() {
+void cidDmp() {
   cout << F("\nManufacturer ID: ");
-  cout << uppercase << showbase << hex << int(m_cid.mid) << dec << endl;
-  cout << F("OEM ID: ") << m_cid.oid[0] << m_cid.oid[1] << endl;
+  cout << uppercase << showbase << hex << int(cid.mid) << dec << endl;
+  cout << F("OEM ID: ") << cid.oid[0] << cid.oid[1] << endl;
   cout << F("Product: ");
   for (uint8_t i = 0; i < 5; i++) {
-    cout << m_cid.pnm[i];
+    cout << cid.pnm[i];
   }
-  cout << F("\nVersion: ");
-  cout << int(m_cid.prv_n) << '.' << int(m_cid.prv_m) << endl;
-  cout << F("Serial number: ") << hex << m_cid.psn << dec << endl;
+  cout << F("\nRevision: ") << cid.prvN() << '.' << cid.prvM() << endl;
+  cout << F("Serial number: ") << hex << cid.psn() << dec << endl;
   cout << F("Manufacturing date: ");
-  cout << int(m_cid.mdt_month) << '/';
-  cout << (2000 + 16*m_cid.mdt_year_high + m_cid.mdt_year_low) << endl;
+  cout << cid.mdtMonth() << '/' << cid.mdtYear() << endl;
   cout << endl;
-  return true;
 }
 //------------------------------------------------------------------------------
 void clearSerialInput() {
@@ -68,30 +67,24 @@ void clearSerialInput() {
   } while (micros() - m < 10000);
 }
 //------------------------------------------------------------------------------
-bool csdDmp() {
-  bool eraseSingleBlock;
-  if (m_csd.v1.csd_ver == 0) {
-    eraseSingleBlock = m_csd.v1.erase_blk_en;
-    m_eraseSize = (m_csd.v1.sector_size_high << 1) | m_csd.v1.sector_size_low;
-  } else if (m_csd.v2.csd_ver == 1) {
-    eraseSingleBlock = m_csd.v2.erase_blk_en;
-    m_eraseSize = (m_csd.v2.sector_size_high << 1) | m_csd.v2.sector_size_low;
-  } else {
-    cout << F("m_csd version error\n");
-    return false;
-  }
-  m_eraseSize++;
-  cout << F("cardSize: ") << 0.000512 * sdCardCapacity(&m_csd);
+void csdDmp() {
+  eraseSize = csd.eraseSize();
+  cout << F("cardSize: ") << 0.000512 * csd.capacity();
   cout << F(" MB (MB = 1,000,000 bytes)\n");
 
-  cout << F("flashEraseSize: ") << int(m_eraseSize) << F(" blocks\n");
+  cout << F("flashEraseSize: ") << int(eraseSize) << F(" blocks\n");
   cout << F("eraseSingleBlock: ");
-  if (eraseSingleBlock) {
+  if (csd.eraseSingleBlock()) {
     cout << F("true\n");
   } else {
     cout << F("false\n");
   }
-  return true;
+  cout << F("dataAfterErase: ");
+  if (scr.dataAfterErase()) {
+    cout << F("ones\n");
+  } else {
+    cout << F("zeros\n");
+  }
 }
 //------------------------------------------------------------------------------
 void errorPrint() {
@@ -99,7 +92,7 @@ void errorPrint() {
     cout << F("SD errorCode: ") << hex << showbase;
     printSdErrorSymbol(&Serial, sd.sdErrorCode());
     cout << F(" = ") << int(sd.sdErrorCode()) << endl;
-    cout << F("SD errorData = ") << int(sd.sdErrorData()) << endl;
+    cout << F("SD errorData = ") << int(sd.sdErrorData()) << dec << endl;
   }
 }
 //------------------------------------------------------------------------------
@@ -116,7 +109,7 @@ bool mbrDmp() {
   for (uint8_t ip = 1; ip < 5; ip++) {
     MbrPart_t *pt = &mbr.part[ip - 1];
     if ((pt->boot != 0 && pt->boot != 0X80) ||
-        getLe32(pt->relativeSectors) > sdCardCapacity(&m_csd)) {
+        getLe32(pt->relativeSectors) > csd.capacity()) {
       valid = false;
     }
     cout << int(ip) << ',' << uppercase << showbase << hex;
@@ -139,20 +132,22 @@ bool mbrDmp() {
 //------------------------------------------------------------------------------
 void dmpVol() {
   cout << F("\nScanning FAT, please wait.\n");
-  uint32_t freeClusterCount = sd.freeClusterCount();
+  int32_t freeClusterCount = sd.freeClusterCount();
   if (sd.fatType() <= 32) {
     cout << F("\nVolume is FAT") << int(sd.fatType()) << endl;
   } else {
     cout << F("\nVolume is exFAT\n");
   }
   cout << F("sectorsPerCluster: ") << sd.sectorsPerCluster() << endl;
-  cout << F("clusterCount:      ") << sd.clusterCount() << endl;
-  cout << F("freeClusterCount:  ") << freeClusterCount << endl;
   cout << F("fatStartSector:    ") << sd.fatStartSector() << endl;
   cout << F("dataStartSector:   ") << sd.dataStartSector() << endl;
-  if (sd.dataStartSector() % m_eraseSize) {
-    cout << F("Data area is not aligned on flash erase boundary!\n");
-    cout << F("Download and use formatter from www.sdcard.org!\n");
+  cout << F("clusterCount:      ") << sd.clusterCount() << endl;  
+  cout << F("freeClusterCount:  ");
+  if (freeClusterCount >= 0) {
+    cout << freeClusterCount << endl;
+  } else {
+    cout << F("failed\n");
+    errorPrint();    
   }
 }
 //------------------------------------------------------------------------------
@@ -170,7 +165,7 @@ void printCardType() {
       break;
 
     case SD_CARD_TYPE_SDHC:
-      if (sdCardCapacity(&m_csd) < 70000000) {
+      if (csd.capacity() < 70000000) {
         cout << F("SDHC\n");
       } else {
         cout << F("SDXC\n");
@@ -239,20 +234,29 @@ void loop() {
     return;
   }
   t = millis() - t;
-  cout << F("init time: ") << t << " ms" << endl;
+  cout << F("init time: ") << dec << t << " ms" << endl;
 
-  if (!sd.card()->readCID(&m_cid) ||
-      !sd.card()->readCSD(&m_csd) ||
-      !sd.card()->readOCR(&m_ocr)) {
+  if (!sd.card()->readCID(&cid) ||
+      !sd.card()->readCSD(&csd) ||
+      !sd.card()->readOCR(&ocr) ||
+      !sd.card()->readSCR(&scr)) {
     cout << F("readInfo failed\n");
     errorPrint();
     return;
   }
   printCardType();
+  cout << F("sdSpecVer: ") << 0.01*scr.sdSpecVer() << endl;
+  cout << F("HighSpeedMode: ");
+  if (scr.sdSpecVer() &&
+    sd.card()->cardCMD6(0X00FFFFFF, cmd6Data) && (2 & cmd6Data[13])) {
+    cout << F("true\n");
+  } else {
+    cout << F("false\n");
+  }      
   cidDmp();
   csdDmp();
   cout << F("\nOCR: ") << uppercase << showbase;
-  cout << hex << m_ocr << dec << endl;
+  cout << hex << ocr << dec << endl;
   if (!mbrDmp()) {
     return;
   }

+ 6 - 8
examples/bench/bench.ino

@@ -30,7 +30,7 @@ const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
 // Try to select the best SD card configuration.
 #if HAS_SDIO_CLASS
 #define SD_CONFIG SdioConfig(FIFO_SDIO)
-#elif ENABLE_DEDICATED_SPI
+#elif  ENABLE_DEDICATED_SPI
 #define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK)
 #else  // HAS_SDIO_CLASS
 #define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SPI_CLOCK)
@@ -89,22 +89,19 @@ ArduinoOutStream cout(Serial);
 void cidDmp() {
   cid_t cid;
   if (!sd.card()->readCID(&cid)) {
-
     error("readCID failed");
   }
   cout << F("\nManufacturer ID: ");
-  cout << hex << int(cid.mid) << dec << endl;
+  cout << uppercase << showbase << hex << int(cid.mid) << dec << endl;
   cout << F("OEM ID: ") << cid.oid[0] << cid.oid[1] << endl;
   cout << F("Product: ");
   for (uint8_t i = 0; i < 5; i++) {
     cout << cid.pnm[i];
   }
-  cout << F("\nVersion: ");
-  cout << int(cid.prv_n) << '.' << int(cid.prv_m) << endl;
-  cout << F("Serial number: ") << hex << cid.psn << dec << endl;
+  cout << F("\nRevision: ") << cid.prvN() << '.' << cid.prvM() << endl;
+  cout << F("Serial number: ") << hex << cid.psn() << dec << endl;
   cout << F("Manufacturing date: ");
-  cout << int(cid.mdt_month) << '/';
-  cout << (2000 + cid.mdt_year_low + 10 * cid.mdt_year_high) << endl;
+  cout << cid.mdtMonth() << '/' << cid.mdtYear() << endl;
   cout << endl;
 }
 //------------------------------------------------------------------------------
@@ -273,4 +270,5 @@ void loop() {
   }
   cout << endl << F("Done") << endl;
   file.close();
+  sd.end();
 }

+ 0 - 129
examples/examplesV1/DirectoryFunctions/DirectoryFunctions.ino

@@ -1,129 +0,0 @@
-/*
- * Example use of chdir(), ls(), mkdir(), and  rmdir().
- */
-#include <SPI.h>
-#include "SdFat.h"
-#include "sdios.h"
-// SD card chip select pin.
-const uint8_t chipSelect = SS;
-//------------------------------------------------------------------------------
-
-// File system object.
-SdFat sd;
-
-// Directory file.
-SdFile root;
-
-// Use for file creation in folders.
-SdFile file;
-
-// Create a Serial output stream.
-ArduinoOutStream cout(Serial);
-
-// Buffer for Serial input.
-char cinBuf[40];
-
-// Create a serial input stream.
-ArduinoInStream cin(Serial, cinBuf, sizeof(cinBuf));
-//==============================================================================
-// Error messages stored in flash.
-#define error(msg) sd.errorHalt(F(msg))
-//------------------------------------------------------------------------------
-void setup() {
-  Serial.begin(9600);
-
-  // Wait for USB Serial
-  while (!Serial) {
-    yield();
-  }
-  delay(1000);
-
-  cout << F("Type any character to start\n");
-  // Wait for input line and discard.
-  cin.readline();
-  cout << endl;
-
-  // Initialize at the highest speed supported by the board that is
-  // not over 50 MHz. Try a lower speed if SPI errors occur.
-  if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
-    sd.initErrorHalt();
-  }
-  if (sd.exists("Folder1")
-    || sd.exists("Folder1/file1.txt")
-    || sd.exists("Folder1/File2.txt")) {
-    error("Please remove existing Folder1, file1.txt, and File2.txt");
-  }
-
-  int rootFileCount = 0;
-  if (!root.open("/")) {
-    error("open root failed");
-  }
-  while (file.openNext(&root, O_RDONLY)) {
-    if (!file.isHidden()) {
-      rootFileCount++;
-    }
-    file.close();
-    if (rootFileCount > 10) {
-      error("Too many files in root. Please use an empty SD.");
-    }
-  }
-  if (rootFileCount) {
-    cout << F("\nPlease use an empty SD for best results.\n\n");
-    delay(1000);
-  }
-  // Create a new folder.
-  if (!sd.mkdir("Folder1")) {
-    error("Create Folder1 failed");
-  }
-  cout << F("Created Folder1\n");
-
-  // Create a file in Folder1 using a path.
-  if (!file.open("Folder1/file1.txt", O_WRONLY | O_CREAT)) {
-    error("create Folder1/file1.txt failed");
-  }
-  file.close();
-  cout << F("Created Folder1/file1.txt\n");
-
-  // Change volume working directory to Folder1.
-  if (!sd.chdir("Folder1")) {
-    error("chdir failed for Folder1.\n");
-  }
-  cout << F("chdir to Folder1\n");
-
-  // Create File2.txt in current directory.
-  if (!file.open("File2.txt", O_WRONLY | O_CREAT)) {
-    error("create File2.txt failed");
-  }
-  file.close();
-  cout << F("Created File2.txt in current directory\n");
-
-  cout << F("\nList of files on the SD.\n");
-  sd.ls("/", LS_R);
-
-  // Remove files from current directory.
-  if (!sd.remove("file1.txt") || !sd.remove("File2.txt")) {
-    error("remove failed");
-  }
-  cout << F("\nfile1.txt and File2.txt removed.\n");
-
-  // Change current directory to root.
-  if (!sd.chdir()) {
-    error("chdir to root failed.\n");
-  }
-
-  cout << F("\nList of files on the SD.\n");
-  sd.ls(LS_R);
-
-  // Remove Folder1.
-  if (!sd.rmdir("Folder1")) {
-    error("rmdir for Folder1 failed\n");
-  }
-
-  cout << F("\nFolder1 removed.\n");
-  cout << F("\nList of files on the SD.\n");
-  sd.ls(LS_R);
-  cout << F("Done!\n");
-}
-//------------------------------------------------------------------------------
-// Nothing happens in loop.
-void loop() {}

+ 0 - 60
examples/examplesV1/OpenNext/OpenNext.ino

@@ -1,60 +0,0 @@
-/*
- * Print size, modify date/time, and name for all files in root.
- */
-#include <SPI.h>
-#include "SdFat.h"
-
-// SD default chip select pin.
-const uint8_t chipSelect = SS;
-
-// file system object
-SdFat sd;
-
-SdFile root;
-SdFile file;
-//------------------------------------------------------------------------------
-void setup() {
-  Serial.begin(9600);
-
-  // Wait for USB Serial
-  while (!Serial) {
-    yield();
-  }
-
-  Serial.println("Type any character to start");
-  while (!Serial.available()) {
-    yield();
-  }
-
-  // Initialize at the highest speed supported by the board that is
-  // not over 50 MHz. Try a lower speed if SPI errors occur.
-  if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
-    sd.initErrorHalt();
-  }
-  if (!root.open("/")) {
-    sd.errorHalt("open root failed");
-  }
-  // Open next file in root.
-  // Warning, openNext starts at the current directory position
-  // so a rewind of the directory may be required.
-  while (file.openNext(&root, O_RDONLY)) {
-    file.printFileSize(&Serial);
-    Serial.write(' ');
-    file.printModifyDateTime(&Serial);
-    Serial.write(' ');
-    file.printName(&Serial);
-    if (file.isDir()) {
-      // Indicate a directory.
-      Serial.write('/');
-    }
-    Serial.println();
-    file.close();
-  }
-  if (root.getError()) {
-    Serial.println("openNext failed");
-  } else {
-    Serial.println("Done!");
-  }
-}
-//------------------------------------------------------------------------------
-void loop() {}

+ 0 - 161
examples/examplesV1/QuickStart/QuickStart.ino

@@ -1,161 +0,0 @@
-// Quick hardware test for SPI card access.
-//
-#include <SPI.h>
-#include "SdFat.h"
-#include "sdios.h"
-//
-// Set DISABLE_CHIP_SELECT to disable a second SPI device.
-// For example, with the Ethernet shield, set DISABLE_CHIP_SELECT
-// to 10 to disable the Ethernet controller.
-const int8_t DISABLE_CHIP_SELECT = -1;
-//
-// Test with reduced SPI speed for breadboards.  SD_SCK_MHZ(4) will select
-// the highest speed supported by the board that is not over 4 MHz.
-// Change SPI_SPEED to SD_SCK_MHZ(50) for best performance.
-#define SPI_SPEED SD_SCK_MHZ(4)
-//------------------------------------------------------------------------------
-// File system object.
-SdFat sd;
-
-// Serial streams
-ArduinoOutStream cout(Serial);
-
-// input buffer for line
-char cinBuf[40];
-ArduinoInStream cin(Serial, cinBuf, sizeof(cinBuf));
-
-// SD card chip select
-int chipSelect;
-
-void cardOrSpeed() {
-  cout << F("Try another SD card or reduce the SPI bus speed.\n");
-  cout << F("Edit SPI_SPEED in this program to change it.\n");
-}
-
-void reformatMsg() {
-  cout << F("Try reformatting the card.  For best results use\n");
-  cout << F("the SdFormatter program in SdFat/examples or download\n");
-  cout << F("and use SDFormatter from www.sdcard.org/downloads.\n");
-}
-
-void setup() {
-  Serial.begin(9600);
-
-  // Wait for USB Serial
-  while (!Serial) {
-    yield();
-  }
-  cout << F("\nSPI pins:\n");
-  cout << F("MISO: ") << int(MISO) << endl;
-  cout << F("MOSI: ") << int(MOSI) << endl;
-  cout << F("SCK:  ") << int(SCK) << endl;
-  cout << F("SS:   ") << int(SS) << endl;
-
-  if (DISABLE_CHIP_SELECT < 0) {
-    cout << F(
-           "\nBe sure to edit DISABLE_CHIP_SELECT if you have\n"
-           "a second SPI device.  For example, with the Ethernet\n"
-           "shield, DISABLE_CHIP_SELECT should be set to 10\n"
-           "to disable the Ethernet controller.\n");
-  }
-  cout << F(
-         "\nSD chip select is the key hardware option.\n"
-         "Common values are:\n"
-         "Arduino Ethernet shield, pin 4\n"
-         "Sparkfun SD shield, pin 8\n"
-         "Adafruit SD shields and modules, pin 10\n");
-}
-
-bool firstTry = true;
-void loop() {
-  // Read any existing Serial data.
-  do {
-    delay(10);
-  } while (Serial.available() && Serial.read() >= 0);
-
-  if (!firstTry) {
-    cout << F("\nRestarting\n");
-  }
-  firstTry = false;
-
-  cout << F("\nEnter the chip select pin number: ");
-  while (!Serial.available()) {
-    yield();
-  }
-  cin.readline();
-  if (cin >> chipSelect) {
-    cout << chipSelect << endl;
-  } else {
-    cout << F("\nInvalid pin number\n");
-    return;
-  }
-  if (DISABLE_CHIP_SELECT < 0) {
-    cout << F(
-           "\nAssuming the SD is the only SPI device.\n"
-           "Edit DISABLE_CHIP_SELECT to disable another device.\n");
-  } else {
-    cout << F("\nDisabling SPI device on pin ");
-    cout << int(DISABLE_CHIP_SELECT) << endl;
-    pinMode(DISABLE_CHIP_SELECT, OUTPUT);
-    digitalWrite(DISABLE_CHIP_SELECT, HIGH);
-  }
-  if (!sd.begin(chipSelect, SPI_SPEED)) {
-    if (sd.card()->errorCode()) {
-      cout << F(
-             "\nSD initialization failed.\n"
-             "Do not reformat the card!\n"
-             "Is the card correctly inserted?\n"
-             "Is chipSelect set to the correct value?\n"
-             "Does another SPI device need to be disabled?\n"
-             "Is there a wiring/soldering problem?\n");
-      cout << F("\nerrorCode: ") << hex << showbase;
-      cout << int(sd.card()->errorCode());
-      cout << F(", errorData: ") << int(sd.card()->errorData());
-      cout << dec << noshowbase << endl;
-      return;
-    }
-    if (sd.vol()->fatType() == 0) {
-      cout << F("Can't find a valid FAT16/FAT32 partition.\n");
-      reformatMsg();
-      return;
-    }
-    cout << F("begin failed, can't determine error type\n");
-    return;
-  }
-  cout << F("\nCard successfully initialized.\n");
-  cout << endl;
-
-  uint32_t size = sd.card()->cardSize();
-  if (size == 0) {
-    cout << F("Can't determine the card size.\n");
-    cardOrSpeed();
-    return;
-  }
-  uint32_t sizeMB = 0.000512 * size + 0.5;
-  cout << F("Card size: ") << sizeMB;
-  cout << F(" MB (MB = 1,000,000 bytes)\n");
-  cout << endl;
-  cout << F("Volume is FAT") << int(sd.vol()->fatType());
-  cout << F(", Cluster size (bytes): ") << 512L * sd.vol()->blocksPerCluster();
-  cout << endl << endl;
-
-  cout << F("Files found (date time size name):\n");
-  sd.ls(LS_R | LS_DATE | LS_SIZE);
-
-  if ((sizeMB > 1100 && sd.vol()->blocksPerCluster() < 64)
-      || (sizeMB < 2200 && sd.vol()->fatType() == 32)) {
-    cout << F("\nThis card should be reformatted for best performance.\n");
-    cout << F("Use a cluster size of 32 KB for cards larger than 1 GB.\n");
-    cout << F("Only cards larger than 2 GB should be formatted FAT32.\n");
-    reformatMsg();
-    return;
-  }
-  // Read any extra Serial data.
-  do {
-    delay(10);
-  } while (Serial.available() && Serial.read() >= 0);
-  cout << F("\nSuccess!  Type any character to restart.\n");
-  while (!Serial.available()) {
-    yield();
-  }
-}

+ 0 - 552
examples/examplesV1/SdFormatter/SdFormatter.ino

@@ -1,552 +0,0 @@
-/*
- * This program will format an SD or SDHC card.
- * Warning all data will be deleted!
- *
- * For SD/SDHC cards larger than 64 MB this
- * program attempts to match the format
- * generated by SDFormatter available here:
- *
- * http://www.sdcard.org/consumers/formatter/
- *
- * For smaller cards this program uses FAT16
- * and SDFormatter uses FAT12.
- */
-#error  use new Version 2 SdFormatter
-// Set USE_SDIO to zero for SPI card access.
-#define USE_SDIO 0
-//
-// Change the value of chipSelect if your hardware does
-// not use the default value, SS.  Common values are:
-// Arduino Ethernet shield: pin 4
-// Sparkfun SD shield: pin 8
-// Adafruit SD shields and modules: pin 10
-const uint8_t chipSelect = SS;
-
-// Initialize at highest supported speed not over 50 MHz.
-// Reduce max speed if errors occur.
-#define SPI_SPEED SD_SCK_MHZ(50)
-
-// Print extra info for debug if DEBUG_PRINT is nonzero
-#define DEBUG_PRINT 0
-#include <SPI.h>
-#include "SdFat.h"
-#include "sdios.h"
-#if DEBUG_PRINT
-#include "FreeStack.h"
-#endif  // DEBUG_PRINT
-
-// Serial output stream
-ArduinoOutStream cout(Serial);
-
-#if USE_SDIO
-// Use faster SdioCardEX
-SdioCardEX card;
-// SdioCard card;
-#else  // USE_SDIO
-Sd2Card card;
-#endif  // USE_SDIO
-
-uint32_t cardSizeBlocks;
-uint32_t cardCapacityMB;
-
-// cache for SD block
-cache_t cache;
-
-// MBR information
-uint8_t partType;
-uint32_t relSector;
-uint32_t partSize;
-
-// Fake disk geometry
-uint8_t numberOfHeads;
-uint8_t sectorsPerTrack;
-
-// FAT parameters
-uint16_t reservedSectors;
-uint8_t sectorsPerCluster;
-uint32_t fatStart;
-uint32_t fatSize;
-uint32_t dataStart;
-
-// constants for file system structure
-uint16_t const BU16 = 128;
-uint16_t const BU32 = 8192;
-
-//  strings needed in file system structures
-char noName[] = "NO NAME    ";
-char fat16str[] = "FAT16   ";
-char fat32str[] = "FAT32   ";
-//------------------------------------------------------------------------------
-#define sdError(msg) {cout << F("error: ") << F(msg) << endl; sdErrorHalt();}
-//------------------------------------------------------------------------------
-void sdErrorHalt() {
-  if (card.errorCode()) {
-    cout << F("SD error: ") << hex << int(card.errorCode());
-    cout << ',' << int(card.errorData()) << dec << endl;
-  }
-  while (true) {}
-}
-//------------------------------------------------------------------------------
-#if DEBUG_PRINT
-void debugPrint() {
-  cout << F("FreeStack: ") << FreeStack() << endl;
-  cout << F("partStart: ") << relSector << endl;
-  cout << F("partSize: ") << partSize << endl;
-  cout << F("reserved: ") << reservedSectors << endl;
-  cout << F("fatStart: ") << fatStart << endl;
-  cout << F("fatSize: ") << fatSize << endl;
-  cout << F("dataStart: ") << dataStart << endl;
-  cout << F("clusterCount: ");
-  cout << ((relSector + partSize - dataStart)/sectorsPerCluster) << endl;
-  cout << endl;
-  cout << F("Heads: ") << int(numberOfHeads) << endl;
-  cout << F("Sectors: ") << int(sectorsPerTrack) << endl;
-  cout << F("Cylinders: ");
-  cout << cardSizeBlocks/(numberOfHeads*sectorsPerTrack) << endl;
-}
-#endif  // DEBUG_PRINT
-//------------------------------------------------------------------------------
-// write cached block to the card
-uint8_t writeCache(uint32_t lbn) {
-  return card.writeBlock(lbn, cache.data);
-}
-//------------------------------------------------------------------------------
-// initialize appropriate sizes for SD capacity
-void initSizes() {
-  if (cardCapacityMB <= 6) {
-    sdError("Card is too small.");
-  } else if (cardCapacityMB <= 16) {
-    sectorsPerCluster = 2;
-  } else if (cardCapacityMB <= 32) {
-    sectorsPerCluster = 4;
-  } else if (cardCapacityMB <= 64) {
-    sectorsPerCluster = 8;
-  } else if (cardCapacityMB <= 128) {
-    sectorsPerCluster = 16;
-  } else if (cardCapacityMB <= 1024) {
-    sectorsPerCluster = 32;
-  } else if (cardCapacityMB <= 32768) {
-    sectorsPerCluster = 64;
-  } else {
-    // SDXC cards
-    sectorsPerCluster = 128;
-  }
-
-  cout << F("Blocks/Cluster: ") << int(sectorsPerCluster) << endl;
-  // set fake disk geometry
-  sectorsPerTrack = cardCapacityMB <= 256 ? 32 : 63;
-
-  if (cardCapacityMB <= 16) {
-    numberOfHeads = 2;
-  } else if (cardCapacityMB <= 32) {
-    numberOfHeads = 4;
-  } else if (cardCapacityMB <= 128) {
-    numberOfHeads = 8;
-  } else if (cardCapacityMB <= 504) {
-    numberOfHeads = 16;
-  } else if (cardCapacityMB <= 1008) {
-    numberOfHeads = 32;
-  } else if (cardCapacityMB <= 2016) {
-    numberOfHeads = 64;
-  } else if (cardCapacityMB <= 4032) {
-    numberOfHeads = 128;
-  } else {
-    numberOfHeads = 255;
-  }
-}
-//------------------------------------------------------------------------------
-// zero cache and optionally set the sector signature
-void clearCache(uint8_t addSig) {
-  memset(&cache, 0, sizeof(cache));
-  if (addSig) {
-    cache.mbr.mbrSig0 = BOOTSIG0;
-    cache.mbr.mbrSig1 = BOOTSIG1;
-  }
-}
-//------------------------------------------------------------------------------
-// zero FAT and root dir area on SD
-void clearFatDir(uint32_t bgn, uint32_t count) {
-  clearCache(false);
-#if USE_SDIO
-  for (uint32_t i = 0; i < count; i++) {
-    if (!card.writeBlock(bgn + i, cache.data)) {
-       sdError("Clear FAT/DIR writeBlock failed");
-    }
-    if ((i & 0XFF) == 0) {
-      cout << '.';
-    }
-  }
-#else  // USE_SDIO
-  if (!card.writeStart(bgn, count)) {
-    sdError("Clear FAT/DIR writeStart failed");
-  }
-  for (uint32_t i = 0; i < count; i++) {
-    if ((i & 0XFF) == 0) {
-      cout << '.';
-    }
-    if (!card.writeData(cache.data)) {
-      sdError("Clear FAT/DIR writeData failed");
-    }
-  }
-  if (!card.writeStop()) {
-    sdError("Clear FAT/DIR writeStop failed");
-  }
-#endif  // USE_SDIO
-  cout << endl;
-}
-//------------------------------------------------------------------------------
-// return cylinder number for a logical block number
-uint16_t lbnToCylinder(uint32_t lbn) {
-  return lbn / (numberOfHeads * sectorsPerTrack);
-}
-//------------------------------------------------------------------------------
-// return head number for a logical block number
-uint8_t lbnToHead(uint32_t lbn) {
-  return (lbn % (numberOfHeads * sectorsPerTrack)) / sectorsPerTrack;
-}
-//------------------------------------------------------------------------------
-// return sector number for a logical block number
-uint8_t lbnToSector(uint32_t lbn) {
-  return (lbn % sectorsPerTrack) + 1;
-}
-//------------------------------------------------------------------------------
-// format and write the Master Boot Record
-void writeMbr() {
-  clearCache(true);
-  part_t* p = cache.mbr.part;
-  p->boot = 0;
-  uint16_t c = lbnToCylinder(relSector);
-  if (c > 1023) {
-    sdError("MBR CHS");
-  }
-  p->beginCylinderHigh = c >> 8;
-  p->beginCylinderLow = c & 0XFF;
-  p->beginHead = lbnToHead(relSector);
-  p->beginSector = lbnToSector(relSector);
-  p->type = partType;
-  uint32_t endLbn = relSector + partSize - 1;
-  c = lbnToCylinder(endLbn);
-  if (c <= 1023) {
-    p->endCylinderHigh = c >> 8;
-    p->endCylinderLow = c & 0XFF;
-    p->endHead = lbnToHead(endLbn);
-    p->endSector = lbnToSector(endLbn);
-  } else {
-    // Too big flag, c = 1023, h = 254, s = 63
-    p->endCylinderHigh = 3;
-    p->endCylinderLow = 255;
-    p->endHead = 254;
-    p->endSector = 63;
-  }
-  p->firstSector = relSector;
-  p->totalSectors = partSize;
-  if (!writeCache(0)) {
-    sdError("write MBR");
-  }
-}
-//------------------------------------------------------------------------------
-// generate serial number from card size and micros since boot
-uint32_t volSerialNumber() {
-  return (cardSizeBlocks << 8) + micros();
-}
-//------------------------------------------------------------------------------
-// format the SD as FAT16
-void makeFat16() {
-  uint32_t nc;
-  for (dataStart = 2 * BU16;; dataStart += BU16) {
-    nc = (cardSizeBlocks - dataStart)/sectorsPerCluster;
-    fatSize = (nc + 2 + 255)/256;
-    uint32_t r = BU16 + 1 + 2 * fatSize + 32;
-    if (dataStart < r) {
-      continue;
-    }
-    relSector = dataStart - r + BU16;
-    break;
-  }
-  // check valid cluster count for FAT16 volume
-  if (nc < 4085 || nc >= 65525) {
-    sdError("Bad cluster count");
-  }
-  reservedSectors = 1;
-  fatStart = relSector + reservedSectors;
-  partSize = nc * sectorsPerCluster + 2 * fatSize + reservedSectors + 32;
-  if (partSize < 32680) {
-    partType = 0X01;
-  } else if (partSize < 65536) {
-    partType = 0X04;
-  } else {
-    partType = 0X06;
-  }
-  // write MBR
-  writeMbr();
-  clearCache(true);
-  fat_boot_t* pb = &cache.fbs;
-  pb->jump[0] = 0XEB;
-  pb->jump[1] = 0X00;
-  pb->jump[2] = 0X90;
-  for (uint8_t i = 0; i < sizeof(pb->oemId); i++) {
-    pb->oemId[i] = ' ';
-  }
-  pb->bytesPerSector = 512;
-  pb->sectorsPerCluster = sectorsPerCluster;
-  pb->reservedSectorCount = reservedSectors;
-  pb->fatCount = 2;
-  pb->rootDirEntryCount = 512;
-  pb->mediaType = 0XF8;
-  pb->sectorsPerFat16 = fatSize;
-  pb->sectorsPerTrack = sectorsPerTrack;
-  pb->headCount = numberOfHeads;
-  pb->hidddenSectors = relSector;
-  pb->totalSectors32 = partSize;
-  pb->driveNumber = 0X80;
-  pb->bootSignature = EXTENDED_BOOT_SIG;
-  pb->volumeSerialNumber = volSerialNumber();
-  memcpy(pb->volumeLabel, noName, sizeof(pb->volumeLabel));
-  memcpy(pb->fileSystemType, fat16str, sizeof(pb->fileSystemType));
-  // write partition boot sector
-  if (!writeCache(relSector)) {
-    sdError("FAT16 write PBS failed");
-  }
-  // clear FAT and root directory
-  clearFatDir(fatStart, dataStart - fatStart);
-  clearCache(false);
-  cache.fat16[0] = 0XFFF8;
-  cache.fat16[1] = 0XFFFF;
-  // write first block of FAT and backup for reserved clusters
-  if (!writeCache(fatStart)
-      || !writeCache(fatStart + fatSize)) {
-    sdError("FAT16 reserve failed");
-  }
-}
-//------------------------------------------------------------------------------
-// format the SD as FAT32
-void makeFat32() {
-  uint32_t nc;
-  relSector = BU32;
-  for (dataStart = 2 * BU32;; dataStart += BU32) {
-    nc = (cardSizeBlocks - dataStart)/sectorsPerCluster;
-    fatSize = (nc + 2 + 127)/128;
-    uint32_t r = relSector + 9 + 2 * fatSize;
-    if (dataStart >= r) {
-      break;
-    }
-  }
-  // error if too few clusters in FAT32 volume
-  if (nc < 65525) {
-    sdError("Bad cluster count");
-  }
-  reservedSectors = dataStart - relSector - 2 * fatSize;
-  fatStart = relSector + reservedSectors;
-  partSize = nc * sectorsPerCluster + dataStart - relSector;
-  // type depends on address of end sector
-  // max CHS has lbn = 16450560 = 1024*255*63
-  if ((relSector + partSize) <= 16450560) {
-    // FAT32
-    partType = 0X0B;
-  } else {
-    // FAT32 with INT 13
-    partType = 0X0C;
-  }
-  writeMbr();
-  clearCache(true);
-
-  fat32_boot_t* pb = &cache.fbs32;
-  pb->jump[0] = 0XEB;
-  pb->jump[1] = 0X00;
-  pb->jump[2] = 0X90;
-  for (uint8_t i = 0; i < sizeof(pb->oemId); i++) {
-    pb->oemId[i] = ' ';
-  }
-  pb->bytesPerSector = 512;
-  pb->sectorsPerCluster = sectorsPerCluster;
-  pb->reservedSectorCount = reservedSectors;
-  pb->fatCount = 2;
-  pb->mediaType = 0XF8;
-  pb->sectorsPerTrack = sectorsPerTrack;
-  pb->headCount = numberOfHeads;
-  pb->hidddenSectors = relSector;
-  pb->totalSectors32 = partSize;
-  pb->sectorsPerFat32 = fatSize;
-  pb->fat32RootCluster = 2;
-  pb->fat32FSInfo = 1;
-  pb->fat32BackBootBlock = 6;
-  pb->driveNumber = 0X80;
-  pb->bootSignature = EXTENDED_BOOT_SIG;
-  pb->volumeSerialNumber = volSerialNumber();
-  memcpy(pb->volumeLabel, noName, sizeof(pb->volumeLabel));
-  memcpy(pb->fileSystemType, fat32str, sizeof(pb->fileSystemType));
-  // write partition boot sector and backup
-  if (!writeCache(relSector)
-      || !writeCache(relSector + 6)) {
-    sdError("FAT32 write PBS failed");
-  }
-  clearCache(true);
-  // write extra boot area and backup
-  if (!writeCache(relSector + 2)
-      || !writeCache(relSector + 8)) {
-    sdError("FAT32 PBS ext failed");
-  }
-  fat32_fsinfo_t* pf = &cache.fsinfo;
-  pf->leadSignature = FSINFO_LEAD_SIG;
-  pf->structSignature = FSINFO_STRUCT_SIG;
-  pf->freeCount = 0XFFFFFFFF;
-  pf->nextFree = 0XFFFFFFFF;
-  // write FSINFO sector and backup
-  if (!writeCache(relSector + 1)
-      || !writeCache(relSector + 7)) {
-    sdError("FAT32 FSINFO failed");
-  }
-  clearFatDir(fatStart, 2 * fatSize + sectorsPerCluster);
-  clearCache(false);
-  cache.fat32[0] = 0x0FFFFFF8;
-  cache.fat32[1] = 0x0FFFFFFF;
-  cache.fat32[2] = 0x0FFFFFFF;
-  // write first block of FAT and backup for reserved clusters
-  if (!writeCache(fatStart)
-      || !writeCache(fatStart + fatSize)) {
-    sdError("FAT32 reserve failed");
-  }
-}
-//------------------------------------------------------------------------------
-// flash erase all data
-uint32_t const ERASE_SIZE = 262144L;
-void eraseCard() {
-  cout << endl << F("Erasing\n");
-  uint32_t firstBlock = 0;
-  uint32_t lastBlock;
-  uint16_t n = 0;
-
-  do {
-    lastBlock = firstBlock + ERASE_SIZE - 1;
-    if (lastBlock >= cardSizeBlocks) {
-      lastBlock = cardSizeBlocks - 1;
-    }
-    if (!card.erase(firstBlock, lastBlock)) {
-      sdError("erase failed");
-    }
-    cout << '.';
-    if ((n++)%32 == 31) {
-      cout << endl;
-    }
-    firstBlock += ERASE_SIZE;
-  } while (firstBlock < cardSizeBlocks);
-  cout << endl;
-
-  if (!card.readBlock(0, cache.data)) {
-    sdError("readBlock");
-  }
-  cout << hex << showbase << setfill('0') << internal;
-  cout << F("All data set to ") << setw(4) << int(cache.data[0]) << endl;
-  cout << dec << noshowbase << setfill(' ') << right;
-  cout << F("Erase done\n");
-}
-//------------------------------------------------------------------------------
-void formatCard() {
-  cout << endl;
-  cout << F("Formatting\n");
-  initSizes();
-  if (card.type() != SD_CARD_TYPE_SDHC) {
-    cout << F("FAT16\n");
-    makeFat16();
-  } else {
-    cout << F("FAT32\n");
-    makeFat32();
-  }
-#if DEBUG_PRINT
-  debugPrint();
-#endif  // DEBUG_PRINT
-  cout << F("Format done\n");
-}
-//------------------------------------------------------------------------------
-void setup() {
-  char c;
-  Serial.begin(9600);
-  // Wait for USB Serial
-  while (!Serial) {
-    yield();
-  }
-  cout << F("Type any character to start\n");
-  while (!Serial.available()) {
-    yield();
-  }
-  // Discard any extra characters.
-  do {
-    delay(10);
-  } while (Serial.available() && Serial.read() >= 0);
-  cout << F(
-         "\n"
-         "This program can erase and/or format SD/SDHC cards.\n"
-         "\n"
-         "Erase uses the card's fast flash erase command.\n"
-         "Flash erase sets all data to 0X00 for most cards\n"
-         "and 0XFF for a few vendor's cards.\n"
-         "\n"
-         "Cards larger than 2 GB will be formatted FAT32 and\n"
-         "smaller cards will be formatted FAT16.\n"
-         "\n"
-         "Warning, all data on the card will be erased.\n"
-         "Enter 'Y' to continue: ");
-  while (!Serial.available()) {
-    yield();
-  }
-
-  c = Serial.read();
-  cout << c << endl;
-  if (c != 'Y') {
-    cout << F("Quiting, you did not enter 'Y'.\n");
-    return;
-  }
-  // Read any existing Serial data.
-  do {
-    delay(10);
-  } while (Serial.available() && Serial.read() >= 0);
-
-  cout << F(
-         "\n"
-         "Options are:\n"
-         "E - erase the card and skip formatting.\n"
-         "F - erase and then format the card. (recommended)\n"
-         "Q - quick format the card without erase.\n"
-         "\n"
-         "Enter option: ");
-
-  while (!Serial.available()) {
-    yield();
-  }
-  c = Serial.read();
-  cout << c << endl;
-  if (!strchr("EFQ", c)) {
-    cout << F("Quiting, invalid option entered.") << endl;
-    return;
-  }
-#if USE_SDIO
-  if (!card.begin()) {
-    sdError("card.begin failed");
-  }
-#else  // USE_SDIO
-  if (!card.begin(chipSelect, SPI_SPEED)) {
-    cout << F(
-           "\nSD initialization failure!\n"
-           "Is the SD card inserted correctly?\n"
-           "Is chip select correct at the top of this program?\n");
-    sdError("card.begin failed");
-  }
-#endif
-  cardSizeBlocks = card.cardSize();
-  if (cardSizeBlocks == 0) {
-    sdError("cardSize");
-  }
-  cardCapacityMB = (cardSizeBlocks + 2047)/2048;
-
-  cout << F("Card Size: ") << setprecision(0) << 1.048576*cardCapacityMB;
-  cout << F(" MB, (MB = 1,000,000 bytes)") << endl;
-
-  if (c == 'E' || c == 'F') {
-    eraseCard();
-  }
-  if (c == 'F' || c == 'Q') {
-    formatCard();
-  }
-}
-//------------------------------------------------------------------------------
-void loop() {}

+ 0 - 248
examples/examplesV1/SdInfo/SdInfo.ino

@@ -1,248 +0,0 @@
-/*
- * This program attempts to initialize an SD card and analyze its structure.
- */
-#include <SPI.h>
-#include "SdFat.h"
-#include "sdios.h"
-#error Use new Version 2 SdInfo
-// Set USE_SDIO to zero for SPI card access.
-#define USE_SDIO 0
-/*
- * SD chip select pin.  Common values are:
- *
- * Arduino Ethernet shield, pin 4.
- * SparkFun SD shield, pin 8.
- * Adafruit SD shields and modules, pin 10.
- * Default SD chip select is the SPI SS pin.
- */
-const uint8_t SD_CHIP_SELECT = SS;
-/*
- * Set DISABLE_CHIP_SELECT to disable a second SPI device.
- * For example, with the Ethernet shield, set DISABLE_CHIP_SELECT
- * to 10 to disable the Ethernet controller.
- */
-const int8_t DISABLE_CHIP_SELECT = -1;
-
-#if USE_SDIO
-// Use faster SdioCardEX
-SdFatSdioEX sd;
-// SdFatSdio sd;
-#else // USE_SDIO
-SdFat sd;
-#endif  // USE_SDIO
-
-// serial output steam
-ArduinoOutStream cout(Serial);
-
-// global for card size
-uint32_t cardSize;
-
-// global for card erase size
-uint32_t eraseSize;
-//------------------------------------------------------------------------------
-// store error strings in flash
-#define sdErrorMsg(msg) sd.errorPrint(F(msg));
-//------------------------------------------------------------------------------
-uint8_t cidDmp() {
-  cid_t cid;
-  if (!sd.card()->readCID(&cid)) {
-    sdErrorMsg("readCID failed");
-    return false;
-  }
-  cout << F("\nManufacturer ID: ");
-  cout << hex << int(cid.mid) << dec << endl;
-  cout << F("OEM ID: ") << cid.oid[0] << cid.oid[1] << endl;
-  cout << F("Product: ");
-  for (uint8_t i = 0; i < 5; i++) {
-    cout << cid.pnm[i];
-  }
-  cout << F("\nVersion: ");
-  cout << int(cid.prv_n) << '.' << int(cid.prv_m) << endl;
-  cout << F("Serial number: ") << hex << cid.psn << dec << endl;
-  cout << F("Manufacturing date: ");
-  cout << int(cid.mdt_month) << '/';
-  cout << (2000 + cid.mdt_year_low + 10 * cid.mdt_year_high) << endl;
-  cout << endl;
-  return true;
-}
-//------------------------------------------------------------------------------
-uint8_t csdDmp() {
-  csd_t csd;
-  uint8_t eraseSingleBlock;
-  if (!sd.card()->readCSD(&csd)) {
-    sdErrorMsg("readCSD failed");
-    return false;
-  }
-  if (csd.v1.csd_ver == 0) {
-    eraseSingleBlock = csd.v1.erase_blk_en;
-    eraseSize = (csd.v1.sector_size_high << 1) | csd.v1.sector_size_low;
-  } else if (csd.v2.csd_ver == 1) {
-    eraseSingleBlock = csd.v2.erase_blk_en;
-    eraseSize = (csd.v2.sector_size_high << 1) | csd.v2.sector_size_low;
-  } else {
-    cout << F("csd version error\n");
-    return false;
-  }
-  eraseSize++;
-  cout << F("cardSize: ") << 0.000512*cardSize;
-  cout << F(" MB (MB = 1,000,000 bytes)\n");
-
-  cout << F("flashEraseSize: ") << int(eraseSize) << F(" blocks\n");
-  cout << F("eraseSingleBlock: ");
-  if (eraseSingleBlock) {
-    cout << F("true\n");
-  } else {
-    cout << F("false\n");
-  }
-  return true;
-}
-//------------------------------------------------------------------------------
-// print partition table
-uint8_t partDmp() {
-  mbr_t mbr;
-  if (!sd.card()->readBlock(0, (uint8_t*)&mbr)) {
-    sdErrorMsg("read MBR failed");
-    return false;
-  }
-  for (uint8_t ip = 1; ip < 5; ip++) {
-    part_t *pt = &mbr.part[ip - 1];
-    if ((pt->boot & 0X7F) != 0 || pt->firstSector > cardSize) {
-      cout << F("\nNo MBR. Assuming Super Floppy format.\n");
-      return true;
-    }
-  }
-  cout << F("\nSD Partition Table\n");
-  cout << F("part,boot,type,start,length\n");
-  for (uint8_t ip = 1; ip < 5; ip++) {
-    part_t *pt = &mbr.part[ip - 1];
-    cout << int(ip) << ',' << hex << int(pt->boot) << ',' << int(pt->type);
-    cout << dec << ',' << pt->firstSector <<',' << pt->totalSectors << endl;
-  }
-  return true;
-}
-//------------------------------------------------------------------------------
-void volDmp() {
-  cout << F("\nVolume is FAT") << int(sd.vol()->fatType()) << endl;
-  cout << F("blocksPerCluster: ") << int(sd.vol()->blocksPerCluster()) << endl;
-  cout << F("clusterCount: ") << sd.vol()->clusterCount() << endl;
-  cout << F("freeClusters: ");
-  uint32_t volFree = sd.vol()->freeClusterCount();
-  cout <<  volFree << endl;
-  float fs = 0.000512*volFree*sd.vol()->blocksPerCluster();
-  cout << F("freeSpace: ") << fs << F(" MB (MB = 1,000,000 bytes)\n");
-  cout << F("fatStartBlock: ") << sd.vol()->fatStartBlock() << endl;
-  cout << F("fatCount: ") << int(sd.vol()->fatCount()) << endl;
-  cout << F("blocksPerFat: ") << sd.vol()->blocksPerFat() << endl;
-  cout << F("rootDirStart: ") << sd.vol()->rootDirStart() << endl;
-  cout << F("dataStartBlock: ") << sd.vol()->dataStartBlock() << endl;
-  if (sd.vol()->dataStartBlock() % eraseSize) {
-    cout << F("Data area is not aligned on flash erase boundaries!\n");
-    cout << F("Download and use formatter from www.sdcard.org!\n");
-  }
-}
-//------------------------------------------------------------------------------
-void setup() {
-  Serial.begin(9600);
-
-  // Wait for USB Serial
-  while (!Serial) {
-    yield();
-  }
-
-  // use uppercase in hex and use 0X base prefix
-  cout << uppercase << showbase << endl;
-
-  // F stores strings in flash to save RAM
-  cout << F("SdFat version: ") << SD_FAT_VERSION << endl;
-#if !USE_SDIO
-  if (DISABLE_CHIP_SELECT < 0) {
-    cout << F(
-           "\nAssuming the SD is the only SPI device.\n"
-           "Edit DISABLE_CHIP_SELECT to disable another device.\n");
-  } else {
-    cout << F("\nDisabling SPI device on pin ");
-    cout << int(DISABLE_CHIP_SELECT) << endl;
-    pinMode(DISABLE_CHIP_SELECT, OUTPUT);
-    digitalWrite(DISABLE_CHIP_SELECT, HIGH);
-  }
-  cout << F("\nAssuming the SD chip select pin is: ") <<int(SD_CHIP_SELECT);
-  cout << F("\nEdit SD_CHIP_SELECT to change the SD chip select pin.\n");
-#endif  // !USE_SDIO
-}
-//------------------------------------------------------------------------------
-void loop() {
-  // Read any existing Serial data.
-  do {
-    delay(10);
-  } while (Serial.available() && Serial.read() >= 0);
-
-  // F stores strings in flash to save RAM
-  cout << F("\ntype any character to start\n");
-  while (!Serial.available()) {
-    yield();
-  }
-
-  uint32_t t = millis();
-#if USE_SDIO
-  if (!sd.cardBegin()) {
-    sdErrorMsg("\ncardBegin failed");
-    return;
-  }
-#else  // USE_SDIO
-  // Initialize at the highest speed supported by the board that is
-  // not over 50 MHz. Try a lower speed if SPI errors occur.
-  if (!sd.cardBegin(SD_CHIP_SELECT, SD_SCK_MHZ(50))) {
-    sdErrorMsg("cardBegin failed");
-    return;
-  }
- #endif  // USE_SDIO
-  t = millis() - t;
-
-  cardSize = sd.card()->cardSize();
-  if (cardSize == 0) {
-    sdErrorMsg("cardSize failed");
-    return;
-  }
-  cout << F("\ninit time: ") << t << " ms" << endl;
-  cout << F("\nCard type: ");
-  switch (sd.card()->type()) {
-  case SD_CARD_TYPE_SD1:
-    cout << F("SD1\n");
-    break;
-
-  case SD_CARD_TYPE_SD2:
-    cout << F("SD2\n");
-    break;
-
-  case SD_CARD_TYPE_SDHC:
-    if (cardSize < 70000000) {
-      cout << F("SDHC\n");
-    } else {
-      cout << F("SDXC\n");
-    }
-    break;
-
-  default:
-    cout << F("Unknown\n");
-  }
-  if (!cidDmp()) {
-    return;
-  }
-  if (!csdDmp()) {
-    return;
-  }
-  uint32_t ocr;
-  if (!sd.card()->readOCR(&ocr)) {
-    sdErrorMsg("\nreadOCR failed");
-    return;
-  }
-  cout << F("OCR: ") << hex << ocr << dec << endl;
-  if (!partDmp()) {
-    return;
-  }
-  if (!sd.fsBegin()) {
-    sdErrorMsg("\nFile System initialization failed.\n");
-    return;
-  }
-  volDmp();
-}

+ 0 - 59
examples/examplesV1/SoftwareSpi/SoftwareSpi.ino

@@ -1,59 +0,0 @@
-// An example of the SdFatSoftSpi template class.
-// This example is for an Adafruit Data Logging Shield on a Mega.
-// Software SPI is required on Mega since this shield connects to pins 10-13.
-// This example will also run on an Uno and other boards using software SPI.
-//
-#include <SPI.h>
-#include "SdFat.h"
-#error See Version 2 software SPI example
-#if ENABLE_SOFTWARE_SPI_CLASS  // Must be set in SdFat/SdFatConfig.h
-//
-// Pin numbers in templates must be constants.
-const uint8_t SOFT_MISO_PIN = 12;
-const uint8_t SOFT_MOSI_PIN = 11;
-const uint8_t SOFT_SCK_PIN  = 13;
-//
-// Chip select may be constant or RAM variable.
-const uint8_t SD_CHIP_SELECT_PIN = 10;
-
-// SdFat software SPI template
-SdFatSoftSpi<SOFT_MISO_PIN, SOFT_MOSI_PIN, SOFT_SCK_PIN> sd;
-
-// Test file.
-SdFile file;
-
-void setup() {
-  Serial.begin(9600);
-  // Wait for USB Serial
-  while (!Serial) {
-    yield();
-  }
-  Serial.println("Type any character to start");
-  while (!Serial.available()) {
-    yield();
-  }
-
-  if (!sd.begin(SD_CHIP_SELECT_PIN)) {
-    sd.initErrorHalt();
-  }
-
-  if (!file.open("SoftSPI.txt", O_RDWR | O_CREAT)) {
-    sd.errorHalt(F("open failed"));
-  }
-  file.println(F("This line was printed using software SPI."));
-
-  file.rewind();
-
-  while (file.available()) {
-    Serial.write(file.read());
-  }
-
-  file.close();
-
-  Serial.println(F("Done."));
-}
-//------------------------------------------------------------------------------
-void loop() {}
-#else  // ENABLE_SOFTWARE_SPI_CLASS
-#error ENABLE_SOFTWARE_SPI_CLASS must be set non-zero in SdFat/SdFatConfig.h
-#endif  //ENABLE_SOFTWARE_SPI_CLASS

+ 0 - 169
examples/examplesV1/TeensySdioDemo/TeensySdioDemo.ino

@@ -1,169 +0,0 @@
-// Simple performance test for Teensy 3.5/3.6 SDHC.
-// Demonstrates yield() efficiency.
-
-// Warning SdFatSdio and SdFatSdioEX normally should
-// not both be used in a program.
-// Each has its own cache and member variables.
-
-#include "SdFat.h"
-#error See Version 2 SDIO example
-// 32 KiB buffer.
-const size_t BUF_DIM = 32768;
-
-// 8 MiB file.
-const uint32_t FILE_SIZE = 256UL*BUF_DIM;
-
-SdFatSdio sd;
-
-SdFatSdioEX sdEx;
-
-File file;
-
-uint8_t buf[BUF_DIM];
-
-// buffer as uint32_t
-uint32_t* buf32 = (uint32_t*)buf;
-
-// Total usec in read/write calls.
-uint32_t totalMicros = 0;
-// Time in yield() function.
-uint32_t yieldMicros = 0;
-// Number of yield calls.
-uint32_t yieldCalls = 0;
-// Max busy time for single yield call.
-uint32_t yieldMaxUsec = 0;
-// Control access to the two versions of SdFat.
-bool useEx = false;
-//-----------------------------------------------------------------------------
-bool sdBusy() {
-  return useEx ? sdEx.card()->isBusy() : sd.card()->isBusy();
-}
-//-----------------------------------------------------------------------------
-void errorHalt(const char* msg) {
-  if (useEx) {
-    sdEx.errorHalt(msg);
-  } else {
-    sd.errorHalt(msg);
-  }
-}
-//------------------------------------------------------------------------------
-uint32_t kHzSdClk() {
-  return useEx ? sdEx.card()->kHzSdClk() : sd.card()->kHzSdClk();
-}
-//------------------------------------------------------------------------------
-// Replace "weak" system yield() function.
-void yield() {
-  // Only count cardBusy time.
-  if (!sdBusy()) {
-    return;
-  }
-  uint32_t m = micros();
-  yieldCalls++;
-  while (sdBusy()) {
-    // Do something here.
-  }
-  m = micros() - m;
-  if (m > yieldMaxUsec) {
-    yieldMaxUsec = m;
-  }
-  yieldMicros += m;
-}
-//-----------------------------------------------------------------------------
-void runTest() {
-  // Zero Stats
-  totalMicros = 0;
-  yieldMicros = 0;
-  yieldCalls = 0;
-  yieldMaxUsec = 0;
-  if (!file.open("TeensyDemo.bin", O_RDWR | O_CREAT)) {
-    errorHalt("open failed");
-  }
-  Serial.println("\nsize,write,read");
-  Serial.println("bytes,KB/sec,KB/sec");
-  for (size_t nb = 512; nb <= BUF_DIM; nb *= 2) {
-    file.truncate(0);
-    uint32_t nRdWr = FILE_SIZE/nb;
-    Serial.print(nb);
-    Serial.print(',');
-    uint32_t t = micros();
-    for (uint32_t n = 0; n < nRdWr; n++) {
-      // Set start and end of buffer.
-      buf32[0] = n;
-      buf32[nb/4 - 1] = n;
-      if (nb != file.write(buf, nb)) {
-        errorHalt("write failed");
-      }
-    }
-    t = micros() - t;
-    totalMicros += t;
-    Serial.print(1000.0*FILE_SIZE/t);
-    Serial.print(',');
-    file.rewind();
-    t = micros();
-
-    for (uint32_t n = 0; n < nRdWr; n++) {
-      if ((int)nb != file.read(buf, nb)) {
-        errorHalt("read failed");
-      }
-      // crude check of data.
-      if (buf32[0] != n || buf32[nb/4 - 1] != n) {
-        errorHalt("data check");
-      }
-    }
-    t = micros() - t;
-    totalMicros += t;
-    Serial.println(1000.0*FILE_SIZE/t);
-  }
-  file.close();
-  Serial.print("\ntotalMicros  ");
-  Serial.println(totalMicros);
-  Serial.print("yieldMicros  ");
-  Serial.println(yieldMicros);
-  Serial.print("yieldCalls   ");
-  Serial.println(yieldCalls);
-  Serial.print("yieldMaxUsec ");
-  Serial.println(yieldMaxUsec);
-  Serial.print("kHzSdClk     ");
-  Serial.println(kHzSdClk());
-  Serial.println("Done");
-}
-//-----------------------------------------------------------------------------
-void setup() {
-  Serial.begin(9600);
-  while (!Serial) {
-  }
-  Serial.println("SdFatSdioEX uses extended multi-block transfers without DMA.");
-  Serial.println("SdFatSdio uses a traditional DMA SDIO implementation.");
-  Serial.println("Note the difference is speed and busy yield time.\n");
-}
-//-----------------------------------------------------------------------------
-void loop() {
-  do {
-    delay(10);
-  } while (Serial.available() && Serial.read());
-
-  Serial.println("Type '1' for SdFatSdioEX or '2' for SdFatSdio");
-  while (!Serial.available()) {
-  }
-  char c = Serial.read();
-  if (c != '1' && c != '2') {
-    Serial.println("Invalid input");
-    return;
-  }
-  if (c =='1') {
-    useEx = true;
-    if (!sdEx.begin()) {
-      sd.initErrorHalt("SdFatSdioEX begin() failed");
-    }
-    // make sdEx the current volume.
-    sdEx.chvol();
-  } else {
-    useEx = false;
-    if (!sd.begin()) {
-      sd.initErrorHalt("SdFatSdio begin() failed");
-    }
-    // make sd the current volume.
-    sd.chvol();
-  }
-  runTest();
-}

+ 0 - 222
examples/examplesV1/bench/bench.ino

@@ -1,222 +0,0 @@
-/*
- * This program is a simple binary write/read benchmark.
- */
-#include <SPI.h>
-#include "SdFat.h"
-#include "sdios.h"
-#include "FreeStack.h"
-
-// Set USE_SDIO to zero for SPI card access.
-#define USE_SDIO 0
-
-// SD chip select pin
-const uint8_t chipSelect = SS;
-
-// Size of read/write.
-const size_t BUF_SIZE = 512;
-
-// File size in MB where MB = 1,000,000 bytes.
-const uint32_t FILE_SIZE_MB = 5;
-
-// Write pass count.
-const uint8_t WRITE_COUNT = 2;
-
-// Read pass count.
-const uint8_t READ_COUNT = 2;
-//==============================================================================
-// End of configuration constants.
-//------------------------------------------------------------------------------
-// File size in bytes.
-const uint32_t FILE_SIZE = 1000000UL*FILE_SIZE_MB;
-
-uint8_t buf[BUF_SIZE];
-
-// file system
-#if USE_SDIO
-// Traditional DMA version.
-// SdFatSdio sd;
-// Faster version.
-SdFatSdioEX sd;
-#else  // USE_SDIO
-SdFat sd;
-#endif  // USE_SDIO
-
-// Set ENABLE_EXTENDED_TRANSFER_CLASS to use extended SD I/O.
-// Requires dedicated use of the SPI bus.
-// SdFatEX sd;
-
-// Set ENABLE_SOFTWARE_SPI_CLASS to use software SPI.
-// Args are misoPin, mosiPin, sckPin.
-// SdFatSoftSpi<6, 7, 5> sd;
-
-// test file
-SdFile file;
-
-// Serial output stream
-ArduinoOutStream cout(Serial);
-//------------------------------------------------------------------------------
-// Store error strings in flash to save RAM.
-#define error(s) sd.errorHalt(F(s))
-//------------------------------------------------------------------------------
-void cidDmp() {
-  cid_t cid;
-  if (!sd.card()->readCID(&cid)) {
-    error("readCID failed");
-  }
-  cout << F("\nManufacturer ID: ");
-  cout << hex << int(cid.mid) << dec << endl;
-  cout << F("OEM ID: ") << cid.oid[0] << cid.oid[1] << endl;
-  cout << F("Product: ");
-  for (uint8_t i = 0; i < 5; i++) {
-    cout << cid.pnm[i];
-  }
-  cout << F("\nVersion: ");
-  cout << int(cid.prv_n) << '.' << int(cid.prv_m) << endl;
-  cout << F("Serial number: ") << hex << cid.psn << dec << endl;
-  cout << F("Manufacturing date: ");
-  cout << int(cid.mdt_month) << '/';
-  cout << (2000 + cid.mdt_year_low + 10 * cid.mdt_year_high) << endl;
-  cout << endl;
-}
-//------------------------------------------------------------------------------
-void setup() {
-  Serial.begin(9600);
-
-  // Wait for USB Serial
-  while (!Serial) {
-    yield();
-  }
-  delay(1000);
-  cout << F("\nUse a freshly formatted SD for best performance.\n");
-
-  // use uppercase in hex and use 0X base prefix
-  cout << uppercase << showbase << endl;
-}
-//------------------------------------------------------------------------------
-void loop() {
-  float s;
-  uint32_t t;
-  uint32_t maxLatency;
-  uint32_t minLatency;
-  uint32_t totalLatency;
-
-  // Discard any input.
-  do {
-    delay(10);
-  } while (Serial.available() && Serial.read() >= 0);
-
-  // F( stores strings in flash to save RAM
-  cout << F("Type any character to start\n");
-  while (!Serial.available()) {
-    yield();
-  }
-  cout << F("chipSelect: ") << int(chipSelect) << endl;
-  cout << F("FreeStack: ") << FreeStack() << endl;
-
-#if USE_SDIO
-  if (!sd.begin()) {
-    sd.initErrorHalt();
-  }
-#else  // USE_SDIO
-  // Initialize at the highest speed supported by the board that is
-  // not over 50 MHz. Try a lower speed if SPI errors occur.
-  if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
-    sd.initErrorHalt();
-  }
-#endif  // USE_SDIO
-  cout << F("Type is FAT") << int(sd.vol()->fatType()) << endl;
-  cout << F("Card size: ") << sd.card()->cardSize()*512E-9;
-  cout << F(" GB (GB = 1E9 bytes)") << endl;
-
-  cidDmp();
-
-  // open or create file - truncate existing file.
-  if (!file.open("bench.dat", O_RDWR | O_CREAT | O_TRUNC)) {
-    error("open failed");
-  }
-
-  // fill buf with known data
-  for (size_t i = 0; i < (BUF_SIZE-2); i++) {
-    buf[i] = 'A' + (i % 26);
-  }
-  buf[BUF_SIZE-2] = '\r';
-  buf[BUF_SIZE-1] = '\n';
-
-  cout << F("File size ") << FILE_SIZE_MB << F(" MB\n");
-  cout << F("Buffer size ") << BUF_SIZE << F(" bytes\n");
-  cout << F("Starting write test, please wait.") << endl << endl;
-
-  // do write test
-  uint32_t n = FILE_SIZE/sizeof(buf);
-  cout <<F("write speed and latency") << endl;
-  cout << F("speed,max,min,avg") << endl;
-  cout << F("KB/Sec,usec,usec,usec") << endl;
-  for (uint8_t nTest = 0; nTest < WRITE_COUNT; nTest++) {
-    file.truncate(0);
-    maxLatency = 0;
-    minLatency = 9999999;
-    totalLatency = 0;
-    t = millis();
-    for (uint32_t i = 0; i < n; i++) {
-      uint32_t m = micros();
-      if (file.write(buf, sizeof(buf)) != sizeof(buf)) {
-        sd.errorPrint("write failed");
-        file.close();
-        return;
-      }
-      m = micros() - m;
-      if (maxLatency < m) {
-        maxLatency = m;
-      }
-      if (minLatency > m) {
-        minLatency = m;
-      }
-      totalLatency += m;
-    }
-    file.sync();
-    t = millis() - t;
-    s = file.fileSize();
-    cout << s/t <<',' << maxLatency << ',' << minLatency;
-    cout << ',' << totalLatency/n << endl;
-  }
-  cout << endl << F("Starting read test, please wait.") << endl;
-  cout << endl <<F("read speed and latency") << endl;
-  cout << F("speed,max,min,avg") << endl;
-  cout << F("KB/Sec,usec,usec,usec") << endl;
-
-  // do read test
-  for (uint8_t nTest = 0; nTest < READ_COUNT; nTest++) {
-    file.rewind();
-    maxLatency = 0;
-    minLatency = 9999999;
-    totalLatency = 0;
-    t = millis();
-    for (uint32_t i = 0; i < n; i++) {
-      buf[BUF_SIZE-1] = 0;
-      uint32_t m = micros();
-      int32_t nr = file.read(buf, sizeof(buf));
-      if (nr != sizeof(buf)) {
-        sd.errorPrint("read failed");
-        file.close();
-        return;
-      }
-      m = micros() - m;
-      if (maxLatency < m) {
-        maxLatency = m;
-      }
-      if (minLatency > m) {
-        minLatency = m;
-      }
-      totalLatency += m;
-      if (buf[BUF_SIZE-1] != '\n') {
-        error("data check");
-      }
-    }
-    s = file.fileSize();
-    t = millis() - t;
-    cout << s/t <<',' << maxLatency << ',' << minLatency;
-    cout << ',' << totalLatency/n << endl;
-  }
-  cout << endl << F("Done") << endl;
-  file.close();
-}

+ 0 - 106
examples/examplesV1/rename/rename.ino

@@ -1,106 +0,0 @@
-/*
- * This program demonstrates use of SdFile::rename()
- * and SdFat::rename().
- */
-#include <SPI.h>
-#include "SdFat.h"
-#include "sdios.h"
-
-// SD chip select pin
-const uint8_t chipSelect = SS;
-
-// file system
-SdFat sd;
-
-// Serial print stream
-ArduinoOutStream cout(Serial);
-//------------------------------------------------------------------------------
-// store error strings in flash to save RAM
-#define error(s) sd.errorHalt(F(s))
-//------------------------------------------------------------------------------
-void setup() {
-  Serial.begin(9600);
-
-  // Wait for USB Serial
-  while (!Serial) {
-    yield();
-  }
-  cout << F("Insert an empty SD.  Type any character to start.") << endl;
-  while (!Serial.available()) {
-    yield();
-  }
-
-  // Initialize at the highest speed supported by the board that is
-  // not over 50 MHz. Try a lower speed if SPI errors occur.
-  if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
-    sd.initErrorHalt();
-  }
-
-  // Remove file/dirs from previous run.
-  if (sd.exists("dir2/DIR3/NAME3.txt")) {
-    cout << F("Removing /dir2/DIR3/NAME3.txt") << endl;
-    if (!sd.remove("dir2/DIR3/NAME3.txt") ||
-        !sd.rmdir("dir2/DIR3/") ||
-        !sd.rmdir("dir2/")) {
-      error("remove/rmdir failed");
-    }
-  }
-  // create a file and write one line to the file
-  SdFile file("Name1.txt", O_WRONLY | O_CREAT);
-  if (!file.isOpen()) {
-    error("Name1.txt");
-  }
-  file.println("A test line for Name1.txt");
-
-  // rename the file name2.txt and add a line.
-  if (!file.rename("name2.txt")) {
-    error("name2.txt");
-  }
-  file.println("A test line for name2.txt");
-
-  // list files
-  cout << F("------") << endl;
-  sd.ls(LS_R);
-
-  // make a new directory - "Dir1"
-  if (!sd.mkdir("Dir1")) {
-    error("Dir1");
-  }
-
-  // move file into Dir1, rename it NAME3.txt and add a line
-  if (!file.rename("Dir1/NAME3.txt")) {
-    error("NAME3.txt");
-  }
-  file.println("A line for Dir1/NAME3.txt");
-
-  // list files
-  cout << F("------") << endl;
-  sd.ls(LS_R);
-
-  // make directory "dir2"
-  if (!sd.mkdir("dir2")) {
-    error("dir2");
-  }
-
-  // close file before rename(oldPath, newPath)
-  file.close();
-
-  // move Dir1 into dir2 and rename it DIR3
-  if (!sd.rename("Dir1", "dir2/DIR3")) {
-    error("dir2/DIR3");
-  }
-
-  // open file for append in new location and add a line
-  if (!file.open("dir2/DIR3/NAME3.txt", O_WRONLY | O_APPEND)) {
-    error("dir2/DIR3/NAME3.txt");
-  }
-  file.println("A line for dir2/DIR3/NAME3.txt");
-  file.close();
-
-  // list files
-  cout << F("------") << endl;
-  sd.ls(LS_R);
-
-  cout << F("Done") << endl;
-}
-void loop() {}

+ 1 - 1
library.properties

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

+ 1 - 1
src/BufferedPrint.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/ExFatLib/ExFatConfig.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 3 - 3
src/ExFatLib/ExFatDbg.cpp

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License
@@ -455,7 +455,7 @@ bool ExFatPartition::printDir(print_t* pr, ExFatFile* file) {
       dir++;
     }
 #endif  // RAW_ROOT
-    if (dir->type == 0) {
+    if (dir->type == EXFAT_TYPE_END_DIR) {
       break;
     }
     pr->println();
@@ -520,7 +520,7 @@ bool ExFatPartition::printDir(print_t* pr, ExFatFile* file) {
         break;
 
       default:
-        if (dir->type & 0x80) {
+        if (dir->type & EXFAT_TYPE_USED) {
           pr->print(F("Unknown dirType: 0x"));
         } else {
           pr->print(F("Unused dirType: 0x"));

+ 50 - 6
src/ExFatLib/ExFatFile.cpp

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License
@@ -41,6 +41,29 @@ inline bool lfnLegalChar(uint8_t c) {
 #endif  // USE_UTF8_LONG_NAMES
 }
 //------------------------------------------------------------------------------
+bool ExFatFile::attrib(uint8_t bits) {
+  if (!isFileOrSubDir() || (bits & FS_ATTRIB_USER_SETTABLE) != bits) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // Don't allow read-only to be set if the file is open for write.
+  if ((bits & FS_ATTRIB_READ_ONLY) && isWritable()) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  m_attributes = (m_attributes & ~FS_ATTRIB_USER_SETTABLE) | bits;
+  // insure sync() will update dir entry
+  m_flags |= FILE_FLAG_DIR_DIRTY;
+  if (!sync()) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  return true;
+
+ fail:
+  return false;
+}
+//------------------------------------------------------------------------------
 uint8_t* ExFatFile::dirCache(uint8_t set, uint8_t options) {
   DirPos_t pos = m_dirPos;
   if (m_vol->dirSeek(&pos, FS_DIR_SIZE*set) != 1) {
@@ -212,6 +235,11 @@ bool ExFatFile::open(ExFatFile* dirFile, const char* path, oflag_t oflag) {
   return false;
 }
 //------------------------------------------------------------------------------
+bool ExFatFile::open(uint32_t index, oflag_t oflag) {
+  ExFatVolume* vol = ExFatVolume::cwv();
+  return vol ? open(vol->vwd(), index, oflag) : false;
+}
+//------------------------------------------------------------------------------
 bool ExFatFile::open(ExFatFile* dirFile, uint32_t index, oflag_t oflag) {
   if (dirFile->seekSet(FS_DIR_SIZE*index) && openNext(dirFile, oflag)) {
     if (dirIndex() == index) {
@@ -223,6 +251,19 @@ bool ExFatFile::open(ExFatFile* dirFile, uint32_t index, oflag_t oflag) {
   return false;
 }
 //------------------------------------------------------------------------------
+bool ExFatFile::openCwd() {
+  if (isOpen() || !ExFatVolume::cwv()) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  *this = *ExFatVolume::cwv()->vwd();
+  rewind();
+  return true;
+
+ fail:
+  return false;
+}
+//------------------------------------------------------------------------------
 bool ExFatFile::openNext(ExFatFile* dir, oflag_t oflag) {
   if (isOpen() || !dir->isDir() || (dir->curPosition() & 0X1F)) {
     DBG_FAIL_MACRO;
@@ -285,7 +326,7 @@ bool ExFatFile::openPrivate(ExFatFile* dir, ExName_t* fname, oflag_t oflag) {
       DBG_FAIL_MACRO;
       goto fail;
     }
-    if (!(buf[0] & 0x80)) {
+    if (!(buf[0] & EXFAT_TYPE_USED)) {
       // Unused entry.
       if (freeCount == 0) {
         freePos.position = dir->curPosition() - FS_DIR_SIZE;
@@ -294,7 +335,7 @@ bool ExFatFile::openPrivate(ExFatFile* dir, ExName_t* fname, oflag_t oflag) {
       if (freeCount < freeNeed) {
         freeCount++;
       }
-      if (!buf[0]) {
+      if (buf[0] == EXFAT_TYPE_END_DIR) {
         if (fname) {
           goto create;
         }
@@ -314,8 +355,8 @@ bool ExFatFile::openPrivate(ExFatFile* dir, ExName_t* fname, oflag_t oflag) {
       memset(this, 0, sizeof(ExFatFile));
       dirFile = reinterpret_cast<DirFile_t*>(buf);
       m_setCount = dirFile->setCount;
-      m_attributes = getLe16(dirFile->attributes) & FILE_ATTR_COPY;
-      if (!(m_attributes & EXFAT_ATTRIB_DIRECTORY)) {
+      m_attributes = getLe16(dirFile->attributes) & FS_ATTRIB_COPY;
+      if (!(m_attributes & FS_ATTRIB_DIRECTORY)) {
         m_attributes |= FILE_ATTR_FILE;
       }
       m_vol = dir->volume();
@@ -381,6 +422,9 @@ bool ExFatFile::openPrivate(ExFatFile* dir, ExName_t* fname, oflag_t oflag) {
     DBG_FAIL_MACRO;
     goto fail;
   }
+  if (isWritable()) {
+    m_attributes |= FS_ATTRIB_ARCHIVE;
+  }
 #endif  // !EXFAT_READ_ONLY
   return true;
 
@@ -418,7 +462,7 @@ bool ExFatFile::openPrivate(ExFatFile* dir, ExName_t* fname, oflag_t oflag) {
   freePos.isContiguous = dir->isContiguous();
   memset(this, 0, sizeof(ExFatFile));
   m_vol = dir->volume();
-  m_attributes = FILE_ATTR_FILE;
+  m_attributes = FILE_ATTR_FILE | FS_ATTRIB_ARCHIVE;
   m_dirPos = freePos;
   fname->reset();
   for (uint8_t i = 0; i < freeNeed; i++) {

+ 48 - 23
src/ExFatLib/ExFatFile.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License
@@ -87,6 +87,21 @@ class ExFatFile {
   operator bool() {
     return isOpen();
   }
+  /**
+   * \return user settable file attributes for success else -1.
+   */
+  int attrib() {
+    return isFileOrSubDir() ? m_attributes & FS_ATTRIB_COPY : -1;
+  }
+  /** Set file attributes
+   *
+   * \param[in] bits bit-wise or of selected attributes: FS_ATTRIB_READ_ONLY,
+   *            FS_ATTRIB_HIDDEN, FS_ATTRIB_SYSTEM, FS_ATTRIB_ARCHIVE.
+   *
+   * \note attrib() will fail for set read-only if the file is open for write.
+   * \return true for success or false for failure.
+   */
+  bool attrib(uint8_t bits);
   /** \return The number of bytes available from the current position
    * to EOF for normal files.  INT_MAX is returned for very large files.
    *
@@ -129,9 +144,10 @@ class ExFatFile {
    * \return true for success or false for failure.
    */
   bool contiguousRange(uint32_t* bgnSector, uint32_t* endSector);
+  /** \return The current cluster number for a file or directory. */
+  uint32_t curCluster() const {return m_curCluster;}
   /** \return The current position for a file or directory. */
   uint64_t curPosition() const {return m_curPosition;}
-
   /** \return Total data length for file. */
   uint64_t dataLength() const {return m_dataLength;}
   /** \return Directory entry index. */
@@ -261,18 +277,22 @@ class ExFatFile {
   bool isDir() const  {return m_attributes & FILE_ATTR_DIR;}
   /** \return True if this is a normal file. */
   bool isFile() const {return m_attributes & FILE_ATTR_FILE;}
+  /** \return True if this is a normal file or sub-directory. */
+  bool isFileOrSubDir() const {return isFile() || isSubDir();}
   /** \return True if this is a hidden. */
-  bool isHidden() const {return m_attributes & FILE_ATTR_HIDDEN;}
+  bool isHidden() const {return m_attributes & FS_ATTRIB_HIDDEN;}
   /** \return true if the file is open. */
   bool isOpen() const {return m_attributes;}
   /** \return True if file is read-only */
-  bool isReadOnly() const {return m_attributes & FILE_ATTR_READ_ONLY;}
+  bool isReadOnly() const {return m_attributes & FS_ATTRIB_READ_ONLY;}
   /** \return True if this is the root directory. */
   bool isRoot() const {return m_attributes & FILE_ATTR_ROOT;}
   /** \return True file is readable. */
   bool isReadable() const {return m_flags & FILE_FLAG_READ;}
-  /** \return True if this is a subdirectory. */
+  /** \return True if this is a sub-directory. */
   bool isSubDir() const {return m_attributes & FILE_ATTR_SUBDIR;}
+  /** \return True if this is a system file. */
+  bool isSystem() const {return m_attributes & FS_ATTRIB_SYSTEM;}
   /** \return True file is writable. */
   bool isWritable() const {return m_flags & FILE_FLAG_WRITE;}
   /** List directory contents.
@@ -291,10 +311,10 @@ class ExFatFile {
    *
    * LS_SIZE - %Print file size.
    *
-   * LS_R - Recursive list of subdirectories.
+   * LS_R - Recursive list of sub-directories.
    *
    * \param[in] indent Amount of space before file name. Used for recursive
-   * list to indicate subdirectory level.
+   * list to indicate sub-directory level.
    *
    * \return true for success or false for failure.
    */
@@ -355,7 +375,7 @@ class ExFatFile {
    *
    * \return true for success or false for failure.
    */
-  bool open(ExFatFile* dirFile, const char* path, oflag_t oflag);
+  bool open(ExFatFile* dirFile, const char* path, oflag_t oflag = O_RDONLY);
   /** Open a file in the volume working directory.
    *
    * \param[in] vol Volume where the file is located.
@@ -367,7 +387,7 @@ class ExFatFile {
    *
    * \return true for success or false for failure.
    */
-  bool open(ExFatVolume* vol, const char* path, oflag_t oflag);
+  bool open(ExFatVolume* vol, const char* path, oflag_t oflag = O_RDONLY);
   /** Open a file by index.
    *
    * \param[in] dirFile An open ExFatFile instance for the directory.
@@ -381,7 +401,19 @@ class ExFatFile {
    * See open() by path for definition of flags.
    * \return true for success or false for failure.
    */
-  bool open(ExFatFile* dirFile, uint32_t index, oflag_t oflag);
+  bool open(ExFatFile* dirFile, uint32_t index, oflag_t oflag = O_RDONLY);
+  /** Open a file by index in the current working directory.
+   *
+   * \param[in] index The \a index of the directory entry for the file to be
+   * opened.  The value for \a index is (directory file position)/32.
+   *
+   * \param[in] oflag bitwise-inclusive OR of open flags.
+   *                  See see FatFile::open(FatFile*, const char*, uint8_t).
+   *
+   * See open() by path for definition of flags.
+   * \return true for success or false for failure.
+   */
+  bool open(uint32_t index, oflag_t oflag = O_RDONLY);
   /** Open a file in the current working directory.
    *
    * \param[in] path A path with a valid name for a file to be opened.
@@ -392,6 +424,11 @@ class ExFatFile {
    * \return true for success or false for failure.
    */
   bool open(const char* path, oflag_t oflag = O_RDONLY);
+   /** Open the current working directory.
+   *
+   * \return true for success or false for failure.
+   */
+  bool openCwd();
   /** Open the next file or subdirectory in a directory.
    *
    * \param[in] dirFile An open instance for the directory
@@ -769,7 +806,6 @@ class ExFatFile {
   bool openPrivate(ExFatFile* dir, ExName_t* fname, oflag_t oflag);
   bool parsePathName(const char* path,
                             ExName_t* fname, const char** ptr);
-  uint32_t curCluster() const {return m_curCluster;}
   ExFatVolume* volume() const {return m_vol;}
   bool syncDir();
   //----------------------------------------------------------------------------
@@ -778,25 +814,14 @@ class ExFatFile {
 
   /** This file has not been opened. */
   static const uint8_t FILE_ATTR_CLOSED = 0;
-  /** File is read-only. */
-  static const uint8_t FILE_ATTR_READ_ONLY = EXFAT_ATTRIB_READ_ONLY;
-  /** File should be hidden in directory listings. */
-  static const uint8_t FILE_ATTR_HIDDEN = EXFAT_ATTRIB_HIDDEN;
-  /** Entry is for a system file. */
-  static const uint8_t FILE_ATTR_SYSTEM = EXFAT_ATTRIB_SYSTEM;
   /** Entry for normal data file */
   static const uint8_t FILE_ATTR_FILE = 0X08;
   /** Entry is for a subdirectory */
-  static const uint8_t FILE_ATTR_SUBDIR = EXFAT_ATTRIB_DIRECTORY;
-  static const uint8_t FILE_ATTR_ARCHIVE = EXFAT_ATTRIB_ARCHIVE;
+  static const uint8_t FILE_ATTR_SUBDIR = FS_ATTRIB_DIRECTORY;
   /** Root directory */
   static const uint8_t FILE_ATTR_ROOT = 0X40;
   /** Directory type bits */
   static const uint8_t FILE_ATTR_DIR = FILE_ATTR_SUBDIR | FILE_ATTR_ROOT;
-  /** Attributes to copy from directory entry */
-  static const uint8_t FILE_ATTR_COPY = EXFAT_ATTRIB_READ_ONLY |
-                       EXFAT_ATTRIB_HIDDEN | EXFAT_ATTRIB_SYSTEM |
-                       EXFAT_ATTRIB_DIRECTORY | EXFAT_ATTRIB_ARCHIVE;
 
   static const uint8_t FILE_FLAG_READ = 0X01;
   static const uint8_t FILE_FLAG_WRITE = 0X02;

+ 1 - 1
src/ExFatLib/ExFatFilePrint.cpp

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 4 - 4
src/ExFatLib/ExFatFileWrite.cpp

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License
@@ -204,7 +204,7 @@ bool ExFatFile::mkdir(ExFatFile* parent, ExName_t* fname) {
     goto fail;
   }
   // convert file to directory
-  m_attributes = FILE_ATTR_SUBDIR;
+  m_attributes = FILE_ATTR_SUBDIR | FS_ATTRIB_ARCHIVE;
 
   // allocate and zero first cluster
   if (!addDirCluster()) {
@@ -402,7 +402,7 @@ bool ExFatFile::syncDir() {
     switch (cache[0]) {
       case EXFAT_TYPE_FILE:
         df = reinterpret_cast<DirFile_t*>(cache);
-        setLe16(df->attributes, m_attributes & FILE_ATTR_COPY);
+        setLe16(df->attributes, m_attributes & FS_ATTRIB_COPY);
         if (FsDateTime::callback) {
           uint16_t date, time;
           uint8_t ms10;
@@ -498,7 +498,7 @@ bool ExFatFile::timestamp(uint8_t flags, uint16_t year, uint8_t month,
     switch (cache[0]) {
       case EXFAT_TYPE_FILE:
         df = reinterpret_cast<DirFile_t*>(cache);
-        setLe16(df->attributes, m_attributes & FILE_ATTR_COPY);
+        setLe16(df->attributes, m_attributes & FS_ATTRIB_COPY);
         m_vol->dataCacheDirty();
         if (flags & T_ACCESS) {
           setLe16(df->accessTime, time);

+ 1 - 1
src/ExFatLib/ExFatFormatter.cpp

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/ExFatLib/ExFatFormatter.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/ExFatLib/ExFatLib.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/ExFatLib/ExFatName.cpp

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 23 - 21
src/ExFatLib/ExFatPartition.cpp

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License
@@ -237,7 +237,7 @@ bool ExFatPartition::freeChain(uint32_t cluster) {
   return false;
 }
 //------------------------------------------------------------------------------
-uint32_t ExFatPartition::freeClusterCount() {
+int32_t ExFatPartition::freeClusterCount() {
   uint32_t nc = 0;
   uint32_t sector = m_clusterHeapStartSector;
   uint32_t usedCount = 0;
@@ -246,7 +246,7 @@ uint32_t ExFatPartition::freeClusterCount() {
   while (true) {
     cache = dataCachePrepare(sector++, FsCache::CACHE_FOR_READ);
     if (!cache) {
-      return 0;
+      return -1;
     }
     for (size_t i = 0; i < m_bytesPerSector; i++) {
       if (cache[i] == 0XFF) {
@@ -266,37 +266,39 @@ uint32_t ExFatPartition::freeClusterCount() {
   }
 }
 //------------------------------------------------------------------------------
-bool ExFatPartition::init(FsBlockDevice* dev, uint8_t part) {
-  uint32_t volStart = 0;
-  uint8_t* cache;
+bool ExFatPartition::init(FsBlockDevice* dev, uint8_t part, uint32_t volStart) {
   pbs_t* pbs;
   BpbExFat_t* bpb;
   MbrSector_t* mbr;
-  MbrPart_t* mp;
-
   m_fatType = 0;
   m_blockDev = dev;
   cacheInit(m_blockDev);
-  cache = dataCachePrepare(0, FsCache::CACHE_FOR_READ);
-  if (part > 4 || !cache) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  if (part >= 1) {
-    mbr = reinterpret_cast<MbrSector_t*>(cache);
-    mp = &mbr->part[part - 1];
-    if ((mp->boot != 0 && mp->boot != 0X80) || mp->type == 0) {
+  // if part == 0 assume super floppy with FAT boot sector in sector zero
+  // if part > 0 assume mbr volume with partition table
+  if (part) {
+    if (part > 4) {
       DBG_FAIL_MACRO;
       goto fail;
     }
-    volStart = getLe32(mp->relativeSectors);
-    cache = dataCachePrepare(volStart, FsCache::CACHE_FOR_READ);
-    if (!cache) {
+    mbr = reinterpret_cast<MbrSector_t*>
+          (dataCachePrepare(0, FsCache::CACHE_FOR_READ));
+    if (!mbr) {
       DBG_FAIL_MACRO;
       goto fail;
     }
+    MbrPart_t* mp = mbr->part + part - 1;
+    if (mp->type == 0 || (mp->boot != 0 && mp->boot != 0X80)) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    volStart = getLe32(mp->relativeSectors);
+  }
+  pbs = reinterpret_cast<pbs_t*>
+        (dataCachePrepare(volStart, FsCache::CACHE_FOR_READ));
+  if (!pbs) {
+    DBG_FAIL_MACRO;
+    goto fail;
   }
-  pbs = reinterpret_cast<pbs_t*>(cache);
   if (strncmp(pbs->oemName, "EXFAT", 5)) {
     DBG_FAIL_MACRO;
     goto fail;

+ 6 - 5
src/ExFatLib/ExFatPartition.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License
@@ -95,18 +95,19 @@ class ExFatPartition {
   uint32_t fatStartSector() const {return m_fatStartSector;}
   /** \return Type FAT_TYPE_EXFAT for exFAT partition or zero for error. */
   uint8_t fatType() const {return m_fatType;}
-  /** \return the free cluster count. */
-  uint32_t freeClusterCount();
+  /** \return free cluster count or -1 if an error occurs. */
+  int32_t freeClusterCount();
   /** Initialize a exFAT partition.
    * \param[in] dev The blockDevice for the 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
-   * a super floppy with the FAT boot sector in sector zero.
+   * a super floppy with the FAT boot sector in sector volStart.
+   * \param[in] volStart location of volume if part is zero.
    *
    * \return true for success or false for failure.
    */
-  bool init(FsBlockDevice* dev, uint8_t part);
+  bool init(FsBlockDevice* dev, uint8_t part, uint32_t volStart = 0);
   /**
    * Check for device busy.
    *

+ 1 - 1
src/ExFatLib/ExFatVolume.cpp

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 25 - 4
src/ExFatLib/ExFatVolume.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License
@@ -33,15 +33,36 @@
 class ExFatVolume : public ExFatPartition {
  public:
   ExFatVolume() {}
+  /** Get file's user settable attributes.
+   * \param[in] path path to file.
+   * \return user settable file attributes for success else -1.
+   */
+  int attrib(const char* path) {
+    ExFatFile tmpFile;
+    return tmpFile.open(this, path, O_RDONLY) ? tmpFile.attrib() : -1;
+  }
+  /** Set file's user settable attributes.
+   * \param[in] path path to file.
+   * \param[in] bits bit-wise or of selected attributes: FS_ATTRIB_READ_ONLY,
+   *            FS_ATTRIB_HIDDEN, FS_ATTRIB_SYSTEM, FS_ATTRIB_ARCHIVE.
+   *
+   * \return true for success or false for failure.
+   */
+  bool attrib(const char* path, uint8_t bits) {
+    ExFatFile tmpFile;
+    return tmpFile.open(this, path, O_RDONLY) ? tmpFile.attrib(bits) : false;
+  }
   /**
    * Initialize an FatVolume object.
    * \param[in] dev Device block driver.
    * \param[in] setCwv Set current working volume if true.
-   * \param[in] part partition to initialize.
+   * \param[in] part Partition to initialize.
+   * \param[in] volStart Start sector of volume if part is zero.
    * \return true for success or false for failure.
    */
-  bool begin(FsBlockDevice* dev, bool setCwv = true, uint8_t part = 1) {
-    if (!init(dev, part)) {
+  bool begin(FsBlockDevice* dev, bool setCwv = true,
+             uint8_t part = 1, uint32_t volStart = 0) {
+    if (!init(dev, part, volStart)) {
       return false;
     }
     if (!chdir()) {

+ 3 - 3
src/FatLib/FatDbg.cpp

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License
@@ -98,7 +98,7 @@ static bool printFatDir(print_t* pr, DirFat_t* dir) {
     return false;
   } else if (dir->name[0] == FAT_NAME_DELETED) {
     pr->println(F("Deleted"));
-  } else if (isFileOrSubdir(dir)) {
+  } else if (isFatFileOrSubdir(dir)) {
     pr->print(F("SFN: "));
     for (uint8_t i = 0; i < 11; i++) {
       printHex(pr, dir->name[i]);
@@ -117,7 +117,7 @@ static bool printFatDir(print_t* pr, DirFat_t* dir) {
     pr->println(fc, HEX);
     pr->print(F("fileSize: "));
     pr->println(getLe32(dir->fileSize));
-  } else if (isLongName(dir)) {
+  } else if (isFatLongName(dir)) {
     pr->print(F("LFN: "));
     for (uint8_t i = 0; i < 13; i++) {
       uint16_t c = getLfnChar(ldir, i);

+ 53 - 11
src/FatLib/FatFile.cpp

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License
@@ -86,6 +86,29 @@ bool FatFile::addDirCluster() {
   return false;
 }
 //------------------------------------------------------------------------------
+bool FatFile::attrib(uint8_t bits) {
+  if (!isFileOrSubDir() || (bits & FS_ATTRIB_USER_SETTABLE) != bits) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // Don't allow read-only to be set if the file is open for write.
+  if ((bits & FS_ATTRIB_READ_ONLY) && isWritable()) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  m_attributes = (m_attributes & ~FS_ATTRIB_USER_SETTABLE) | bits;
+  // insure sync() will update dir entry
+  m_flags |= FILE_FLAG_DIR_DIRTY;
+  if (!sync()) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  return true;
+
+ fail:
+  return false;
+}
+//------------------------------------------------------------------------------
 // cache a file's directory entry
 // return pointer to cached entry or null for failure
 DirFat_t* FatFile::cacheDirEntry(uint8_t action) {
@@ -382,7 +405,7 @@ bool FatFile::mkdir(FatFile* parent, FatName_t* fname) {
     goto fail;
   }
   // change directory entry attribute
-  dir->attributes = FAT_ATTRIB_DIRECTORY;
+  dir->attributes = FS_ATTRIB_DIRECTORY;
 
   // make entry for '.'
   memcpy(&dot, dir, sizeof(dot));
@@ -466,6 +489,11 @@ bool FatFile::open(FatFile* dirFile, const char* path, oflag_t oflag) {
   return false;
 }
 //------------------------------------------------------------------------------
+bool FatFile::open(uint16_t index, oflag_t oflag) {
+  FatVolume* vol = FatVolume::cwv();
+  return vol ? open(vol->vwd(), index, oflag) : false;
+}
+//------------------------------------------------------------------------------
 bool FatFile::open(FatFile* dirFile, uint16_t index, oflag_t oflag) {
   if (index) {
     // Find start of LFN.
@@ -519,12 +547,12 @@ bool FatFile::openCachedEntry(FatFile* dirFile, uint16_t dirIndex,
   dir += 0XF & dirIndex;
 
   // Must be file or subdirectory.
-  if (!isFileOrSubdir(dir)) {
+  if (!isFatFileOrSubdir(dir)) {
     DBG_FAIL_MACRO;
     goto fail;
   }
-  m_attributes = dir->attributes & FILE_ATTR_COPY;
-  if (isFileDir(dir)) {
+  m_attributes = dir->attributes & FS_ATTRIB_COPY;
+  if (isFatFile(dir)) {
     m_attributes |= FILE_ATTR_FILE;
   }
   m_lfnOrd = lfnOrd;
@@ -556,6 +584,7 @@ bool FatFile::openCachedEntry(FatFile* dirFile, uint16_t dirIndex,
       DBG_FAIL_MACRO;
       goto fail;
     }
+    m_attributes |= FS_ATTRIB_ARCHIVE;
   }
   m_flags |= (oflag & O_APPEND ? FILE_FLAG_APPEND : 0);
 
@@ -570,7 +599,6 @@ bool FatFile::openCachedEntry(FatFile* dirFile, uint16_t dirIndex,
       DBG_FAIL_MACRO;
       goto fail;
     }
-
     // need to update directory entry
     m_flags |= FILE_FLAG_DIR_DIRTY;
   } else {
@@ -601,6 +629,19 @@ bool FatFile::openCluster(FatFile* file) {
   return true;
 }
 //------------------------------------------------------------------------------
+bool FatFile::openCwd() {
+  if (isOpen() || !FatVolume::cwv()) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  *this = *FatVolume::cwv()->vwd();
+  rewind();
+  return true;
+
+ fail:
+  return false;
+}
+//------------------------------------------------------------------------------
 bool FatFile::openNext(FatFile* dirFile, oflag_t oflag) {
   uint8_t checksum = 0;
   DirLfn_t* ldir;
@@ -629,7 +670,7 @@ bool FatFile::openNext(FatFile* dirFile, oflag_t oflag) {
     // skip empty slot or '.' or '..'
     if (dir->name[0] == '.' || dir->name[0] == FAT_NAME_DELETED) {
       lfnOrd = 0;
-    } else if (isFileOrSubdir(dir)) {
+    } else if (isFatFileOrSubdir(dir)) {
       if (lfnOrd && checksum != lfnChecksum(dir->name)) {
         DBG_FAIL_MACRO;
         goto fail;
@@ -639,7 +680,7 @@ bool FatFile::openNext(FatFile* dirFile, oflag_t oflag) {
         goto fail;
       }
       return true;
-    } else if (isLongName(dir)) {
+    } else if (isFatLongName(dir)) {
       ldir = reinterpret_cast<DirLfn_t*>(dir);
       if (ldir->order & FAT_ORDER_LAST_LONG_ENTRY) {
         lfnOrd = ldir->order & 0X1F;
@@ -855,7 +896,7 @@ int8_t FatFile::readDir(DirFat_t* dir) {
       continue;
     }
     // return if normal file or subdirectory
-    if (isFileOrSubdir(dir)) {
+    if (isFatFileOrSubdir(dir)) {
       return n;
     }
   }
@@ -1040,7 +1081,7 @@ bool FatFile::rmdir() {
       continue;
     }
     // error not empty
-    if (isFileOrSubdir(dir)) {
+    if (isFatFileOrSubdir(dir)) {
       DBG_FAIL_MACRO;
       goto fail;
     }
@@ -1086,7 +1127,7 @@ bool FatFile::rmRfStar() {
     }
 
     // skip if part of long file name or volume label in root
-    if (!isFileOrSubdir(dir)) {
+    if (!isFatFileOrSubdir(dir)) {
       continue;
     }
 
@@ -1207,6 +1248,7 @@ bool FatFile::sync() {
       DBG_FAIL_MACRO;
       goto fail;
     }
+    dir->attributes = m_attributes & FS_ATTRIB_COPY;
     // do not set filesize for dir files
     if (isFile()) {
       setLe32(dir->fileSize, m_fileSize);

+ 45 - 21
src/FatLib/FatFile.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License
@@ -156,6 +156,21 @@ class FatFile {
    * \return true if a file is open.
    */
   operator bool() const {return isOpen();}
+  /**
+   * \return user settable file attributes for success else -1.
+   */
+  int attrib() {
+    return isFileOrSubDir() ? m_attributes & FS_ATTRIB_USER_SETTABLE : -1;
+  }
+  /** Set file attributes
+   *
+   * \param[in] bits bit-wise or of selected attributes: FS_ATTRIB_READ_ONLY,
+   *            FS_ATTRIB_HIDDEN, FS_ATTRIB_SYSTEM, FS_ATTRIB_ARCHIVE.
+   *
+   * \note attrib() will fail for set read-only if the file is open for write.
+   * \return true for success or false for failure.
+   */
+  bool attrib(uint8_t bits);
   /** \return The number of bytes available from the current position
    * to EOF for normal files.  INT_MAX is returned for very large files.
    *
@@ -390,8 +405,10 @@ class FatFile {
   bool isDir() const {return m_attributes & FILE_ATTR_DIR;}
   /** \return True if this is a normal file. */
   bool isFile() const {return m_attributes & FILE_ATTR_FILE;}
+  /** \return True if this is a normal file or sub-directory. */
+  bool isFileOrSubDir() const {return isFile() || isSubDir();}
   /** \return True if this is a hidden file. */
-  bool isHidden() const {return m_attributes & FILE_ATTR_HIDDEN;}
+  bool isHidden() const {return m_attributes & FS_ATTRIB_HIDDEN;}
   /** \return true if this file has a Long File Name. */
   bool isLFN() const {return m_lfnOrd;}
   /** \return True if this is an open file/directory. */
@@ -399,17 +416,17 @@ class FatFile {
   /** \return True file is readable. */
   bool isReadable() const {return m_flags & FILE_FLAG_READ;}
   /** \return True if file is read-only */
-  bool isReadOnly() const {return m_attributes & FILE_ATTR_READ_ONLY;}
+  bool isReadOnly() const {return m_attributes & FS_ATTRIB_READ_ONLY;}
   /** \return True if this is the root directory. */
   bool isRoot() const {return m_attributes & FILE_ATTR_ROOT;}
   /** \return True if this is the FAT32 root directory. */
   bool isRoot32() const {return m_attributes & FILE_ATTR_ROOT32;}
   /** \return True if this is the FAT12 of FAT16 root directory. */
   bool isRootFixed() const {return m_attributes & FILE_ATTR_ROOT_FIXED;}
-  /** \return True if this is a subdirectory. */
+  /** \return True if this is a sub-directory. */
   bool isSubDir() const {return m_attributes & FILE_ATTR_SUBDIR;}
   /** \return True if this is a system file. */
-  bool isSystem() const {return m_attributes & FILE_ATTR_SYSTEM;}
+  bool isSystem() const {return m_attributes & FS_ATTRIB_SYSTEM;}
   /** \return True file is writable. */
   bool isWritable() const {return m_flags & FILE_FLAG_WRITE;}
   /** List directory contents.
@@ -453,7 +470,7 @@ class FatFile {
    *
    * \return true for success or false for failure.
    */
-  bool open(FatVolume* vol, const char* path, oflag_t oflag);
+  bool open(FatVolume* vol, const char* path, oflag_t oflag = O_RDONLY);
   /** Open a file by index.
    *
    * \param[in] dirFile An open FatFile instance for the directory.
@@ -467,7 +484,19 @@ class FatFile {
    * See open() by path for definition of flags.
    * \return true for success or false for failure.
    */
-  bool open(FatFile* dirFile, uint16_t index, oflag_t oflag);
+  bool open(FatFile* dirFile, uint16_t index, oflag_t oflag = O_RDONLY);
+  /** Open a file by index in the current working directory.
+   *
+   * \param[in] index The \a index of the directory entry for the file to be
+   * opened.  The value for \a index is (directory file position)/32.
+   *
+   * \param[in] oflag bitwise-inclusive OR of open flags.
+   *                  See see FatFile::open(FatFile*, const char*, uint8_t).
+   *
+   * See open() by path for definition of flags.
+   * \return true for success or false for failure.
+   */
+  bool open(uint16_t index, oflag_t oflag = O_RDONLY);
   /** Open a file or directory by name.
    *
    * \param[in] dirFile An open FatFile instance for the directory containing
@@ -513,7 +542,7 @@ class FatFile {
    *
    * \return true for success or false for failure.
    */
-  bool open(FatFile* dirFile, const char* path, oflag_t oflag);
+  bool open(FatFile* dirFile, const char* path, oflag_t oflag = O_RDONLY);
   /** Open a file in the current working volume.
    *
    * \param[in] path A path with a valid name for a file to be opened.
@@ -524,6 +553,11 @@ class FatFile {
    * \return true for success or false for failure.
    */
   bool open(const char* path, oflag_t oflag = O_RDONLY);
+   /** Open the current working directory.
+   *
+   * \return true for success or false for failure.
+   */
+  bool openCwd();
   /** Open existing file wih Short 8.3 names.
    * \param[in] path with short 8.3 names.
    *
@@ -957,29 +991,19 @@ class FatFile {
 
   /** This file has not been opened. */
   static const uint8_t FILE_ATTR_CLOSED = 0;
-  /** File is read-only. */
-  static const uint8_t FILE_ATTR_READ_ONLY = FAT_ATTRIB_READ_ONLY;
-  /** File should be hidden in directory listings. */
-  static const uint8_t FILE_ATTR_HIDDEN = FAT_ATTRIB_HIDDEN;
-  /** Entry is for a system file. */
-  static const uint8_t FILE_ATTR_SYSTEM = FAT_ATTRIB_SYSTEM;
   /** Entry for normal data file */
   static const uint8_t FILE_ATTR_FILE = 0X08;
   /** Entry is for a subdirectory */
-  static const uint8_t FILE_ATTR_SUBDIR = FAT_ATTRIB_DIRECTORY;
+  static const uint8_t FILE_ATTR_SUBDIR = FS_ATTRIB_DIRECTORY;
   /** A FAT12 or FAT16 root directory */
-  static const uint8_t FILE_ATTR_ROOT_FIXED = 0X20;
+  static const uint8_t FILE_ATTR_ROOT_FIXED = 0X40;
   /** A FAT32 root directory */
-  static const uint8_t FILE_ATTR_ROOT32 = 0X40;
+  static const uint8_t FILE_ATTR_ROOT32 = 0X80;
   /** Entry is for root. */
   static const uint8_t FILE_ATTR_ROOT =
                        FILE_ATTR_ROOT_FIXED | FILE_ATTR_ROOT32;
   /** Directory type bits */
   static const uint8_t FILE_ATTR_DIR = FILE_ATTR_SUBDIR | FILE_ATTR_ROOT;
-  /** Attributes to copy from directory entry */
-  static const uint8_t FILE_ATTR_COPY =
-                       FAT_ATTRIB_READ_ONLY | FAT_ATTRIB_HIDDEN |
-                       FAT_ATTRIB_SYSTEM | FAT_ATTRIB_DIRECTORY;
 
   // private functions
 

+ 5 - 4
src/FatLib/FatFileLFN.cpp

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License
@@ -268,7 +268,7 @@ bool FatFile::makeUniqueSfn(FatLfn_t* fname) {
       if (dir->name[0] == FAT_NAME_FREE) {
         goto done;
       }
-      if (isFileOrSubdir(dir) && !memcmp(fname->sfn, dir->name, 11)) {
+      if (isFatFileOrSubdir(dir) && !memcmp(fname->sfn, dir->name, 11)) {
         // Name found - try another.
         break;
       }
@@ -338,7 +338,7 @@ bool FatFile::open(FatFile* dirFile, FatLfn_t* fname, oflag_t oflag) {
     // skip empty slot or '.' or '..'
     if (dir->name[0] == FAT_NAME_DELETED || dir->name[0] == '.') {
       lfnOrd = 0;
-    } else if (isLongName(dir)) {
+    } else if (isFatLongName(dir)) {
       ldir = reinterpret_cast<DirLfn_t*>(dir);
       if (!lfnOrd) {
         order = ldir->order & 0X1F;
@@ -357,7 +357,7 @@ bool FatFile::open(FatFile* dirFile, FatLfn_t* fname, oflag_t oflag) {
           lfnOrd = 0;
         }
       }
-    } else if (isFileOrSubdir(dir)) {
+    } else if (isFatFileOrSubdir(dir)) {
       if (lfnOrd) {
         if (1 == order && lfnChecksum(dir->name) == checksum) {
           goto found;
@@ -476,6 +476,7 @@ bool FatFile::parsePathName(const char* path,
     path++;
   }
   fname->begin = path;
+  fname->len = 0;
   while (*path && !isDirSeparator(*path)) {
 #if USE_UTF8_LONG_NAMES
     uint32_t cp;

+ 1 - 1
src/FatLib/FatFilePrint.cpp

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 12 - 8
src/FatLib/FatFileSFN.cpp

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License
@@ -62,7 +62,7 @@ bool FatFile::open(FatFile* dirFile, FatSfn_t* fname, oflag_t oflag) {
         break;
       }
       lfnOrd = 0;
-    } else if (isFileOrSubdir(dir)) {
+    } else if (isFatFileOrSubdir(dir)) {
       if (!memcmp(fname->sfn, dir->name, 11)) {
         // don't open existing file if O_EXCL
         if (oflag & O_EXCL) {
@@ -83,7 +83,7 @@ bool FatFile::open(FatFile* dirFile, FatSfn_t* fname, oflag_t oflag) {
       } else {
         lfnOrd = 0;
       }
-    } else if (isLongName(dir)) {
+    } else if (isFatLongName(dir)) {
       ldir = reinterpret_cast<DirLfn_t*>(dir);
       if (ldir->order & FAT_ORDER_LAST_LONG_ENTRY) {
         lfnOrd = ldir->order & 0X1F;
@@ -191,25 +191,29 @@ bool FatFile::openSFN(FatSfn_t* fname) {
       DBG_FAIL_MACRO;
       goto fail;
     }
-    if (isFileOrSubdir(&dir) && memcmp(fname->sfn, dir.name, 11) == 0) {
+    if (isFatFileOrSubdir(&dir) && memcmp(fname->sfn, dir.name, 11) == 0) {
       uint16_t dirIndex = (m_curPosition - 32) >> 5;
       uint32_t dirCluster = m_firstCluster;
       memset(this, 0 , sizeof(FatFile));
-      m_attributes = dir.attributes & FILE_ATTR_COPY;
-      if (isFileDir(&dir)) {
+      m_attributes = dir.attributes & FS_ATTRIB_COPY;
+      m_flags = FILE_FLAG_READ;
+      if (isFatFile(&dir)) {
         m_attributes |= FILE_ATTR_FILE;
+        if (!isReadOnly()) {
+          m_attributes |= FS_ATTRIB_ARCHIVE;
+          m_flags |= FILE_FLAG_WRITE;
+        }
       }
       m_lfnOrd = lfnOrd;
       m_firstCluster = (uint32_t)getLe16(dir.firstClusterHigh) << 16;
       m_firstCluster |= getLe16(dir.firstClusterLow);
       m_fileSize = getLe32(dir.fileSize);
-      m_flags = isFile() ? FILE_FLAG_READ | FILE_FLAG_WRITE : FILE_FLAG_READ;
       m_vol = vol;
       m_dirCluster = dirCluster;
       m_dirSector = m_vol->cacheSectorNumber();
       m_dirIndex = dirIndex;
       return true;
-    } else if (isLongName(&dir)) {
+    } else if (isFatLongName(&dir)) {
       ldir = reinterpret_cast<DirLfn_t*>(&dir);
       if (ldir->order & FAT_ORDER_LAST_LONG_ENTRY) {
         lfnOrd = ldir->order & 0X1F;

+ 1 - 1
src/FatLib/FatFormatter.cpp

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/FatLib/FatFormatter.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/FatLib/FatLib.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/FatLib/FatName.cpp

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 16 - 11
src/FatLib/FatPartition.cpp

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License
@@ -390,10 +390,9 @@ int32_t FatPartition::freeClusterCount() {
   return -1;
 }
 //------------------------------------------------------------------------------
-bool FatPartition::init(FsBlockDevice* dev, uint8_t part) {
+bool FatPartition::init(FsBlockDevice* dev, uint8_t part, uint32_t volStart) {
   uint32_t clusterCount;
   uint32_t totalSectors;
-  uint32_t volumeStartSector = 0;
   m_blockDev = dev;
   pbs_t* pbs;
   BpbFat32_t* bpb;
@@ -414,19 +413,25 @@ bool FatPartition::init(FsBlockDevice* dev, uint8_t part) {
     }
     mbr = reinterpret_cast<MbrSector_t*>
           (dataCachePrepare(0, FsCache::CACHE_FOR_READ));
+    if (!mbr) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
     MbrPart_t* mp = mbr->part + part - 1;
-
-    if (!mbr || mp->type == 0 || (mp->boot != 0 && mp->boot != 0X80)) {
+    if (mp->type == 0 || (mp->boot != 0 && mp->boot != 0X80)) {
       DBG_FAIL_MACRO;
       goto fail;
     }
-    volumeStartSector = getLe32(mp->relativeSectors);
+    volStart = getLe32(mp->relativeSectors);
   }
   pbs = reinterpret_cast<pbs_t*>
-        (dataCachePrepare(volumeStartSector, FsCache::CACHE_FOR_READ));
+        (dataCachePrepare(volStart, FsCache::CACHE_FOR_READ));
+  if (!pbs) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
   bpb = reinterpret_cast<BpbFat32_t*>(pbs->bpb);
-  if (!pbs || bpb->fatCount != 2 ||
-    getLe16(bpb->bytesPerSector) != m_bytesPerSector) {
+  if (bpb->fatCount != 2 || getLe16(bpb->bytesPerSector) != m_bytesPerSector) {
     DBG_FAIL_MACRO;
     goto fail;
   }
@@ -445,7 +450,7 @@ bool FatPartition::init(FsBlockDevice* dev, uint8_t part) {
   if (m_sectorsPerFat == 0) {
     m_sectorsPerFat = getLe32(bpb->sectorsPerFat32);
   }
-  m_fatStartSector = volumeStartSector + getLe16(bpb->reservedSectorCount);
+  m_fatStartSector = volStart + getLe16(bpb->reservedSectorCount);
 
   // count for FAT16 zero for FAT32
   m_rootDirEntryCount = getLe16(bpb->rootDirEntryCount);
@@ -462,7 +467,7 @@ bool FatPartition::init(FsBlockDevice* dev, uint8_t part) {
     totalSectors = getLe32(bpb->totalSectors32);
   }
   // total data sectors
-  clusterCount = totalSectors - (m_dataStartSector - volumeStartSector);
+  clusterCount = totalSectors - (m_dataStartSector - volStart);
 
   // divide by cluster size to get cluster count
   clusterCount >>= m_sectorsPerClusterShift;

+ 5 - 7
src/FatLib/FatPartition.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License
@@ -126,10 +126,7 @@ class FatPartition {
   uint8_t fatType() const {
     return m_fatType;
   }
-  /** Volume free space in clusters.
-   *
-   * \return Count of free clusters for success or -1 if an error occurs.
-   */
+  /** \return free cluster count or -1 if an error occurs. */
   int32_t freeClusterCount();
   /** Initialize a FAT partition.
    *
@@ -137,11 +134,12 @@ class FatPartition {
    * \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
-   * a super floppy with the FAT boot sector in sector zero.
+   * a super floppy with the FAT boot sector in sector volStart.
+   * \param[in] volStart location of volume if part is zero.
    *
    * \return true for success or false for failure.
    */
-  bool init(FsBlockDevice* dev, uint8_t part = 1);
+  bool init(FsBlockDevice* dev, uint8_t part = 1, uint32_t volStart = 0);
   /** \return The number of entries in the root directory for FAT16 volumes. */
   uint16_t rootDirEntryCount() const {
     return m_rootDirEntryCount;

+ 1 - 1
src/FatLib/FatVolume.cpp

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 24 - 3
src/FatLib/FatVolume.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License
@@ -36,15 +36,36 @@
  */
 class FatVolume : public  FatPartition {
  public:
+  /** Get file's user settable attributes.
+   * \param[in] path path to file.
+   * \return user settable file attributes for success else -1.
+   */
+  int attrib(const char* path) {
+    File32 tmpFile;
+    return tmpFile.open(this, path, O_RDONLY) ? tmpFile.attrib() : -1;
+  }
+  /** Set file's user settable attributes.
+   * \param[in] path path to file.
+   * \param[in] bits bit-wise or of selected attributes: FS_ATTRIB_READ_ONLY,
+   *            FS_ATTRIB_HIDDEN, FS_ATTRIB_SYSTEM, FS_ATTRIB_ARCHIVE.
+   *
+   * \return true for success or false for failure.
+   */
+  bool attrib(const char* path, uint8_t bits) {
+    File32 tmpFile;
+    return tmpFile.open(this, path, O_RDONLY) ? tmpFile.attrib(bits) : false;
+  }
   /**
    * Initialize an FatVolume object.
    * \param[in] dev Device block driver.
    * \param[in] setCwv Set current working volume if true.
    * \param[in] part partition to initialize.
+   * \param[in] volStart Start sector of volume if part is zero.
    * \return true for success or false for failure.
    */
-  bool begin(FsBlockDevice* dev, bool setCwv = true, uint8_t part = 1) {
-    if (!init(dev, part)) {
+  bool begin(FsBlockDevice* dev, bool setCwv = true,
+             uint8_t part = 1, uint32_t volStart = 0) {
+    if (!init(dev, part, volStart)) {
       return false;
     }
     if (!chdir()) {

+ 1 - 1
src/FreeStack.cpp

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/FreeStack.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 24 - 11
src/FsLib/FsFile.cpp

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License
@@ -37,7 +37,7 @@ FsBaseFile::FsBaseFile(const FsBaseFile& from) {
 }
 //------------------------------------------------------------------------------
 FsBaseFile& FsBaseFile::operator=(const FsBaseFile& from) {
-  if (this == &from) return *this;
+  if (this == &from) {return *this;}
   close();
   if (from.m_fFile) {
     m_fFile = new (m_fileMem) FatFile;
@@ -50,15 +50,10 @@ FsBaseFile& FsBaseFile::operator=(const FsBaseFile& from) {
 }
 //------------------------------------------------------------------------------
 bool FsBaseFile::close() {
-  if (m_fFile && m_fFile->close()) {
-    m_fFile = nullptr;
-    return true;
-  }
-  if (m_xFile && m_xFile->close()) {
-    m_xFile = nullptr;
-    return true;
-  }
-  return false;
+  bool rtn = m_fFile ? m_fFile->close() : m_xFile ? m_xFile->close() : true;
+  m_fFile = nullptr;
+  m_xFile = nullptr;
+  return rtn;
 }
 //------------------------------------------------------------------------------
 bool FsBaseFile::mkdir(FsBaseFile* dir, const char* path, bool pFlag) {
@@ -136,6 +131,24 @@ bool FsBaseFile::open(FsBaseFile* dir, uint32_t index, oflag_t oflag) {
   return false;
 }
 //------------------------------------------------------------------------------
+bool FsBaseFile::openCwd() {
+  close();
+  if (FsVolume::m_cwv && FsVolume::m_cwv->m_fVol) {
+    m_fFile = new (m_fileMem) FatFile;
+    if (m_fFile->openCwd()) {
+      return true;
+    }
+    m_fFile = nullptr;
+  } else if (FsVolume::m_cwv && FsVolume::m_cwv->m_xVol) {
+    m_xFile = new (m_fileMem) ExFatFile;
+    if (m_xFile->openCwd()) {
+      return true;
+    }
+    m_xFile = nullptr;
+  }
+  return false;
+}
+//------------------------------------------------------------------------------
 bool FsBaseFile::openNext(FsBaseFile* dir, oflag_t oflag) {
   close();
   if (dir->m_fFile) {

+ 57 - 8
src/FsLib/FsFile.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License
@@ -66,6 +66,25 @@ class FsBaseFile {
     * \return true if a file is open.
     */
   operator bool() const {return isOpen();}
+  /**
+   * \return user settable file attributes for success else -1.
+   */
+  int attrib() {
+     return m_fFile ? m_fFile->attrib() :
+            m_xFile ? m_xFile->attrib() : -1;
+  }
+  /** Set file attributes
+   *
+   * \param[in] bits bit-wise or of selected attributes: FS_ATTRIB_READ_ONLY,
+   *            FS_ATTRIB_HIDDEN, FS_ATTRIB_SYSTEM, FS_ATTRIB_ARCHIVE.
+   *
+   * \note attrib() will fail for set read-only if the file is open for write.
+   * \return true for success or false for failure.
+   */
+  bool attrib(uint8_t bits) {
+    return m_fFile ? m_fFile->attrib(bits) :
+           m_xFile ? m_xFile->attrib(bits) : false;
+  }
   /** \return number of bytes available from the current position to EOF
    *   or INT_MAX if more than INT_MAX bytes are available.
    */
@@ -105,6 +124,11 @@ class FsBaseFile {
     return m_fFile ? m_fFile->contiguousRange(bgnSector, endSector) :
            m_xFile ? m_xFile->contiguousRange(bgnSector, endSector) : false;
   }
+  /** \return The current cluster number for a file or directory. */
+  uint32_t curCluster() const {
+    return m_fFile ? m_fFile->curCluster() :
+           m_xFile ? m_xFile->curCluster() : 0;
+  }
   /** \return The current position for a file or directory. */
   uint64_t curPosition() const {
     return m_fFile ? m_fFile->curPosition() :
@@ -270,6 +294,11 @@ class FsBaseFile {
     return m_fFile ? m_fFile->isFile() :
            m_xFile ? m_xFile->isFile() : false;
   }
+  /** \return True if this is a normal file or sub-directory. */
+  bool isFileOrSubDir() const {
+    return m_fFile ? m_fFile->isFileOrSubDir() :
+           m_xFile ? m_xFile->isFileOrSubDir() : false;
+  }
   /** \return True if this is a hidden file else false. */
   bool isHidden() const {
     return m_fFile ? m_fFile->isHidden() :
@@ -287,7 +316,7 @@ class FsBaseFile {
     return m_fFile ? m_fFile->isReadOnly() :
            m_xFile ? m_xFile->isReadOnly() : false;
   }
-  /** \return True if this is a subdirectory file else false. */
+  /** \return True if this is a sub-directory file else false. */
   bool isSubDir() const {
     return m_fFile ? m_fFile->isSubDir() :
            m_xFile ? m_xFile->isSubDir() : false;
@@ -410,7 +439,7 @@ class FsBaseFile {
    * See open() by path for definition of flags.
    * \return true for success or false for failure.
    */
-  bool open(FsBaseFile* dir, uint32_t index, oflag_t oflag);
+  bool open(FsBaseFile* dir, uint32_t index, oflag_t oflag = O_RDONLY);
   /** Open a file or directory by name.
    *
    * \param[in] vol Volume where the file is located.
@@ -422,7 +451,7 @@ class FsBaseFile {
    *
    * \return true for success or false for failure.
    */
-  bool open(FsVolume* vol, const char* path, oflag_t oflag);
+  bool open(FsVolume* vol, const char* path, oflag_t oflag = O_RDONLY);
   /** Open a file or directory by name.
    *
    * \param[in] path A path for a file to be opened.
@@ -435,6 +464,25 @@ class FsBaseFile {
   bool open(const char* path, oflag_t oflag = O_RDONLY) {
     return FsVolume::m_cwv && open(FsVolume::m_cwv, path, oflag);
   }
+   /** Open a file or directory by index in the current working directory.
+   *
+   * \param[in] index The \a index of the directory entry for the file to be
+   * opened.  The value for \a index is (directory file position)/32.
+   *
+   * \param[in] oflag Values for \a oflag are constructed by a
+   *                  bitwise-inclusive OR of open flags.
+   *
+   * \return true for success or false for failure.
+   */
+  bool open(uint32_t index, oflag_t oflag = O_RDONLY) {
+    FsBaseFile cwd;
+    return cwd.openCwd() && open(&cwd, index, oflag);
+  }
+  /** Open the current working directory.
+   *
+   * \return true for success or false for failure.
+   */
+  bool openCwd();
   /** Opens the next file or folder in a directory.
    * \param[in] dir directory containing files.
    * \param[in] oflag open flags.
@@ -620,14 +668,15 @@ class FsBaseFile {
   }
   /** Rename a file or subdirectory.
    *
-   * \param[in] dirFile Directory for the new path.
+   * \param[in] dir Directory for the new path.
    * \param[in] newPath New path name for the file/directory.
    *
    * \return true for success or false for failure.
    */
-  bool rename(FsBaseFile* dirFile, const char* newPath) {
-    return m_fFile ? m_fFile->rename(dirFile->m_fFile, newPath) :
-           m_xFile ? m_xFile->rename(dirFile->m_xFile, newPath) : false;
+  bool rename(FsBaseFile* dir, const char* newPath) {
+    return m_fFile && dir->m_fFile ? m_fFile->rename(dir->m_fFile, newPath) :
+           m_xFile && dir->m_xFile ? m_xFile->rename(dir->m_xFile, newPath) :
+           false;
   }
   /** Set the file's current position to zero. */
   void rewind() {

+ 1 - 1
src/FsLib/FsFormatter.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/FsLib/FsLib.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/FsLib/FsNew.cpp

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/FsLib/FsNew.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 5 - 5
src/FsLib/FsVolume.cpp

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License
@@ -25,19 +25,19 @@
 #include "FsLib.h"
 FsVolume* FsVolume::m_cwv = nullptr;
 //------------------------------------------------------------------------------
-bool FsVolume::begin(FsBlockDevice* blockDev, bool setCwv, uint8_t part) {
+bool FsVolume::begin(FsBlockDevice* blockDev, bool setCwv,
+                     uint8_t part, uint32_t volStart) {
   m_blockDev = blockDev;
   m_fVol = nullptr;
   m_xVol = new (m_volMem) ExFatVolume;
-  if (m_xVol && m_xVol->begin(m_blockDev, false, part)) {
+  if (m_xVol && m_xVol->begin(m_blockDev, false, part, volStart)) {
     goto done;
   }
   m_xVol = nullptr;
   m_fVol = new (m_volMem) FatVolume;
-  if (m_fVol && m_fVol->begin(m_blockDev, false, part)) {
+  if (m_fVol && m_fVol->begin(m_blockDev, false, part, volStart)) {
     goto done;
   }
-  m_cwv = nullptr;
   m_fVol = nullptr;
   return false;
 

+ 26 - 6
src/FsLib/FsVolume.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License
@@ -42,15 +42,35 @@ class FsVolume {
   FsVolume() {}
 
   ~FsVolume() {end();}
-
+  /** Get file's user settable attributes.
+   * \param[in] path path to file.
+   * \return user settable file attributes for success else -1.
+   */
+  int attrib(const char* path) {
+    return m_fVol ? m_fVol->attrib(path) :
+           m_xVol ? m_xVol->attrib(path) : -1;
+  }
+  /** Set file's user settable attributes.
+   * \param[in] path path to file.
+   * \param[in] bits bit-wise or of selected attributes: FS_ATTRIB_READ_ONLY,
+   *            FS_ATTRIB_HIDDEN, FS_ATTRIB_SYSTEM, FS_ATTRIB_ARCHIVE.
+   *
+   * \return true for success or false for failure.
+   */
+  bool attrib(const char* path, uint8_t bits) {
+    return m_fVol ? m_fVol->attrib(path, bits) :
+           m_xVol ? m_xVol->attrib(path, bits) : false;
+  }
   /**
    * 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.
+   * \param[in] volStart Start sector of volume if part is zero.
    * \return true for success or false for failure.
    */
-  bool begin(FsBlockDevice* blockDev, bool setCwv = true, uint8_t part = 1);
+  bool begin(FsBlockDevice* blockDev, bool setCwv = true, uint8_t
+             part = 1, uint32_t volStart = 0);
 #ifndef DOXYGEN_SHOULD_SKIP_THIS
   uint32_t __attribute__((error("use sectorsPerCluster()"))) blocksPerCluster();
 #endif  // DOXYGEN_SHOULD_SKIP_THIS
@@ -119,10 +139,10 @@ class FsVolume {
     return m_fVol ? m_fVol->fatType() :
            m_xVol ? m_xVol->fatType() : 0;
   }
-  /** \return the free cluster count. */
-  uint32_t freeClusterCount() const {
+  /** \return free cluster count or -1 if an error occurs. */
+  int32_t freeClusterCount() const {
     return m_fVol ? m_fVol->freeClusterCount() :
-           m_xVol ? m_xVol->freeClusterCount() : 0;
+           m_xVol ? m_xVol->freeClusterCount() : -1;
   }
   /**
    * Check for device busy.

+ 1 - 1
src/MinimumSerial.cpp

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/MinimumSerial.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/RingBuf.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/SdCard/SdCard.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/SdCard/SdCardInfo.cpp

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 127 - 197
src/SdCard/SdCardInfo.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License
@@ -32,10 +32,14 @@
 // Part 1
 // Physical Layer
 // Simplified Specification
-// Version 5.00
-// Aug 10, 2016
+// Version 8.00
+// Sep 23, 2020
 //
 // https://www.sdcard.org/downloads/pls/
+#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
+// SD registers are big endian.
+#error bit fields in structures assume little endian processor.
+#endif  // __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
 //------------------------------------------------------------------------------
 // SD card errors
 // See the SD Specification for command info.
@@ -64,6 +68,7 @@
   SD_CARD_ERROR(ACMD13, "Read extended status")\
   SD_CARD_ERROR(ACMD23, "Set pre-erased count")\
   SD_CARD_ERROR(ACMD41, "Activate card initialization")\
+  SD_CARD_ERROR(ACMD51, "Read SCR data")\
   SD_CARD_ERROR(READ_TOKEN, "Bad read data token")\
   SD_CARD_ERROR(READ_CRC, "Read CRC error")\
   SD_CARD_ERROR(READ_FIFO, "SDIO fifo read timeout")\
@@ -170,6 +175,8 @@ const uint8_t ACMD23 = 0X17;
 /** SD_SEND_OP_COMD - Sends host capacity support information and
     activates the card's initialization process */
 const uint8_t ACMD41 = 0X29;
+/** Reads the SD Configuration Register (SCR). */
+const uint8_t ACMD51 = 0X33;
 //==============================================================================
 // CARD_STATUS
 /** The command's argument was out of the allowed range for this card. */
@@ -259,231 +266,154 @@ const uint8_t DATA_RES_ACCEPTED = 0X05;
 typedef struct CID {
   // byte 0
   /** Manufacturer ID */
-  unsigned char mid;
+  uint8_t mid;
   // byte 1-2
-  /** OEM/Application ID */
+  /** OEM/Application ID. */
   char oid[2];
   // byte 3-7
-  /** Product name */
+  /** Product name. */
   char pnm[5];
   // byte 8
-  /** Product revision least significant digit */
-  unsigned char prv_m : 4;
-  /** Product revision most significant digit */
-  unsigned char prv_n : 4;
+  /** Product revision - n.m two 4-bit nibbles. */
+  uint8_t prv;
   // byte 9-12
-  /** Product serial number */
-  uint32_t psn;
-  // byte 13
-  /** Manufacturing date year high digit */
-  unsigned char mdt_year_high : 4;
-  /** not used */
-  unsigned char reserved : 4;
-  // byte 14
-  /** Manufacturing date month */
-  unsigned char mdt_month : 4;
-  /** Manufacturing date year low digit */
-  unsigned char mdt_year_low : 4;
+  /** Product serial 32-bit number Big Endian format. */
+  uint8_t psn8[4];
+  // byte 13-14
+  /** Manufacturing date big endian - four nibbles RYYM Reserved Year Month. */
+  uint8_t mdt[2];
   // byte 15
-  /** not used always 1 */
-  unsigned char always1 : 1;
-  /** CRC7 checksum */
-  unsigned char crc : 7;
+  /** CRC7 bits 1-7 checksum, bit 0 always 1 */
+  uint8_t crc;
+  // Extract big endian fields.
+  /** \return major revision number. */
+  int prvN() const {return prv >> 4;}
+  /** \return minor revision number. */
+  int prvM() const {return prv & 0XF;}
+  /** \return Manufacturing Year. */
+  int mdtYear() const {return 2000 + ((mdt[0] & 0XF) << 4) + (mdt[1] >> 4);}
+  /** \return Manufacturing Month. */
+  int mdtMonth() const {return mdt[1] & 0XF;}
+  /** \return Product Serial Number. */
+  uint32_t psn() const {
+  return (uint32_t)psn8[0] << 24 |
+         (uint32_t)psn8[1] << 16 |
+         (uint32_t)psn8[2] <<  8 |
+         (uint32_t)psn8[3];
+  }
 } __attribute__((packed)) cid_t;
-
-//==============================================================================
-#ifndef DOXYGEN_SHOULD_SKIP_THIS
-/**
- * \class CSDV1
- * \brief CSD register for version 1.00 cards .
- */
-typedef struct CSDV1 {
-  // byte 0
-  unsigned char reserved1 : 6;
-  unsigned char csd_ver : 2;
-  // byte 1
-  unsigned char taac;
-  // byte 2
-  unsigned char nsac;
-  // byte 3
-  unsigned char tran_speed;
-  // byte 4
-  unsigned char ccc_high;
-  // byte 5
-  unsigned char read_bl_len : 4;
-  unsigned char ccc_low : 4;
-  // byte 6
-  unsigned char c_size_high : 2;
-  unsigned char reserved2 : 2;
-  unsigned char dsr_imp : 1;
-  unsigned char read_blk_misalign : 1;
-  unsigned char write_blk_misalign : 1;
-  unsigned char read_bl_partial : 1;
-  // byte 7
-  unsigned char c_size_mid;
-  // byte 8
-  unsigned char vdd_r_curr_max : 3;
-  unsigned char vdd_r_curr_min : 3;
-  unsigned char c_size_low : 2;
-  // byte 9
-  unsigned char c_size_mult_high : 2;
-  unsigned char vdd_w_cur_max : 3;
-  unsigned char vdd_w_curr_min : 3;
-  // byte 10
-  unsigned char sector_size_high : 6;
-  unsigned char erase_blk_en : 1;
-  unsigned char c_size_mult_low : 1;
-  // byte 11
-  unsigned char wp_grp_size : 7;
-  unsigned char sector_size_low : 1;
-  // byte 12
-  unsigned char write_bl_len_high : 2;
-  unsigned char r2w_factor : 3;
-  unsigned char reserved3 : 2;
-  unsigned char wp_grp_enable : 1;
-  // byte 13
-  unsigned char reserved4 : 5;
-  unsigned char write_partial : 1;
-  unsigned char write_bl_len_low : 2;
-  // byte 14
-  unsigned char reserved5: 2;
-  unsigned char file_format : 2;
-  unsigned char tmp_write_protect : 1;
-  unsigned char perm_write_protect : 1;
-  unsigned char copy : 1;
-  /** Indicates the file format on the card */
-  unsigned char file_format_grp : 1;
-  // byte 15
-  unsigned char always1 : 1;
-  unsigned char crc : 7;
-} __attribute__((packed)) csd1_t;
 //==============================================================================
 /**
- * \class CSDV2
- * \brief CSD register for version 2.00 cards.
+ * \class CSD
+ * \brief Union of old and new style CSD register.
  */
-typedef struct CSDV2 {
-  // byte 0
-  unsigned char reserved1 : 6;
-  unsigned char csd_ver : 2;
-  // byte 1
-  /** fixed to 0X0E */
-  unsigned char taac;
-  // byte 2
-  /** fixed to 0 */
-  unsigned char nsac;
-  // byte 3
-  unsigned char tran_speed;
-  // byte 4
-  unsigned char ccc_high;
-  // byte 5
-  /** This field is fixed to 9h, which indicates READ_BL_LEN=512 Byte */
-  unsigned char read_bl_len : 4;
-  unsigned char ccc_low : 4;
-  // byte 6
-  /** not used */
-  unsigned char reserved2 : 4;
-  unsigned char dsr_imp : 1;
-  /** fixed to 0 */
-  unsigned char read_blk_misalign : 1;
-  /** fixed to 0 */
-  unsigned char write_blk_misalign : 1;
-  /** fixed to 0 - no partial read */
-  unsigned char read_bl_partial : 1;
-  // byte 7
-  /** high part of card size */
-  unsigned char c_size_high : 6;
-  /** not used */
-  unsigned char reserved3 : 2;
-  // byte 8
-  /** middle part of card size */
-  unsigned char c_size_mid;
-  // byte 9
-  /** low part of card size */
-  unsigned char c_size_low;
-  // byte 10
-  /** sector size is fixed at 64 KB */
-  unsigned char sector_size_high : 6;
-  /** fixed to 1 - erase single is supported */
-  unsigned char erase_blk_en : 1;
-  /** not used */
-  unsigned char reserved4 : 1;
-  // byte 11
-  unsigned char wp_grp_size : 7;
-  /** sector size is fixed at 64 KB */
-  unsigned char sector_size_low : 1;
-  // byte 12
-  /** write_bl_len fixed for 512 byte sectors */
-  unsigned char write_bl_len_high : 2;
-  /** fixed value of 2 */
-  unsigned char r2w_factor : 3;
-  /** not used */
-  unsigned char reserved5 : 2;
-  /** fixed value of 0 - no write protect groups */
-  unsigned char wp_grp_enable : 1;
-  // byte 13
-  unsigned char reserved6 : 5;
-  /** always zero - no partial sector read*/
-  unsigned char write_partial : 1;
-  /** write_bl_len fixed for 512 byte sectors */
-  unsigned char write_bl_len_low : 2;
-  // byte 14
-  unsigned char reserved7: 2;
-  /** Do not use always 0 */
-  unsigned char file_format : 2;
-  unsigned char tmp_write_protect : 1;
-  unsigned char perm_write_protect : 1;
-  unsigned char copy : 1;
-  /** Do not use always 0 */
-  unsigned char file_format_grp : 1;
-  // byte 15
-  /** not used always 1 */
-  unsigned char always1 : 1;
-  /** checksum */
-  unsigned char crc : 7;
-} __attribute__((packed)) csd2_t;
+typedef struct CSD {
+  /** union of all CSD versions */
+  uint8_t csd[16];
+  // Extract big endian fields.
+  /** \return Capacity in sectors */
+  uint32_t capacity() const {
+    uint32_t c_size;
+    uint8_t ver = csd[0] >> 6;
+    if (ver == 0) {
+      c_size = (uint32_t)(csd[6] & 3) << 10;
+      c_size |= (uint32_t)csd[7] << 2 | csd[8] >> 6;
+      uint8_t c_size_mult = (csd[9] & 3) << 1 | csd[10] >> 7;
+      uint8_t read_bl_len = csd[5] & 15;
+      return (c_size + 1) << (c_size_mult + read_bl_len + 2 - 9);
+    } else if (ver == 1) {
+      c_size = (uint32_t)(csd[7] & 63) << 16;
+      c_size |= (uint32_t)csd[8] << 8;
+      c_size |= csd[9];
+      return (c_size + 1) << 10;
+    } else {
+      return 0;
+    }
+  }
+  /** \return true if erase granularity is single block. */
+  bool eraseSingleBlock() const {return csd[10] & 0X40;}
+  /** \return erase size in 512 byte blocks if eraseSingleBlock is false. */
+  int eraseSize() const {return ((csd[10] & 0X3F) << 1 | csd[11] >> 7) + 1;}
+  /** \return true if the contents is copied or true if original. */
+  bool copy() const {return csd[14] & 0X40;}
+  /** \return true if the entire card is permanently write protected. */
+  bool permWriteProtect() const {return  csd[14] & 0X20;}
+  /** \return true if the entire card is temporarily write protected. */
+  bool tempWriteProtect() const {return  csd[14] & 0X10;}
+} csd_t;
 //==============================================================================
 /**
- * \class csd_t
- * \brief Union of old and new style CSD register.
+ * \class SCR
+ * \brief SCR register.
  */
-union csd_t {
-  csd1_t v1;
-  csd2_t v2;
-};
-//-----------------------------------------------------------------------------
-inline uint32_t sdCardCapacity(csd_t* csd) {
-  if (csd->v1.csd_ver == 0) {
-    uint8_t read_bl_len = csd->v1.read_bl_len;
-    uint16_t c_size = (csd->v1.c_size_high << 10)
-                      | (csd->v1.c_size_mid << 2) | csd->v1.c_size_low;
-    uint8_t c_size_mult = (csd->v1.c_size_mult_high << 1)
-                          | csd->v1.c_size_mult_low;
-    return (uint32_t)(c_size + 1) << (c_size_mult + read_bl_len - 7);
-  } else if (csd->v2.csd_ver == 1) {
-    return (((uint32_t)csd->v2.c_size_high << 16) +
-           ((uint16_t)csd->v2.c_size_mid << 8) + csd->v2.c_size_low + 1) << 10;
-  } else {
-    return 0;
+typedef struct SCR {
+  /** Bytes 0-3 SD Association, bytes 4-7 reserved for manufacturer. */
+  uint8_t scr[8];
+  /** \return SCR_STRUCTURE field  - must be zero.*/
+  uint8_t srcStructure() {return scr[0] >> 4;}
+  /** \return SD_SPEC field 0 - v1.0 or V1.01, 1 - 1.10, 2 - V2.00 or greater */
+  uint8_t sdSpec() {return scr[0] & 0XF;}
+  /** \return false if all zero, true if all one. */
+  bool dataAfterErase() {return 0X80 & scr[1];}
+  /** \return CPRM Security Version. */
+  uint8_t sdSecurity() {return (scr[1] >> 4) & 0X7;}
+  /** \return 0101b.  */
+  uint8_t sdBusWidths() {return scr[1] & 0XF;}
+  /** \return true if V3.0 or greater. */
+  bool sdSpec3() {return scr[2] & 0X80;}
+  /** \return if true and sdSpecX is zero V4.xx. */
+  bool sdSpec4() {return scr[2] & 0X4;}
+  /** \return nonzero for version 5 or greater if sdSpec == 2,
+              sdSpec3 == true. Version is return plus four.*/
+  uint8_t sdSpecX() {return (scr[2] & 0X3) << 2 | scr[3] >> 6;}
+  /** \return bit map for support CMD58/59, CMD48/49, CMD23, and CMD20 */
+  uint8_t cmdSupport() {return scr[3] &0XF;}
+  /** \return SD spec version */
+  int16_t sdSpecVer() {
+    if (sdSpec() > 2) {
+      return -1;
+    } else if (sdSpec() < 2) {
+      return sdSpec() ? 110 : 101;
+    } else if (!sdSpec3()) {
+      return 200;
+    } else if (!sdSpec4() && !sdSpecX()) {
+      return 300;
+    }
+    return 400 + 100*sdSpecX();
   }
-}
-//-----------------------------------------------------------------------------
+} scr_t;
+//==============================================================================
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
 // fields are big endian
 typedef struct SdStatus {
+  //
   uint8_t busWidthSecureMode;
   uint8_t reserved1;
+  // byte 2
   uint8_t sdCardType[2];
+  // byte 4
   uint8_t sizeOfProtectedArea[4];
+  // byte 8
   uint8_t speedClass;
+  // byte 9
   uint8_t performanceMove;
+  // byte 10
   uint8_t auSize;
+  // byte 11
   uint8_t eraseSize[2];
+  // byte 13
   uint8_t eraseTimeoutOffset;
+  // byte 14
   uint8_t uhsSpeedAuSize;
+  // byte 15
   uint8_t videoSpeed;
+  // byte 16
   uint8_t vscAuSize[2];
+  // byte 18
   uint8_t susAddr[3];
+  // byte 21
   uint8_t reserved2[3];
+  // byte 24
   uint8_t reservedManufacturer[40];
 } SdStatus_t;
 #endif  // DOXYGEN_SHOULD_SKIP_THIS

+ 14 - 1
src/SdCard/SdCardInterface.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License
@@ -32,6 +32,13 @@
  */
 class SdCardInterface : public FsBlockDeviceInterface {
  public:
+  /** CMD6 Switch mode: Check Function Set Function.
+   * \param[in] arg CMD6 argument.
+   * \param[out] status return status data.
+   *
+   * \return true for success or false for failure.
+   */
+  virtual bool cardCMD6(uint32_t arg, uint8_t* status) = 0;
   /** end use of card */
   virtual void end() = 0;
    /** Erase a range of sectors.
@@ -82,6 +89,12 @@ class SdCardInterface : public FsBlockDeviceInterface {
    * \return true for success or false for failure.
    */
   virtual bool readOCR(uint32_t* ocr) = 0;
+  /** Read SCR register.
+   *
+   * \param[out] scr Value of SCR register.
+   * \return true for success or false for failure.
+   */
+  virtual bool readSCR(scr_t *scr) = 0;
   /**
    * Determine the size of an SD flash memory card.
    *

+ 62 - 14
src/SdCard/SdSpiCard.cpp

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License
@@ -132,6 +132,7 @@ static uint16_t CRC_CCITT(const uint8_t* data, size_t n) {
 bool SharedSpiCard::begin(SdSpiConfig spiConfig) {
   Timeout timeout;
   m_spiActive = false;
+  m_beginCalled = false;
   m_errorCode = SD_CARD_ERROR_NONE;
   m_type = 0;
   m_csPin = spiConfig.csPin;
@@ -146,6 +147,7 @@ bool SharedSpiCard::begin(SdSpiConfig spiConfig) {
   spiUnselect();
   spiSetSckSpeed(1000UL*SD_MAX_INIT_RATE_KHZ);
   spiBegin(spiConfig);
+  m_beginCalled = true;
   uint32_t arg;
   m_state = IDLE_STATE;
   spiStart();
@@ -165,6 +167,10 @@ bool SharedSpiCard::begin(SdSpiConfig spiConfig) {
       error(SD_CARD_ERROR_CMD0);
       goto fail;
     }
+    // Force any active transfer to end for an already initialized card.
+    for (uint8_t j = 0; j < 0XFF; j++) {
+      spiSend(0XFF);
+    }
   }
 #if USE_SD_CRC
   if (cardCommand(CMD59, 1) != R1_IDLE_STATE) {
@@ -172,7 +178,6 @@ bool SharedSpiCard::begin(SdSpiConfig spiConfig) {
     goto fail;
   }
 #endif  // USE_SD_CRC
-
   // check SD version
   if (!(cardCommand(CMD8, 0x1AA) & R1_ILLEGAL_COMMAND)) {
     type(SD_CARD_TYPE_SD2);
@@ -220,6 +225,22 @@ bool SharedSpiCard::begin(SdSpiConfig spiConfig) {
   return false;
 }
 //------------------------------------------------------------------------------
+bool SharedSpiCard::cardCMD6(uint32_t arg, uint8_t* status) {
+  if (cardCommand(CMD6, arg)) {
+    error(SD_CARD_ERROR_CMD6);
+    goto fail;
+  }
+  if (!readData(status, 64)) {
+    goto fail;
+  }
+  spiStop();
+  return true;
+
+ fail:
+  spiStop();
+  return false;
+}
+//------------------------------------------------------------------------------
 // send command and return error code.  Return zero for OK
 uint8_t SharedSpiCard::cardCommand(uint8_t cmd, uint32_t arg) {
   if (!syncDevice()) {
@@ -229,12 +250,9 @@ uint8_t SharedSpiCard::cardCommand(uint8_t cmd, uint32_t arg) {
   if (!m_spiActive) {
     spiStart();
   }
-  if (cmd != CMD12) {
-    if (!waitReady(SD_CMD_TIMEOUT) && cmd != CMD0) {
-      return 0XFF;
-    }
+  if (cmd != CMD0 && cmd != CMD12 && !waitReady(SD_CMD_TIMEOUT)) {
+    return 0XFF;
   }
-
 #if USE_SD_CRC
   // form message
   uint8_t buf[6];
@@ -274,15 +292,23 @@ uint8_t SharedSpiCard::cardCommand(uint8_t cmd, uint32_t arg) {
   return m_status;
 }
 //------------------------------------------------------------------------------
+void SharedSpiCard::end() {
+  if (m_beginCalled) {
+    spiStop();
+    spiEnd();
+    m_beginCalled = false;
+  }
+}
+//------------------------------------------------------------------------------
 bool SharedSpiCard::erase(uint32_t firstSector, uint32_t lastSector) {
   csd_t csd;
   if (!readCSD(&csd)) {
     goto fail;
   }
   // check for single sector erase
-  if (!csd.v1.erase_blk_en) {
+  if (!csd.eraseSingleBlock()) {
     // erase size mask
-    uint8_t m = (csd.v1.sector_size_high << 1) | csd.v1.sector_size_low;
+    uint8_t m = csd.eraseSize() - 1;
     if ((firstSector & m) != 0 || ((lastSector + 1) & m) != 0) {
       // error card can't erase specified area
       error(SD_CARD_ERROR_ERASE_SINGLE_SECTOR);
@@ -313,7 +339,7 @@ bool SharedSpiCard::erase(uint32_t firstSector, uint32_t lastSector) {
 //------------------------------------------------------------------------------
 bool SharedSpiCard::eraseSingleSectorEnable() {
   csd_t csd;
-  return readCSD(&csd) ? csd.v1.erase_blk_en : false;
+  return readCSD(&csd) ? csd.eraseSingleBlock() : false;
 }
 //------------------------------------------------------------------------------
 bool SharedSpiCard::isBusy() {
@@ -384,7 +410,11 @@ bool SharedSpiCard::readOCR(uint32_t* ocr) {
     goto fail;
   }
   for (uint8_t i = 0; i < 4; i++) {
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
     p[3 - i] = spiReceive();
+#else  // __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+    p[i] = spiReceive();
+#endif  // __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
   }
   spiStop();
   return true;
@@ -412,6 +442,23 @@ bool SharedSpiCard::readRegister(uint8_t cmd, void* buf) {
   return false;
 }
 //------------------------------------------------------------------------------
+bool SharedSpiCard::readSCR(scr_t* scr) {
+  uint8_t* dst = reinterpret_cast<uint8_t*>(scr);
+  if (cardAcmd(ACMD51, 0)) {
+    error(SD_CARD_ERROR_ACMD51);
+    goto fail;
+  }
+  if (!readData(dst, sizeof(scr_t))) {
+    goto fail;
+  }
+  spiStop();
+  return true;
+
+ fail:
+  spiStop();
+  return false;
+}
+//------------------------------------------------------------------------------
 bool SharedSpiCard::readSector(uint32_t sector, uint8_t* dst) {
   // use address if not SDHC card
   if (type() != SD_CARD_TYPE_SDHC) {
@@ -462,13 +509,14 @@ bool SharedSpiCard::readStart(uint32_t sector) {
   return false;
 }
 //------------------------------------------------------------------------------
-bool SharedSpiCard::readStatus(uint8_t* status) {
+bool SharedSpiCard::readStatus(SdStatus* status) {
+  uint8_t* dst = reinterpret_cast<uint8_t*>(status);
   // retrun is R2 so read extra status byte.
   if (cardAcmd(ACMD13, 0) || spiReceive()) {
     error(SD_CARD_ERROR_ACMD13);
     goto fail;
   }
-  if (!readData(status, 64)) {
+  if (!readData(dst, 64)) {
     goto fail;
   }
   spiStop();
@@ -495,16 +543,16 @@ bool SharedSpiCard::readStop() {
 //------------------------------------------------------------------------------
 uint32_t SharedSpiCard::sectorCount() {
   csd_t csd;
-  return readCSD(&csd) ? sdCardCapacity(&csd) : 0;
+  return readCSD(&csd) ? csd.capacity() : 0;
 }
 //------------------------------------------------------------------------------
 void SharedSpiCard::spiStart() {
   if (!m_spiActive) {
     spiActivate();
+    m_spiActive = true;
     spiSelect();
     // Dummy byte to drive MISO busy status.
     spiSend(0XFF);
-    m_spiActive = true;
   }
 }
 //------------------------------------------------------------------------------

+ 37 - 5
src/SdCard/SdSpiCard.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License
@@ -33,6 +33,17 @@
 #include "SdCardInfo.h"
 #include "SdCardInterface.h"
 #include "../SpiDriver/SdSpiDriver.h"
+/** Verify correct SPI active if non-zero. */
+#define CHECK_SPI_ACTIVE 0
+#if CHECK_SPI_ACTIVE
+/** Check SPI active. */
+#define SPI_ASSERT_ACTIVE {if (!m_spiActive) {\
+  Serial.print(F("SPI_ASSERTACTIVE"));\
+  Serial.println(__LINE__);}}
+#else  // CHECK_SPI_ACTIVE
+/** Do not check SPI active. */
+#define SPI_ASSERT_ACTIVE
+#endif  // CHECK_SPI_ACTIVE
 //==============================================================================
 /**
  * \class SharedSpiCard
@@ -59,10 +70,15 @@ class SharedSpiCard {
    * \return true for success or false for failure.
    */
   bool begin(SdSpiConfig spiConfig);
+  /** CMD6 Switch mode: Check Function Set Function.
+   * \param[in] arg CMD6 argument.
+   * \param[out] status return status data.
+   *
+   * \return true for success or false for failure.
+   */
+  bool cardCMD6(uint32_t arg, uint8_t* status);
   /** End use of card */
-  void end() {
-    spiEnd();
-  }
+  void end();
   /** Erase a range of sectors.
    *
    * \param[in] firstSector The address of the first sector in the range.
@@ -146,6 +162,12 @@ class SharedSpiCard {
    * \return true for success or false for failure.
    */
   bool readOCR(uint32_t* ocr);
+  /** Read SCR register.
+   *
+   * \param[out] scr Value of SCR register.
+   * \return true for success or false for failure.
+   */
+  bool readSCR(scr_t* scr);
   /**
    * Read a 512 byte sector from an SD card.
    *
@@ -178,7 +200,7 @@ class SharedSpiCard {
    * \param[out] status location for 64 status bytes.
    * \return true for success or false for failure.
    */
-  bool readStatus(uint8_t* status);
+  bool readStatus(SdStatus* status);
   /** End a read multiple sectors sequence.
    *
    * \return true for success or false for failure.
@@ -293,15 +315,19 @@ class SharedSpiCard {
     m_spiDriver.end();
   }
   uint8_t spiReceive() {
+    SPI_ASSERT_ACTIVE;
     return m_spiDriver.receive();
   }
   uint8_t spiReceive(uint8_t* buf, size_t n) {
+    SPI_ASSERT_ACTIVE;
     return m_spiDriver.receive(buf, n);
   }
   void spiSend(uint8_t data) {
+    SPI_ASSERT_ACTIVE;
     m_spiDriver.send(data);
   }
   void spiSend(const uint8_t* buf, size_t n) {
+    SPI_ASSERT_ACTIVE;
     m_spiDriver.send(buf, n);
   }
   void spiSetSckSpeed(uint32_t maxSck) {
@@ -322,22 +348,28 @@ class SharedSpiCard {
     m_spiDriverPtr->end();
   }
   uint8_t spiReceive() {
+    SPI_ASSERT_ACTIVE;
     return m_spiDriverPtr->receive();
   }
   uint8_t spiReceive(uint8_t* buf, size_t n) {
+    SPI_ASSERT_ACTIVE;
     return m_spiDriverPtr->receive(buf, n);
   }
   void spiSend(uint8_t data) {
+    SPI_ASSERT_ACTIVE;
     m_spiDriverPtr->send(data);
   }
   void spiSend(const uint8_t* buf, size_t n) {
+    SPI_ASSERT_ACTIVE;
     m_spiDriverPtr->send(buf, n);
   }
   void spiSetSckSpeed(uint32_t maxSck) {
     m_spiDriverPtr->setSckSpeed(maxSck);
   }
   SdSpiDriver* m_spiDriverPtr;
+
 #endif  // SPI_DRIVER_SELECT < 2
+  bool m_beginCalled = false;
   SdCsPin_t m_csPin;
   uint8_t m_errorCode = SD_CARD_ERROR_INIT_NOT_CALLED;
   bool    m_spiActive;

+ 14 - 1
src/SdCard/SdioCard.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License
@@ -60,6 +60,13 @@ class SdioCard : public SdCardInterface {
    * \return true for success or false for failure.
    */
   bool begin(SdioConfig sdioConfig);
+  /** CMD6 Switch mode: Check Function Set Function.
+   * \param[in] arg CMD6 argument.
+   * \param[out] status return status data.
+   *
+   * \return true for success or false for failure.
+   */
+  bool cardCMD6(uint32_t arg, uint8_t* status);
   /** Disable an SDIO card.
    * not implemented.
    */
@@ -146,6 +153,12 @@ class SdioCard : public SdCardInterface {
    * \return true for success or false for failure.
    */
   bool readOCR(uint32_t* ocr);
+  /** Read SCR register.
+   *
+   * \param[out] scr Value of SCR register.
+   * \return true for success or false for failure.
+   */
+  bool readSCR(scr_t *scr);
   /** Start a read multiple sectors sequence.
    *
    * \param[in] sector Address of first sector in sequence.

+ 45 - 13
src/SdCard/SdioTeensy.cpp

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License
@@ -130,6 +130,9 @@ const uint32_t ACMD6_XFERTYP = SDHC_XFERTYP_CMDINX(ACMD6) | CMD_RESP_R1;
 
 const uint32_t ACMD41_XFERTYP = SDHC_XFERTYP_CMDINX(ACMD41) | CMD_RESP_R3;
 
+const uint32_t ACMD51_XFERTYP = SDHC_XFERTYP_CMDINX(ACMD51) | CMD_RESP_R1 |
+                                DATA_READ_DMA;
+
 const uint32_t CMD0_XFERTYP = SDHC_XFERTYP_CMDINX(CMD0) | CMD_RESP_NONE;
 
 const uint32_t CMD2_XFERTYP = SDHC_XFERTYP_CMDINX(CMD2) | CMD_RESP_R2;
@@ -208,6 +211,7 @@ static uint32_t m_sdClkKhz = 0;
 static uint32_t m_ocr;
 static cid_t m_cid;
 static csd_t m_csd;
+static scr_t m_scr;
 //==============================================================================
 #define DBG_TRACE Serial.print("TRACE."); Serial.println(__LINE__); delay(200);
 #define USE_DEBUG_MODE 0
@@ -402,17 +406,17 @@ static bool cardCommand(uint32_t xfertyp, uint32_t arg) {
          !(m_irqstat & SDHC_IRQSTAT_CMD_ERROR);
 }
 //------------------------------------------------------------------------------
-static bool cardCMD6(uint32_t arg, uint8_t* status) {
-  // CMD6 returns 64 bytes.
+static bool cardACMD51(scr_t* scr) {
+  // ACMD51 returns 8 bytes.
   if (waitTimeout(isBusyCMD13)) {
     return sdError(SD_CARD_ERROR_CMD13);
   }
   enableDmaIrs();
-  SDHC_DSADDR  = (uint32_t)status;
-  SDHC_BLKATTR = SDHC_BLKATTR_BLKCNT(1) | SDHC_BLKATTR_BLKSIZE(64);
+  SDHC_DSADDR  = (uint32_t)scr;
+  SDHC_BLKATTR = SDHC_BLKATTR_BLKCNT(1) | SDHC_BLKATTR_BLKSIZE(8);
   SDHC_IRQSIGEN = SDHC_IRQSIGEN_MASK;
-  if (!cardCommand(CMD6_XFERTYP, arg)) {
-    return sdError(SD_CARD_ERROR_CMD6);
+  if (!cardAcmd(m_rca, ACMD51_XFERTYP, 0)) {
+    return sdError(SD_CARD_ERROR_ACMD51);
   }
   if (!waitDmaStatus()) {
     return sdError(SD_CARD_ERROR_DMA);
@@ -662,7 +666,10 @@ bool SdioCard::begin(SdioConfig sdioConfig) {
       m_version2 = true;
       break;
     }
+    SDHC_SYSCTL |= SDHC_SYSCTL_RSTA;
+    while (SDHC_SYSCTL & SDHC_SYSCTL_RSTA) {}
   }
+  // Must support 3.2-3.4 Volts
   arg = m_version2 ? 0X40300000 : 0x00300000;
   int m = micros();
   do {
@@ -703,10 +710,14 @@ bool SdioCard::begin(SdioConfig sdioConfig) {
 
   SDHC_WML = SDHC_WML_RDWML(FIFO_WML) | SDHC_WML_WRWML(FIFO_WML);
 
+  if (!cardACMD51(&m_scr)) {
+    return false;
+  }
   // Determine if High Speed mode is supported and set frequency.
   // Check status[16] for error 0XF or status[16] for new mode 0X1.
   uint8_t status[64];
-  if (cardCMD6(0X00FFFFFF, status) && (2 & status[13]) &&
+  if (m_scr.sdSpec() > 0 &&
+      cardCMD6(0X00FFFFFF, status) && (2 & status[13]) &&
       cardCMD6(0X80FFFFF1, status) && (status[16] & 0XF) == 1) {
     kHzSdClk = 50000;
   } else {
@@ -724,16 +735,32 @@ bool SdioCard::begin(SdioConfig sdioConfig) {
   return true;
 }
 //------------------------------------------------------------------------------
+bool SdioCard::cardCMD6(uint32_t arg, uint8_t* status) {
+  // CMD6 returns 64 bytes.
+  if (waitTimeout(isBusyCMD13)) {
+    return sdError(SD_CARD_ERROR_CMD13);
+  }
+  enableDmaIrs();
+  SDHC_DSADDR  = (uint32_t)status;
+  SDHC_BLKATTR = SDHC_BLKATTR_BLKCNT(1) | SDHC_BLKATTR_BLKSIZE(64);
+  SDHC_IRQSIGEN = SDHC_IRQSIGEN_MASK;
+  if (!cardCommand(CMD6_XFERTYP, arg)) {
+    return sdError(SD_CARD_ERROR_CMD6);
+  }
+  if (!waitDmaStatus()) {
+    return sdError(SD_CARD_ERROR_DMA);
+  }
+  return true;
+}
+//------------------------------------------------------------------------------
 bool SdioCard::erase(uint32_t firstSector, uint32_t lastSector) {
-#if ENABLE_TEENSY_SDIO_MOD
   if (m_curState != IDLE_STATE && !syncDevice()) {
     return false;
   }
-#endif  // ENABLE_TEENSY_SDIO_MOD
   // check for single sector erase
-  if (!m_csd.v1.erase_blk_en) {
+  if (!m_csd.eraseSingleBlock()) {
     // erase size mask
-    uint8_t m = (m_csd.v1.sector_size_high << 1) | m_csd.v1.sector_size_low;
+    uint8_t m = m_csd.eraseSize() - 1;
     if ((firstSector & m) != 0 || ((lastSector + 1) & m) != 0) {
       // error card can't erase specified area
       return sdError(SD_CARD_ERROR_ERASE_SINGLE_SECTOR);
@@ -843,6 +870,11 @@ bool SdioCard::readOCR(uint32_t* ocr) {
   return true;
 }
 //------------------------------------------------------------------------------
+bool SdioCard::readSCR(scr_t* scr) {
+  memcpy(scr, &m_scr, 8);
+  return true;
+}
+//------------------------------------------------------------------------------
 bool SdioCard::readSector(uint32_t sector, uint8_t* dst) {
   if (m_sdioConfig.useDma()) {
     uint8_t aligned[512];
@@ -933,7 +965,7 @@ bool SdioCard::readStop() {
 }
 //------------------------------------------------------------------------------
 uint32_t SdioCard::sectorCount() {
-  return sdCardCapacity(&m_csd);
+  return m_csd.capacity();
 }
 //------------------------------------------------------------------------------
 uint32_t SdioCard::status() {

+ 5 - 5
src/SdFat.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License
@@ -38,9 +38,9 @@
 #endif  // INCLUDE_SDIOS
 //------------------------------------------------------------------------------
 /** SdFat version for cpp use. */
-#define SD_FAT_VERSION 20102
+#define SD_FAT_VERSION 20200
 /** SdFat version as string. */
-#define SD_FAT_VERSION_STR "2.1.2"
+#define SD_FAT_VERSION_STR "2.2.0"
 //==============================================================================
 /**
  * \class SdBase
@@ -174,11 +174,11 @@ class SdBase : public Vol {
     }
     bool switchSpi = hasDedicatedSpi() && !isDedicatedSpi();
     if (switchSpi && !setDedicatedSpi(true)) {
-      return 0;
+      return false;
     }
     bool rtn = fmt.format(card(), mem, pr);
     if (switchSpi && !setDedicatedSpi(false)) {
-      return 0;
+      return false;
     }
     return rtn;
   }

+ 3 - 2
src/SdFatConfig.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License
@@ -41,6 +41,7 @@
 // #define ENABLE_DEDICATED_SPI 0
 // #define USE_LONG_FILE_NAMES 0
 // #define SDFAT_FILE_TYPE 1
+// #define CHECK_FLASH_PROGRAMMING 0  // May cause SD to sleep at high current.
 //
 // Options can be set in a makefile or an IDE like platformIO
 // if they are in a #ifndef/#endif block below.
@@ -258,7 +259,7 @@ typedef uint8_t SdCsPin_t;
  * is non-zero.
  */
 #ifndef CHECK_FLASH_PROGRAMMING
-#define CHECK_FLASH_PROGRAMMING 0
+#define CHECK_FLASH_PROGRAMMING 1
 #endif  // CHECK_FLASH_PROGRAMMING
 //------------------------------------------------------------------------------
 /**

+ 1 - 1
src/SpiDriver/SdSpiArduinoDriver.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/SpiDriver/SdSpiArtemis.cpp

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/SpiDriver/SdSpiAvr.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/SpiDriver/SdSpiBareUnoDriver.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/SpiDriver/SdSpiBaseClass.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/SpiDriver/SdSpiChipSelect.cpp

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/SpiDriver/SdSpiDriver.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/SpiDriver/SdSpiDue.cpp

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/SpiDriver/SdSpiESP.cpp

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/SpiDriver/SdSpiLibDriver.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/SpiDriver/SdSpiParticle.cpp

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/SpiDriver/SdSpiSTM32.cpp

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/SpiDriver/SdSpiSTM32Core.cpp

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/SpiDriver/SdSpiSoftDriver.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/SpiDriver/SdSpiTeensy3.cpp

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/common/ArduinoFiles.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/common/CompileDateTime.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/common/DebugMacros.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/common/FmtNumber.cpp

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/common/FmtNumber.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/common/FsApiConstants.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/common/FsBlockDevice.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/common/FsBlockDeviceInterface.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/common/FsCache.cpp

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/common/FsCache.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/common/FsDateTime.cpp

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/common/FsDateTime.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/common/FsName.cpp

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/common/FsName.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 1 - 1
src/common/FsStructs.cpp

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

+ 43 - 35
src/common/FsStructs.h

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License
@@ -26,9 +26,9 @@
 #define FsStructs_h
 #include <stddef.h>
 #include <stdint.h>
-//-----------------------------------------------------------------------------
+//------------------------------------------------------------------------------
 void lbaToMbrChs(uint8_t* chs, uint32_t capacityMB, uint32_t lba);
-//-----------------------------------------------------------------------------
+//------------------------------------------------------------------------------
 #if !defined(USE_SIMPLE_LITTLE_ENDIAN) || USE_SIMPLE_LITTLE_ENDIAN
 // assumes CPU is little-endian and handles alignment issues.
 inline uint16_t getLe16(const uint8_t* src) {
@@ -149,7 +149,7 @@ typedef struct {
   uint64_t position;
   uint32_t cluster;
 } fspos_t;
-//=============================================================================
+//==============================================================================
 const uint8_t EXTENDED_BOOT_SIGNATURE = 0X29;
 typedef struct biosParameterBlockFat16 {
   uint8_t  bytesPerSector[2];
@@ -172,7 +172,7 @@ typedef struct biosParameterBlockFat16 {
   uint8_t  volumeLabel[11];
   uint8_t  volumeType[8];
 } BpbFat16_t;
-//-----------------------------------------------------------------------------
+//------------------------------------------------------------------------------
 typedef struct biosParameterBlockFat32 {
   uint8_t  bytesPerSector[2];
   uint8_t  sectorsPerCluster;
@@ -202,7 +202,7 @@ typedef struct biosParameterBlockFat32 {
   uint8_t  volumeLabel[11];
   uint8_t  volumeType[8];
 } BpbFat32_t;
-//-----------------------------------------------------------------------------
+//------------------------------------------------------------------------------
 typedef struct partitionBootSectorFat {
   uint8_t  jmpInstruction[3];
   char     oemName[8];
@@ -214,7 +214,7 @@ typedef struct partitionBootSectorFat {
   uint8_t  bootCode[390];
   uint8_t  signature[2];
 } PbsFat_t;
-//-----------------------------------------------------------------------------
+//------------------------------------------------------------------------------
 const uint32_t FSINFO_LEAD_SIGNATURE = 0X41615252;
 const uint32_t FSINFO_STRUCT_SIGNATURE = 0x61417272;
 const uint32_t FSINFO_TRAIL_SIGNATURE = 0xAA550000;
@@ -227,17 +227,25 @@ typedef struct FsInfoSector {
   uint8_t reserved2[12];
   uint8_t trailSignature[4];
 } FsInfo_t;
-//-----------------------------------------------------------------------------
-/** name[0] value for entry that is free after being "deleted" */
-const uint8_t FAT_NAME_DELETED = 0XE5;
+//==============================================================================
+/** Attributes common to FAT and exFAT */
+const uint8_t FS_ATTRIB_READ_ONLY = 0x01;
+const uint8_t FS_ATTRIB_HIDDEN    = 0x02;
+const uint8_t FS_ATTRIB_SYSTEM    = 0x04;
+const uint8_t FS_ATTRIB_DIRECTORY = 0x10;
+const uint8_t FS_ATTRIB_ARCHIVE   = 0x20;
+// Attributes that users can change.
+const uint8_t FS_ATTRIB_USER_SETTABLE = FS_ATTRIB_READ_ONLY |
+  FS_ATTRIB_HIDDEN | FS_ATTRIB_SYSTEM | FS_ATTRIB_ARCHIVE;
+// Attributes to copy when a file is opened.
+const uint8_t FS_ATTRIB_COPY = FS_ATTRIB_USER_SETTABLE | FS_ATTRIB_DIRECTORY;
+//==============================================================================
 /** name[0] value for entry that is free and no allocated entries follow */
 const uint8_t FAT_NAME_FREE = 0X00;
-const uint8_t FAT_ATTRIB_READ_ONLY = 0x01;
-const uint8_t FAT_ATTRIB_HIDDEN    = 0x02;
-const uint8_t FAT_ATTRIB_SYSTEM    = 0x04;
+/** name[0] value for entry that is free after being "deleted" */
+const uint8_t FAT_NAME_DELETED = 0XE5;
+// Directiry attribute of volume label.
 const uint8_t FAT_ATTRIB_LABEL     = 0x08;
-const uint8_t FAT_ATTRIB_DIRECTORY = 0x10;
-const uint8_t FAT_ATTRIB_ARCHIVE   = 0x20;
 const uint8_t FAT_ATTRIB_LONG_NAME = 0X0F;
 /** Filename base-name is all lower case */
 const uint8_t FAT_CASE_LC_BASE = 0X08;
@@ -259,20 +267,20 @@ typedef struct {
   uint8_t  fileSize[4];
 } DirFat_t;
 
-static inline bool isFileDir(const DirFat_t* dir) {
-  return (dir->attributes & (FAT_ATTRIB_DIRECTORY | FAT_ATTRIB_LABEL)) == 0;
+static inline bool isFatFile(const DirFat_t* dir) {
+  return (dir->attributes & (FS_ATTRIB_DIRECTORY | FAT_ATTRIB_LABEL)) == 0;
 }
-static inline bool isFileOrSubdir(const DirFat_t* dir) {
+static inline bool isFatFileOrSubdir(const DirFat_t* dir) {
   return (dir->attributes & FAT_ATTRIB_LABEL) == 0;
 }
-static inline uint8_t isLongName(const DirFat_t* dir) {
+static inline uint8_t isFatLongName(const DirFat_t* dir) {
   return dir->attributes == FAT_ATTRIB_LONG_NAME;
 }
-static inline bool isSubdir(const DirFat_t* dir) {
-  return (dir->attributes & (FAT_ATTRIB_DIRECTORY | FAT_ATTRIB_LABEL))
-          == FAT_ATTRIB_DIRECTORY;
+static inline bool isFatSubdir(const DirFat_t* dir) {
+  return (dir->attributes & (FS_ATTRIB_DIRECTORY | FAT_ATTRIB_LABEL))
+          == FS_ATTRIB_DIRECTORY;
 }
-//-----------------------------------------------------------------------------
+//------------------------------------------------------------------------------
 /**
  * Order mask that indicates the entry is the last long dir entry in a
  * set of long dir entries. All valid sets of long dir entries must
@@ -292,11 +300,11 @@ typedef struct {
   uint8_t  mustBeZero2[2];
   uint8_t  unicode3[4];
 } DirLfn_t;
-//=============================================================================
+//==============================================================================
 inline uint32_t exFatChecksum(uint32_t sum, uint8_t data) {
   return (sum << 31) + (sum >> 1) + data;
 }
-//-----------------------------------------------------------------------------
+//------------------------------------------------------------------------------
 typedef struct biosParameterBlockExFat {
   uint8_t mustBeZero[53];
   uint8_t partitionOffset[8];
@@ -316,7 +324,7 @@ typedef struct biosParameterBlockExFat {
   uint8_t percentInUse;
   uint8_t reserved[7];
 } BpbExFat_t;
-//-----------------------------------------------------------------------------
+//------------------------------------------------------------------------------
 typedef struct ExFatBootSector {
   uint8_t  jmpInstruction[3];
   char     oemName[8];
@@ -324,7 +332,7 @@ typedef struct ExFatBootSector {
   uint8_t  bootCode[390];
   uint8_t  signature[2];
 } ExFatPbs_t;
-//-----------------------------------------------------------------------------
+//------------------------------------------------------------------------------
 const uint32_t EXFAT_EOC = 0XFFFFFFFF;
 
 const uint8_t EXFAT_TYPE_BITMAP = 0X81;
@@ -335,7 +343,7 @@ typedef struct {
   uint8_t  firstCluster[4];
   uint8_t  size[8];
 } DirBitmap_t;
-//-----------------------------------------------------------------------------
+//------------------------------------------------------------------------------
 const uint8_t EXFAT_TYPE_UPCASE = 0X82;
 typedef struct {
   uint8_t  type;
@@ -345,7 +353,7 @@ typedef struct {
   uint8_t  firstCluster[4];
   uint8_t  size[8];
 } DirUpcase_t;
-//-----------------------------------------------------------------------------
+//------------------------------------------------------------------------------
 const uint8_t EXFAT_TYPE_LABEL = 0X83;
 typedef struct {
   uint8_t  type;
@@ -353,14 +361,14 @@ typedef struct {
   uint8_t  unicode[22];
   uint8_t  reserved[8];
 } DirLabel_t;
-//-----------------------------------------------------------------------------
+//------------------------------------------------------------------------------
+// Last entry in directory.
+const uint8_t EXFAT_TYPE_END_DIR     = 0X00;
+// Entry is used if bit is set.
+const uint8_t EXFAT_TYPE_USED        = 0X80;
 const uint8_t EXFAT_TYPE_FILE        = 0X85;
-const uint8_t EXFAT_ATTRIB_READ_ONLY = 0x01;
-const uint8_t EXFAT_ATTRIB_HIDDEN    = 0x02;
-const uint8_t EXFAT_ATTRIB_SYSTEM    = 0x04;
+// File attribute reserved since used for FAT volume label.
 const uint8_t EXFAT_ATTRIB_RESERVED  = 0x08;
-const uint8_t EXFAT_ATTRIB_DIRECTORY = 0x10;
-const uint8_t EXFAT_ATTRIB_ARCHIVE   = 0x20;
 
 typedef struct {
   uint8_t  type;

+ 1 - 1
src/common/FsUtf.cpp

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2011-2021 Bill Greiman
+ * Copyright (c) 2011-2022 Bill Greiman
  * This file is part of the SdFat library for SD memory cards.
  *
  * MIT License

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است