Эх сурвалжийг харах

New features, major internal changes, bug fixes

Bill Greiman 2 жил өмнө
parent
commit
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.
 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
 0X15,SD_CARD_ERROR_ACMD13 - Read extended status
 0X16,SD_CARD_ERROR_ACMD23 - Set pre-erased count
 0X16,SD_CARD_ERROR_ACMD23 - Set pre-erased count
 0X17,SD_CARD_ERROR_ACMD41 - Activate card initialization
 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.
 backward compatible with SdFat Version 1 for FAT16/FAT32 cards.
 
 
 You should edit SdFatConfig.h to select features. The default version of
 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
 \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.
 on Standard SD, SDHC, and SDXC cards.
- 
+
 In %SdFat version 1, SdFat and File are the main classes.
 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
 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
 SdFat32 supports FAT16 and FAT32. SdExFat supports exFAT, SdFs supports
 FAT16, FAT32, and exFAT.
 FAT16, FAT32, and exFAT.
 
 
@@ -69,7 +69,7 @@ boards.
 #endif  // defined(__AVR__) && FLASHEND < 0X8000
 #endif  // defined(__AVR__) && FLASHEND < 0X8000
 \endcode
 \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
 on an Uno or other AVR board with 32KB flash and 2KB SRAM but memory
 will be very limited.
 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 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.
 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.
 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.
 Initially this directory is the root directory for the volume.
 
 
 The volume working directory is changed by calling the chdir(path).
 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
 \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
 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
 It will be necessary to unzip and rename the folder if you download a zip
 file from GitHub.
 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.
 // 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)
 #define SPI_CLOCK SD_SCK_MHZ(10)
+
 // Select fastest interface.
 // Select fastest interface.
 #if ENABLE_DEDICATED_SPI
 #if ENABLE_DEDICATED_SPI
 #define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK)
 #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;
 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);
 static ArduinoOutStream cout(Serial);
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
-bool cidDmp() {
+void cidDmp() {
   cout << F("\nManufacturer ID: ");
   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: ");
   cout << F("Product: ");
   for (uint8_t i = 0; i < 5; i++) {
   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 << 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;
   cout << endl;
-  return true;
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 void clearSerialInput() {
 void clearSerialInput() {
@@ -68,30 +67,24 @@ void clearSerialInput() {
   } while (micros() - m < 10000);
   } 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(" 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: ");
   cout << F("eraseSingleBlock: ");
-  if (eraseSingleBlock) {
+  if (csd.eraseSingleBlock()) {
     cout << F("true\n");
     cout << F("true\n");
   } else {
   } else {
     cout << F("false\n");
     cout << F("false\n");
   }
   }
-  return true;
+  cout << F("dataAfterErase: ");
+  if (scr.dataAfterErase()) {
+    cout << F("ones\n");
+  } else {
+    cout << F("zeros\n");
+  }
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 void errorPrint() {
 void errorPrint() {
@@ -99,7 +92,7 @@ void errorPrint() {
     cout << F("SD errorCode: ") << hex << showbase;
     cout << F("SD errorCode: ") << hex << showbase;
     printSdErrorSymbol(&Serial, sd.sdErrorCode());
     printSdErrorSymbol(&Serial, sd.sdErrorCode());
     cout << F(" = ") << int(sd.sdErrorCode()) << endl;
     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++) {
   for (uint8_t ip = 1; ip < 5; ip++) {
     MbrPart_t *pt = &mbr.part[ip - 1];
     MbrPart_t *pt = &mbr.part[ip - 1];
     if ((pt->boot != 0 && pt->boot != 0X80) ||
     if ((pt->boot != 0 && pt->boot != 0X80) ||
-        getLe32(pt->relativeSectors) > sdCardCapacity(&m_csd)) {
+        getLe32(pt->relativeSectors) > csd.capacity()) {
       valid = false;
       valid = false;
     }
     }
     cout << int(ip) << ',' << uppercase << showbase << hex;
     cout << int(ip) << ',' << uppercase << showbase << hex;
@@ -139,20 +132,22 @@ bool mbrDmp() {
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 void dmpVol() {
 void dmpVol() {
   cout << F("\nScanning FAT, please wait.\n");
   cout << F("\nScanning FAT, please wait.\n");
-  uint32_t freeClusterCount = sd.freeClusterCount();
+  int32_t freeClusterCount = sd.freeClusterCount();
   if (sd.fatType() <= 32) {
   if (sd.fatType() <= 32) {
     cout << F("\nVolume is FAT") << int(sd.fatType()) << endl;
     cout << F("\nVolume is FAT") << int(sd.fatType()) << endl;
   } else {
   } else {
     cout << F("\nVolume is exFAT\n");
     cout << F("\nVolume is exFAT\n");
   }
   }
   cout << F("sectorsPerCluster: ") << sd.sectorsPerCluster() << endl;
   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("fatStartSector:    ") << sd.fatStartSector() << endl;
   cout << F("dataStartSector:   ") << sd.dataStartSector() << 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;
       break;
 
 
     case SD_CARD_TYPE_SDHC:
     case SD_CARD_TYPE_SDHC:
-      if (sdCardCapacity(&m_csd) < 70000000) {
+      if (csd.capacity() < 70000000) {
         cout << F("SDHC\n");
         cout << F("SDHC\n");
       } else {
       } else {
         cout << F("SDXC\n");
         cout << F("SDXC\n");
@@ -239,20 +234,29 @@ void loop() {
     return;
     return;
   }
   }
   t = millis() - t;
   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");
     cout << F("readInfo failed\n");
     errorPrint();
     errorPrint();
     return;
     return;
   }
   }
   printCardType();
   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();
   cidDmp();
   csdDmp();
   csdDmp();
   cout << F("\nOCR: ") << uppercase << showbase;
   cout << F("\nOCR: ") << uppercase << showbase;
-  cout << hex << m_ocr << dec << endl;
+  cout << hex << ocr << dec << endl;
   if (!mbrDmp()) {
   if (!mbrDmp()) {
     return;
     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.
 // Try to select the best SD card configuration.
 #if HAS_SDIO_CLASS
 #if HAS_SDIO_CLASS
 #define SD_CONFIG SdioConfig(FIFO_SDIO)
 #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)
 #define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK)
 #else  // HAS_SDIO_CLASS
 #else  // HAS_SDIO_CLASS
 #define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SPI_CLOCK)
 #define SD_CONFIG SdSpiConfig(SD_CS_PIN, SHARED_SPI, SPI_CLOCK)
@@ -89,22 +89,19 @@ ArduinoOutStream cout(Serial);
 void cidDmp() {
 void cidDmp() {
   cid_t cid;
   cid_t cid;
   if (!sd.card()->readCID(&cid)) {
   if (!sd.card()->readCID(&cid)) {
-
     error("readCID failed");
     error("readCID failed");
   }
   }
   cout << F("\nManufacturer ID: ");
   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("OEM ID: ") << cid.oid[0] << cid.oid[1] << endl;
   cout << F("Product: ");
   cout << F("Product: ");
   for (uint8_t i = 0; i < 5; i++) {
   for (uint8_t i = 0; i < 5; i++) {
     cout << cid.pnm[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 << 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;
   cout << endl;
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
@@ -273,4 +270,5 @@ void loop() {
   }
   }
   cout << endl << F("Done") << endl;
   cout << endl << F("Done") << endl;
   file.close();
   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
 name=SdFat
-version=2.1.2
+version=2.2.0
 license=MIT
 license=MIT
 author=Bill Greiman <fat16lib@sbcglobal.net>
 author=Bill Greiman <fat16lib@sbcglobal.net>
 maintainer=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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * MIT License
@@ -455,7 +455,7 @@ bool ExFatPartition::printDir(print_t* pr, ExFatFile* file) {
       dir++;
       dir++;
     }
     }
 #endif  // RAW_ROOT
 #endif  // RAW_ROOT
-    if (dir->type == 0) {
+    if (dir->type == EXFAT_TYPE_END_DIR) {
       break;
       break;
     }
     }
     pr->println();
     pr->println();
@@ -520,7 +520,7 @@ bool ExFatPartition::printDir(print_t* pr, ExFatFile* file) {
         break;
         break;
 
 
       default:
       default:
-        if (dir->type & 0x80) {
+        if (dir->type & EXFAT_TYPE_USED) {
           pr->print(F("Unknown dirType: 0x"));
           pr->print(F("Unknown dirType: 0x"));
         } else {
         } else {
           pr->print(F("Unused dirType: 0x"));
           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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * MIT License
@@ -41,6 +41,29 @@ inline bool lfnLegalChar(uint8_t c) {
 #endif  // USE_UTF8_LONG_NAMES
 #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) {
 uint8_t* ExFatFile::dirCache(uint8_t set, uint8_t options) {
   DirPos_t pos = m_dirPos;
   DirPos_t pos = m_dirPos;
   if (m_vol->dirSeek(&pos, FS_DIR_SIZE*set) != 1) {
   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;
   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) {
 bool ExFatFile::open(ExFatFile* dirFile, uint32_t index, oflag_t oflag) {
   if (dirFile->seekSet(FS_DIR_SIZE*index) && openNext(dirFile, oflag)) {
   if (dirFile->seekSet(FS_DIR_SIZE*index) && openNext(dirFile, oflag)) {
     if (dirIndex() == index) {
     if (dirIndex() == index) {
@@ -223,6 +251,19 @@ bool ExFatFile::open(ExFatFile* dirFile, uint32_t index, oflag_t oflag) {
   return false;
   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) {
 bool ExFatFile::openNext(ExFatFile* dir, oflag_t oflag) {
   if (isOpen() || !dir->isDir() || (dir->curPosition() & 0X1F)) {
   if (isOpen() || !dir->isDir() || (dir->curPosition() & 0X1F)) {
     DBG_FAIL_MACRO;
     DBG_FAIL_MACRO;
@@ -285,7 +326,7 @@ bool ExFatFile::openPrivate(ExFatFile* dir, ExName_t* fname, oflag_t oflag) {
       DBG_FAIL_MACRO;
       DBG_FAIL_MACRO;
       goto fail;
       goto fail;
     }
     }
-    if (!(buf[0] & 0x80)) {
+    if (!(buf[0] & EXFAT_TYPE_USED)) {
       // Unused entry.
       // Unused entry.
       if (freeCount == 0) {
       if (freeCount == 0) {
         freePos.position = dir->curPosition() - FS_DIR_SIZE;
         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) {
       if (freeCount < freeNeed) {
         freeCount++;
         freeCount++;
       }
       }
-      if (!buf[0]) {
+      if (buf[0] == EXFAT_TYPE_END_DIR) {
         if (fname) {
         if (fname) {
           goto create;
           goto create;
         }
         }
@@ -314,8 +355,8 @@ bool ExFatFile::openPrivate(ExFatFile* dir, ExName_t* fname, oflag_t oflag) {
       memset(this, 0, sizeof(ExFatFile));
       memset(this, 0, sizeof(ExFatFile));
       dirFile = reinterpret_cast<DirFile_t*>(buf);
       dirFile = reinterpret_cast<DirFile_t*>(buf);
       m_setCount = dirFile->setCount;
       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_attributes |= FILE_ATTR_FILE;
       }
       }
       m_vol = dir->volume();
       m_vol = dir->volume();
@@ -381,6 +422,9 @@ bool ExFatFile::openPrivate(ExFatFile* dir, ExName_t* fname, oflag_t oflag) {
     DBG_FAIL_MACRO;
     DBG_FAIL_MACRO;
     goto fail;
     goto fail;
   }
   }
+  if (isWritable()) {
+    m_attributes |= FS_ATTRIB_ARCHIVE;
+  }
 #endif  // !EXFAT_READ_ONLY
 #endif  // !EXFAT_READ_ONLY
   return true;
   return true;
 
 
@@ -418,7 +462,7 @@ bool ExFatFile::openPrivate(ExFatFile* dir, ExName_t* fname, oflag_t oflag) {
   freePos.isContiguous = dir->isContiguous();
   freePos.isContiguous = dir->isContiguous();
   memset(this, 0, sizeof(ExFatFile));
   memset(this, 0, sizeof(ExFatFile));
   m_vol = dir->volume();
   m_vol = dir->volume();
-  m_attributes = FILE_ATTR_FILE;
+  m_attributes = FILE_ATTR_FILE | FS_ATTRIB_ARCHIVE;
   m_dirPos = freePos;
   m_dirPos = freePos;
   fname->reset();
   fname->reset();
   for (uint8_t i = 0; i < freeNeed; i++) {
   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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * MIT License
@@ -87,6 +87,21 @@ class ExFatFile {
   operator bool() {
   operator bool() {
     return isOpen();
     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
   /** \return The number of bytes available from the current position
    * to EOF for normal files.  INT_MAX is returned for very large files.
    * 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.
    * \return true for success or false for failure.
    */
    */
   bool contiguousRange(uint32_t* bgnSector, uint32_t* endSector);
   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. */
   /** \return The current position for a file or directory. */
   uint64_t curPosition() const {return m_curPosition;}
   uint64_t curPosition() const {return m_curPosition;}
-
   /** \return Total data length for file. */
   /** \return Total data length for file. */
   uint64_t dataLength() const {return m_dataLength;}
   uint64_t dataLength() const {return m_dataLength;}
   /** \return Directory entry index. */
   /** \return Directory entry index. */
@@ -261,18 +277,22 @@ class ExFatFile {
   bool isDir() const  {return m_attributes & FILE_ATTR_DIR;}
   bool isDir() const  {return m_attributes & FILE_ATTR_DIR;}
   /** \return True if this is a normal file. */
   /** \return True if this is a normal file. */
   bool isFile() const {return m_attributes & FILE_ATTR_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. */
   /** \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. */
   /** \return true if the file is open. */
   bool isOpen() const {return m_attributes;}
   bool isOpen() const {return m_attributes;}
   /** \return True if file is read-only */
   /** \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. */
   /** \return True if this is the root directory. */
   bool isRoot() const {return m_attributes & FILE_ATTR_ROOT;}
   bool isRoot() const {return m_attributes & FILE_ATTR_ROOT;}
   /** \return True file is readable. */
   /** \return True file is readable. */
   bool isReadable() const {return m_flags & FILE_FLAG_READ;}
   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;}
   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. */
   /** \return True file is writable. */
   bool isWritable() const {return m_flags & FILE_FLAG_WRITE;}
   bool isWritable() const {return m_flags & FILE_FLAG_WRITE;}
   /** List directory contents.
   /** List directory contents.
@@ -291,10 +311,10 @@ class ExFatFile {
    *
    *
    * LS_SIZE - %Print file size.
    * 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
    * \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.
    * \return true for success or false for failure.
    */
    */
@@ -355,7 +375,7 @@ class ExFatFile {
    *
    *
    * \return true for success or false for failure.
    * \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.
   /** Open a file in the volume working directory.
    *
    *
    * \param[in] vol Volume where the file is located.
    * \param[in] vol Volume where the file is located.
@@ -367,7 +387,7 @@ class ExFatFile {
    *
    *
    * \return true for success or false for failure.
    * \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.
   /** Open a file by index.
    *
    *
    * \param[in] dirFile An open ExFatFile instance for the directory.
    * \param[in] dirFile An open ExFatFile instance for the directory.
@@ -381,7 +401,19 @@ class ExFatFile {
    * See open() by path for definition of flags.
    * See open() by path for definition of flags.
    * \return true for success or false for failure.
    * \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.
   /** Open a file in the current working directory.
    *
    *
    * \param[in] path A path with a valid name for a file to be opened.
    * \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.
    * \return true for success or false for failure.
    */
    */
   bool open(const char* path, oflag_t oflag = O_RDONLY);
   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.
   /** Open the next file or subdirectory in a directory.
    *
    *
    * \param[in] dirFile An open instance for the 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 openPrivate(ExFatFile* dir, ExName_t* fname, oflag_t oflag);
   bool parsePathName(const char* path,
   bool parsePathName(const char* path,
                             ExName_t* fname, const char** ptr);
                             ExName_t* fname, const char** ptr);
-  uint32_t curCluster() const {return m_curCluster;}
   ExFatVolume* volume() const {return m_vol;}
   ExFatVolume* volume() const {return m_vol;}
   bool syncDir();
   bool syncDir();
   //----------------------------------------------------------------------------
   //----------------------------------------------------------------------------
@@ -778,25 +814,14 @@ class ExFatFile {
 
 
   /** This file has not been opened. */
   /** This file has not been opened. */
   static const uint8_t FILE_ATTR_CLOSED = 0;
   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 */
   /** Entry for normal data file */
   static const uint8_t FILE_ATTR_FILE = 0X08;
   static const uint8_t FILE_ATTR_FILE = 0X08;
   /** Entry is for a subdirectory */
   /** 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 */
   /** Root directory */
   static const uint8_t FILE_ATTR_ROOT = 0X40;
   static const uint8_t FILE_ATTR_ROOT = 0X40;
   /** Directory type bits */
   /** Directory type bits */
   static const uint8_t FILE_ATTR_DIR = FILE_ATTR_SUBDIR | FILE_ATTR_ROOT;
   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_READ = 0X01;
   static const uint8_t FILE_FLAG_WRITE = 0X02;
   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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * MIT License
@@ -204,7 +204,7 @@ bool ExFatFile::mkdir(ExFatFile* parent, ExName_t* fname) {
     goto fail;
     goto fail;
   }
   }
   // convert file to directory
   // convert file to directory
-  m_attributes = FILE_ATTR_SUBDIR;
+  m_attributes = FILE_ATTR_SUBDIR | FS_ATTRIB_ARCHIVE;
 
 
   // allocate and zero first cluster
   // allocate and zero first cluster
   if (!addDirCluster()) {
   if (!addDirCluster()) {
@@ -402,7 +402,7 @@ bool ExFatFile::syncDir() {
     switch (cache[0]) {
     switch (cache[0]) {
       case EXFAT_TYPE_FILE:
       case EXFAT_TYPE_FILE:
         df = reinterpret_cast<DirFile_t*>(cache);
         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) {
         if (FsDateTime::callback) {
           uint16_t date, time;
           uint16_t date, time;
           uint8_t ms10;
           uint8_t ms10;
@@ -498,7 +498,7 @@ bool ExFatFile::timestamp(uint8_t flags, uint16_t year, uint8_t month,
     switch (cache[0]) {
     switch (cache[0]) {
       case EXFAT_TYPE_FILE:
       case EXFAT_TYPE_FILE:
         df = reinterpret_cast<DirFile_t*>(cache);
         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();
         m_vol->dataCacheDirty();
         if (flags & T_ACCESS) {
         if (flags & T_ACCESS) {
           setLe16(df->accessTime, time);
           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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * MIT License
@@ -237,7 +237,7 @@ bool ExFatPartition::freeChain(uint32_t cluster) {
   return false;
   return false;
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
-uint32_t ExFatPartition::freeClusterCount() {
+int32_t ExFatPartition::freeClusterCount() {
   uint32_t nc = 0;
   uint32_t nc = 0;
   uint32_t sector = m_clusterHeapStartSector;
   uint32_t sector = m_clusterHeapStartSector;
   uint32_t usedCount = 0;
   uint32_t usedCount = 0;
@@ -246,7 +246,7 @@ uint32_t ExFatPartition::freeClusterCount() {
   while (true) {
   while (true) {
     cache = dataCachePrepare(sector++, FsCache::CACHE_FOR_READ);
     cache = dataCachePrepare(sector++, FsCache::CACHE_FOR_READ);
     if (!cache) {
     if (!cache) {
-      return 0;
+      return -1;
     }
     }
     for (size_t i = 0; i < m_bytesPerSector; i++) {
     for (size_t i = 0; i < m_bytesPerSector; i++) {
       if (cache[i] == 0XFF) {
       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;
   pbs_t* pbs;
   BpbExFat_t* bpb;
   BpbExFat_t* bpb;
   MbrSector_t* mbr;
   MbrSector_t* mbr;
-  MbrPart_t* mp;
-
   m_fatType = 0;
   m_fatType = 0;
   m_blockDev = dev;
   m_blockDev = dev;
   cacheInit(m_blockDev);
   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;
       DBG_FAIL_MACRO;
       goto fail;
       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;
       DBG_FAIL_MACRO;
       goto fail;
       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)) {
   if (strncmp(pbs->oemName, "EXFAT", 5)) {
     DBG_FAIL_MACRO;
     DBG_FAIL_MACRO;
     goto fail;
     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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * MIT License
@@ -95,18 +95,19 @@ class ExFatPartition {
   uint32_t fatStartSector() const {return m_fatStartSector;}
   uint32_t fatStartSector() const {return m_fatStartSector;}
   /** \return Type FAT_TYPE_EXFAT for exFAT partition or zero for error. */
   /** \return Type FAT_TYPE_EXFAT for exFAT partition or zero for error. */
   uint8_t fatType() const {return m_fatType;}
   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.
   /** Initialize a exFAT partition.
    * \param[in] dev The blockDevice for the partition.
    * \param[in] dev The blockDevice for the partition.
    * \param[in] part The partition to be used.  Legal values for \a part are
    * \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
    * 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 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.
    * \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.
    * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * MIT License
@@ -33,15 +33,36 @@
 class ExFatVolume : public ExFatPartition {
 class ExFatVolume : public ExFatPartition {
  public:
  public:
   ExFatVolume() {}
   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.
    * Initialize an FatVolume object.
    * \param[in] dev Device block driver.
    * \param[in] dev Device block driver.
    * \param[in] setCwv Set current working volume if true.
    * \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.
    * \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;
       return false;
     }
     }
     if (!chdir()) {
     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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * MIT License
@@ -98,7 +98,7 @@ static bool printFatDir(print_t* pr, DirFat_t* dir) {
     return false;
     return false;
   } else if (dir->name[0] == FAT_NAME_DELETED) {
   } else if (dir->name[0] == FAT_NAME_DELETED) {
     pr->println(F("Deleted"));
     pr->println(F("Deleted"));
-  } else if (isFileOrSubdir(dir)) {
+  } else if (isFatFileOrSubdir(dir)) {
     pr->print(F("SFN: "));
     pr->print(F("SFN: "));
     for (uint8_t i = 0; i < 11; i++) {
     for (uint8_t i = 0; i < 11; i++) {
       printHex(pr, dir->name[i]);
       printHex(pr, dir->name[i]);
@@ -117,7 +117,7 @@ static bool printFatDir(print_t* pr, DirFat_t* dir) {
     pr->println(fc, HEX);
     pr->println(fc, HEX);
     pr->print(F("fileSize: "));
     pr->print(F("fileSize: "));
     pr->println(getLe32(dir->fileSize));
     pr->println(getLe32(dir->fileSize));
-  } else if (isLongName(dir)) {
+  } else if (isFatLongName(dir)) {
     pr->print(F("LFN: "));
     pr->print(F("LFN: "));
     for (uint8_t i = 0; i < 13; i++) {
     for (uint8_t i = 0; i < 13; i++) {
       uint16_t c = getLfnChar(ldir, 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * MIT License
@@ -86,6 +86,29 @@ bool FatFile::addDirCluster() {
   return false;
   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
 // cache a file's directory entry
 // return pointer to cached entry or null for failure
 // return pointer to cached entry or null for failure
 DirFat_t* FatFile::cacheDirEntry(uint8_t action) {
 DirFat_t* FatFile::cacheDirEntry(uint8_t action) {
@@ -382,7 +405,7 @@ bool FatFile::mkdir(FatFile* parent, FatName_t* fname) {
     goto fail;
     goto fail;
   }
   }
   // change directory entry attribute
   // change directory entry attribute
-  dir->attributes = FAT_ATTRIB_DIRECTORY;
+  dir->attributes = FS_ATTRIB_DIRECTORY;
 
 
   // make entry for '.'
   // make entry for '.'
   memcpy(&dot, dir, sizeof(dot));
   memcpy(&dot, dir, sizeof(dot));
@@ -466,6 +489,11 @@ bool FatFile::open(FatFile* dirFile, const char* path, oflag_t oflag) {
   return false;
   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) {
 bool FatFile::open(FatFile* dirFile, uint16_t index, oflag_t oflag) {
   if (index) {
   if (index) {
     // Find start of LFN.
     // Find start of LFN.
@@ -519,12 +547,12 @@ bool FatFile::openCachedEntry(FatFile* dirFile, uint16_t dirIndex,
   dir += 0XF & dirIndex;
   dir += 0XF & dirIndex;
 
 
   // Must be file or subdirectory.
   // Must be file or subdirectory.
-  if (!isFileOrSubdir(dir)) {
+  if (!isFatFileOrSubdir(dir)) {
     DBG_FAIL_MACRO;
     DBG_FAIL_MACRO;
     goto fail;
     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_attributes |= FILE_ATTR_FILE;
   }
   }
   m_lfnOrd = lfnOrd;
   m_lfnOrd = lfnOrd;
@@ -556,6 +584,7 @@ bool FatFile::openCachedEntry(FatFile* dirFile, uint16_t dirIndex,
       DBG_FAIL_MACRO;
       DBG_FAIL_MACRO;
       goto fail;
       goto fail;
     }
     }
+    m_attributes |= FS_ATTRIB_ARCHIVE;
   }
   }
   m_flags |= (oflag & O_APPEND ? FILE_FLAG_APPEND : 0);
   m_flags |= (oflag & O_APPEND ? FILE_FLAG_APPEND : 0);
 
 
@@ -570,7 +599,6 @@ bool FatFile::openCachedEntry(FatFile* dirFile, uint16_t dirIndex,
       DBG_FAIL_MACRO;
       DBG_FAIL_MACRO;
       goto fail;
       goto fail;
     }
     }
-
     // need to update directory entry
     // need to update directory entry
     m_flags |= FILE_FLAG_DIR_DIRTY;
     m_flags |= FILE_FLAG_DIR_DIRTY;
   } else {
   } else {
@@ -601,6 +629,19 @@ bool FatFile::openCluster(FatFile* file) {
   return true;
   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) {
 bool FatFile::openNext(FatFile* dirFile, oflag_t oflag) {
   uint8_t checksum = 0;
   uint8_t checksum = 0;
   DirLfn_t* ldir;
   DirLfn_t* ldir;
@@ -629,7 +670,7 @@ bool FatFile::openNext(FatFile* dirFile, oflag_t oflag) {
     // skip empty slot or '.' or '..'
     // skip empty slot or '.' or '..'
     if (dir->name[0] == '.' || dir->name[0] == FAT_NAME_DELETED) {
     if (dir->name[0] == '.' || dir->name[0] == FAT_NAME_DELETED) {
       lfnOrd = 0;
       lfnOrd = 0;
-    } else if (isFileOrSubdir(dir)) {
+    } else if (isFatFileOrSubdir(dir)) {
       if (lfnOrd && checksum != lfnChecksum(dir->name)) {
       if (lfnOrd && checksum != lfnChecksum(dir->name)) {
         DBG_FAIL_MACRO;
         DBG_FAIL_MACRO;
         goto fail;
         goto fail;
@@ -639,7 +680,7 @@ bool FatFile::openNext(FatFile* dirFile, oflag_t oflag) {
         goto fail;
         goto fail;
       }
       }
       return true;
       return true;
-    } else if (isLongName(dir)) {
+    } else if (isFatLongName(dir)) {
       ldir = reinterpret_cast<DirLfn_t*>(dir);
       ldir = reinterpret_cast<DirLfn_t*>(dir);
       if (ldir->order & FAT_ORDER_LAST_LONG_ENTRY) {
       if (ldir->order & FAT_ORDER_LAST_LONG_ENTRY) {
         lfnOrd = ldir->order & 0X1F;
         lfnOrd = ldir->order & 0X1F;
@@ -855,7 +896,7 @@ int8_t FatFile::readDir(DirFat_t* dir) {
       continue;
       continue;
     }
     }
     // return if normal file or subdirectory
     // return if normal file or subdirectory
-    if (isFileOrSubdir(dir)) {
+    if (isFatFileOrSubdir(dir)) {
       return n;
       return n;
     }
     }
   }
   }
@@ -1040,7 +1081,7 @@ bool FatFile::rmdir() {
       continue;
       continue;
     }
     }
     // error not empty
     // error not empty
-    if (isFileOrSubdir(dir)) {
+    if (isFatFileOrSubdir(dir)) {
       DBG_FAIL_MACRO;
       DBG_FAIL_MACRO;
       goto fail;
       goto fail;
     }
     }
@@ -1086,7 +1127,7 @@ bool FatFile::rmRfStar() {
     }
     }
 
 
     // skip if part of long file name or volume label in root
     // skip if part of long file name or volume label in root
-    if (!isFileOrSubdir(dir)) {
+    if (!isFatFileOrSubdir(dir)) {
       continue;
       continue;
     }
     }
 
 
@@ -1207,6 +1248,7 @@ bool FatFile::sync() {
       DBG_FAIL_MACRO;
       DBG_FAIL_MACRO;
       goto fail;
       goto fail;
     }
     }
+    dir->attributes = m_attributes & FS_ATTRIB_COPY;
     // do not set filesize for dir files
     // do not set filesize for dir files
     if (isFile()) {
     if (isFile()) {
       setLe32(dir->fileSize, m_fileSize);
       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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * MIT License
@@ -156,6 +156,21 @@ class FatFile {
    * \return true if a file is open.
    * \return true if a file is open.
    */
    */
   operator bool() const {return isOpen();}
   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
   /** \return The number of bytes available from the current position
    * to EOF for normal files.  INT_MAX is returned for very large files.
    * 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;}
   bool isDir() const {return m_attributes & FILE_ATTR_DIR;}
   /** \return True if this is a normal file. */
   /** \return True if this is a normal file. */
   bool isFile() const {return m_attributes & FILE_ATTR_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. */
   /** \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. */
   /** \return true if this file has a Long File Name. */
   bool isLFN() const {return m_lfnOrd;}
   bool isLFN() const {return m_lfnOrd;}
   /** \return True if this is an open file/directory. */
   /** \return True if this is an open file/directory. */
@@ -399,17 +416,17 @@ class FatFile {
   /** \return True file is readable. */
   /** \return True file is readable. */
   bool isReadable() const {return m_flags & FILE_FLAG_READ;}
   bool isReadable() const {return m_flags & FILE_FLAG_READ;}
   /** \return True if file is read-only */
   /** \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. */
   /** \return True if this is the root directory. */
   bool isRoot() const {return m_attributes & FILE_ATTR_ROOT;}
   bool isRoot() const {return m_attributes & FILE_ATTR_ROOT;}
   /** \return True if this is the FAT32 root directory. */
   /** \return True if this is the FAT32 root directory. */
   bool isRoot32() const {return m_attributes & FILE_ATTR_ROOT32;}
   bool isRoot32() const {return m_attributes & FILE_ATTR_ROOT32;}
   /** \return True if this is the FAT12 of FAT16 root directory. */
   /** \return True if this is the FAT12 of FAT16 root directory. */
   bool isRootFixed() const {return m_attributes & FILE_ATTR_ROOT_FIXED;}
   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;}
   bool isSubDir() const {return m_attributes & FILE_ATTR_SUBDIR;}
   /** \return True if this is a system file. */
   /** \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. */
   /** \return True file is writable. */
   bool isWritable() const {return m_flags & FILE_FLAG_WRITE;}
   bool isWritable() const {return m_flags & FILE_FLAG_WRITE;}
   /** List directory contents.
   /** List directory contents.
@@ -453,7 +470,7 @@ class FatFile {
    *
    *
    * \return true for success or false for failure.
    * \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.
   /** Open a file by index.
    *
    *
    * \param[in] dirFile An open FatFile instance for the directory.
    * \param[in] dirFile An open FatFile instance for the directory.
@@ -467,7 +484,19 @@ class FatFile {
    * See open() by path for definition of flags.
    * See open() by path for definition of flags.
    * \return true for success or false for failure.
    * \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.
   /** Open a file or directory by name.
    *
    *
    * \param[in] dirFile An open FatFile instance for the directory containing
    * \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.
    * \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.
   /** Open a file in the current working volume.
    *
    *
    * \param[in] path A path with a valid name for a file to be opened.
    * \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.
    * \return true for success or false for failure.
    */
    */
   bool open(const char* path, oflag_t oflag = O_RDONLY);
   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.
   /** Open existing file wih Short 8.3 names.
    * \param[in] path with short 8.3 names.
    * \param[in] path with short 8.3 names.
    *
    *
@@ -957,29 +991,19 @@ class FatFile {
 
 
   /** This file has not been opened. */
   /** This file has not been opened. */
   static const uint8_t FILE_ATTR_CLOSED = 0;
   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 */
   /** Entry for normal data file */
   static const uint8_t FILE_ATTR_FILE = 0X08;
   static const uint8_t FILE_ATTR_FILE = 0X08;
   /** Entry is for a subdirectory */
   /** 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 */
   /** 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 */
   /** A FAT32 root directory */
-  static const uint8_t FILE_ATTR_ROOT32 = 0X40;
+  static const uint8_t FILE_ATTR_ROOT32 = 0X80;
   /** Entry is for root. */
   /** Entry is for root. */
   static const uint8_t FILE_ATTR_ROOT =
   static const uint8_t FILE_ATTR_ROOT =
                        FILE_ATTR_ROOT_FIXED | FILE_ATTR_ROOT32;
                        FILE_ATTR_ROOT_FIXED | FILE_ATTR_ROOT32;
   /** Directory type bits */
   /** Directory type bits */
   static const uint8_t FILE_ATTR_DIR = FILE_ATTR_SUBDIR | FILE_ATTR_ROOT;
   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
   // 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * MIT License
@@ -268,7 +268,7 @@ bool FatFile::makeUniqueSfn(FatLfn_t* fname) {
       if (dir->name[0] == FAT_NAME_FREE) {
       if (dir->name[0] == FAT_NAME_FREE) {
         goto done;
         goto done;
       }
       }
-      if (isFileOrSubdir(dir) && !memcmp(fname->sfn, dir->name, 11)) {
+      if (isFatFileOrSubdir(dir) && !memcmp(fname->sfn, dir->name, 11)) {
         // Name found - try another.
         // Name found - try another.
         break;
         break;
       }
       }
@@ -338,7 +338,7 @@ bool FatFile::open(FatFile* dirFile, FatLfn_t* fname, oflag_t oflag) {
     // skip empty slot or '.' or '..'
     // skip empty slot or '.' or '..'
     if (dir->name[0] == FAT_NAME_DELETED || dir->name[0] == '.') {
     if (dir->name[0] == FAT_NAME_DELETED || dir->name[0] == '.') {
       lfnOrd = 0;
       lfnOrd = 0;
-    } else if (isLongName(dir)) {
+    } else if (isFatLongName(dir)) {
       ldir = reinterpret_cast<DirLfn_t*>(dir);
       ldir = reinterpret_cast<DirLfn_t*>(dir);
       if (!lfnOrd) {
       if (!lfnOrd) {
         order = ldir->order & 0X1F;
         order = ldir->order & 0X1F;
@@ -357,7 +357,7 @@ bool FatFile::open(FatFile* dirFile, FatLfn_t* fname, oflag_t oflag) {
           lfnOrd = 0;
           lfnOrd = 0;
         }
         }
       }
       }
-    } else if (isFileOrSubdir(dir)) {
+    } else if (isFatFileOrSubdir(dir)) {
       if (lfnOrd) {
       if (lfnOrd) {
         if (1 == order && lfnChecksum(dir->name) == checksum) {
         if (1 == order && lfnChecksum(dir->name) == checksum) {
           goto found;
           goto found;
@@ -476,6 +476,7 @@ bool FatFile::parsePathName(const char* path,
     path++;
     path++;
   }
   }
   fname->begin = path;
   fname->begin = path;
+  fname->len = 0;
   while (*path && !isDirSeparator(*path)) {
   while (*path && !isDirSeparator(*path)) {
 #if USE_UTF8_LONG_NAMES
 #if USE_UTF8_LONG_NAMES
     uint32_t cp;
     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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * MIT License
@@ -62,7 +62,7 @@ bool FatFile::open(FatFile* dirFile, FatSfn_t* fname, oflag_t oflag) {
         break;
         break;
       }
       }
       lfnOrd = 0;
       lfnOrd = 0;
-    } else if (isFileOrSubdir(dir)) {
+    } else if (isFatFileOrSubdir(dir)) {
       if (!memcmp(fname->sfn, dir->name, 11)) {
       if (!memcmp(fname->sfn, dir->name, 11)) {
         // don't open existing file if O_EXCL
         // don't open existing file if O_EXCL
         if (oflag & O_EXCL) {
         if (oflag & O_EXCL) {
@@ -83,7 +83,7 @@ bool FatFile::open(FatFile* dirFile, FatSfn_t* fname, oflag_t oflag) {
       } else {
       } else {
         lfnOrd = 0;
         lfnOrd = 0;
       }
       }
-    } else if (isLongName(dir)) {
+    } else if (isFatLongName(dir)) {
       ldir = reinterpret_cast<DirLfn_t*>(dir);
       ldir = reinterpret_cast<DirLfn_t*>(dir);
       if (ldir->order & FAT_ORDER_LAST_LONG_ENTRY) {
       if (ldir->order & FAT_ORDER_LAST_LONG_ENTRY) {
         lfnOrd = ldir->order & 0X1F;
         lfnOrd = ldir->order & 0X1F;
@@ -191,25 +191,29 @@ bool FatFile::openSFN(FatSfn_t* fname) {
       DBG_FAIL_MACRO;
       DBG_FAIL_MACRO;
       goto fail;
       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;
       uint16_t dirIndex = (m_curPosition - 32) >> 5;
       uint32_t dirCluster = m_firstCluster;
       uint32_t dirCluster = m_firstCluster;
       memset(this, 0 , sizeof(FatFile));
       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;
         m_attributes |= FILE_ATTR_FILE;
+        if (!isReadOnly()) {
+          m_attributes |= FS_ATTRIB_ARCHIVE;
+          m_flags |= FILE_FLAG_WRITE;
+        }
       }
       }
       m_lfnOrd = lfnOrd;
       m_lfnOrd = lfnOrd;
       m_firstCluster = (uint32_t)getLe16(dir.firstClusterHigh) << 16;
       m_firstCluster = (uint32_t)getLe16(dir.firstClusterHigh) << 16;
       m_firstCluster |= getLe16(dir.firstClusterLow);
       m_firstCluster |= getLe16(dir.firstClusterLow);
       m_fileSize = getLe32(dir.fileSize);
       m_fileSize = getLe32(dir.fileSize);
-      m_flags = isFile() ? FILE_FLAG_READ | FILE_FLAG_WRITE : FILE_FLAG_READ;
       m_vol = vol;
       m_vol = vol;
       m_dirCluster = dirCluster;
       m_dirCluster = dirCluster;
       m_dirSector = m_vol->cacheSectorNumber();
       m_dirSector = m_vol->cacheSectorNumber();
       m_dirIndex = dirIndex;
       m_dirIndex = dirIndex;
       return true;
       return true;
-    } else if (isLongName(&dir)) {
+    } else if (isFatLongName(&dir)) {
       ldir = reinterpret_cast<DirLfn_t*>(&dir);
       ldir = reinterpret_cast<DirLfn_t*>(&dir);
       if (ldir->order & FAT_ORDER_LAST_LONG_ENTRY) {
       if (ldir->order & FAT_ORDER_LAST_LONG_ENTRY) {
         lfnOrd = ldir->order & 0X1F;
         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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * MIT License
@@ -390,10 +390,9 @@ int32_t FatPartition::freeClusterCount() {
   return -1;
   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 clusterCount;
   uint32_t totalSectors;
   uint32_t totalSectors;
-  uint32_t volumeStartSector = 0;
   m_blockDev = dev;
   m_blockDev = dev;
   pbs_t* pbs;
   pbs_t* pbs;
   BpbFat32_t* bpb;
   BpbFat32_t* bpb;
@@ -414,19 +413,25 @@ bool FatPartition::init(FsBlockDevice* dev, uint8_t part) {
     }
     }
     mbr = reinterpret_cast<MbrSector_t*>
     mbr = reinterpret_cast<MbrSector_t*>
           (dataCachePrepare(0, FsCache::CACHE_FOR_READ));
           (dataCachePrepare(0, FsCache::CACHE_FOR_READ));
+    if (!mbr) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
     MbrPart_t* mp = mbr->part + part - 1;
     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;
       DBG_FAIL_MACRO;
       goto fail;
       goto fail;
     }
     }
-    volumeStartSector = getLe32(mp->relativeSectors);
+    volStart = getLe32(mp->relativeSectors);
   }
   }
   pbs = reinterpret_cast<pbs_t*>
   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);
   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;
     DBG_FAIL_MACRO;
     goto fail;
     goto fail;
   }
   }
@@ -445,7 +450,7 @@ bool FatPartition::init(FsBlockDevice* dev, uint8_t part) {
   if (m_sectorsPerFat == 0) {
   if (m_sectorsPerFat == 0) {
     m_sectorsPerFat = getLe32(bpb->sectorsPerFat32);
     m_sectorsPerFat = getLe32(bpb->sectorsPerFat32);
   }
   }
-  m_fatStartSector = volumeStartSector + getLe16(bpb->reservedSectorCount);
+  m_fatStartSector = volStart + getLe16(bpb->reservedSectorCount);
 
 
   // count for FAT16 zero for FAT32
   // count for FAT16 zero for FAT32
   m_rootDirEntryCount = getLe16(bpb->rootDirEntryCount);
   m_rootDirEntryCount = getLe16(bpb->rootDirEntryCount);
@@ -462,7 +467,7 @@ bool FatPartition::init(FsBlockDevice* dev, uint8_t part) {
     totalSectors = getLe32(bpb->totalSectors32);
     totalSectors = getLe32(bpb->totalSectors32);
   }
   }
   // total data sectors
   // total data sectors
-  clusterCount = totalSectors - (m_dataStartSector - volumeStartSector);
+  clusterCount = totalSectors - (m_dataStartSector - volStart);
 
 
   // divide by cluster size to get cluster count
   // divide by cluster size to get cluster count
   clusterCount >>= m_sectorsPerClusterShift;
   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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * MIT License
@@ -126,10 +126,7 @@ class FatPartition {
   uint8_t fatType() const {
   uint8_t fatType() const {
     return m_fatType;
     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();
   int32_t freeClusterCount();
   /** Initialize a FAT partition.
   /** Initialize a FAT partition.
    *
    *
@@ -137,11 +134,12 @@ class FatPartition {
    * \param[in] part The partition to be used.  Legal values for \a part are
    * \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
    * 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 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.
    * \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. */
   /** \return The number of entries in the root directory for FAT16 volumes. */
   uint16_t rootDirEntryCount() const {
   uint16_t rootDirEntryCount() const {
     return m_rootDirEntryCount;
     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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * MIT License
@@ -36,15 +36,36 @@
  */
  */
 class FatVolume : public  FatPartition {
 class FatVolume : public  FatPartition {
  public:
  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.
    * Initialize an FatVolume object.
    * \param[in] dev Device block driver.
    * \param[in] dev Device block driver.
    * \param[in] setCwv Set current working volume if true.
    * \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.
    * \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;
       return false;
     }
     }
     if (!chdir()) {
     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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * MIT License
@@ -37,7 +37,7 @@ FsBaseFile::FsBaseFile(const FsBaseFile& from) {
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 FsBaseFile& FsBaseFile::operator=(const FsBaseFile& from) {
 FsBaseFile& FsBaseFile::operator=(const FsBaseFile& from) {
-  if (this == &from) return *this;
+  if (this == &from) {return *this;}
   close();
   close();
   if (from.m_fFile) {
   if (from.m_fFile) {
     m_fFile = new (m_fileMem) FatFile;
     m_fFile = new (m_fileMem) FatFile;
@@ -50,15 +50,10 @@ FsBaseFile& FsBaseFile::operator=(const FsBaseFile& from) {
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 bool FsBaseFile::close() {
 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) {
 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;
   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) {
 bool FsBaseFile::openNext(FsBaseFile* dir, oflag_t oflag) {
   close();
   close();
   if (dir->m_fFile) {
   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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * MIT License
@@ -66,6 +66,25 @@ class FsBaseFile {
     * \return true if a file is open.
     * \return true if a file is open.
     */
     */
   operator bool() const {return isOpen();}
   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
   /** \return number of bytes available from the current position to EOF
    *   or INT_MAX if more than INT_MAX bytes are available.
    *   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) :
     return m_fFile ? m_fFile->contiguousRange(bgnSector, endSector) :
            m_xFile ? m_xFile->contiguousRange(bgnSector, endSector) : false;
            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. */
   /** \return The current position for a file or directory. */
   uint64_t curPosition() const {
   uint64_t curPosition() const {
     return m_fFile ? m_fFile->curPosition() :
     return m_fFile ? m_fFile->curPosition() :
@@ -270,6 +294,11 @@ class FsBaseFile {
     return m_fFile ? m_fFile->isFile() :
     return m_fFile ? m_fFile->isFile() :
            m_xFile ? m_xFile->isFile() : false;
            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. */
   /** \return True if this is a hidden file else false. */
   bool isHidden() const {
   bool isHidden() const {
     return m_fFile ? m_fFile->isHidden() :
     return m_fFile ? m_fFile->isHidden() :
@@ -287,7 +316,7 @@ class FsBaseFile {
     return m_fFile ? m_fFile->isReadOnly() :
     return m_fFile ? m_fFile->isReadOnly() :
            m_xFile ? m_xFile->isReadOnly() : false;
            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 {
   bool isSubDir() const {
     return m_fFile ? m_fFile->isSubDir() :
     return m_fFile ? m_fFile->isSubDir() :
            m_xFile ? m_xFile->isSubDir() : false;
            m_xFile ? m_xFile->isSubDir() : false;
@@ -410,7 +439,7 @@ class FsBaseFile {
    * See open() by path for definition of flags.
    * See open() by path for definition of flags.
    * \return true for success or false for failure.
    * \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.
   /** Open a file or directory by name.
    *
    *
    * \param[in] vol Volume where the file is located.
    * \param[in] vol Volume where the file is located.
@@ -422,7 +451,7 @@ class FsBaseFile {
    *
    *
    * \return true for success or false for failure.
    * \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.
   /** Open a file or directory by name.
    *
    *
    * \param[in] path A path for a file to be opened.
    * \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) {
   bool open(const char* path, oflag_t oflag = O_RDONLY) {
     return FsVolume::m_cwv && open(FsVolume::m_cwv, path, oflag);
     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.
   /** Opens the next file or folder in a directory.
    * \param[in] dir directory containing files.
    * \param[in] dir directory containing files.
    * \param[in] oflag open flags.
    * \param[in] oflag open flags.
@@ -620,14 +668,15 @@ class FsBaseFile {
   }
   }
   /** Rename a file or subdirectory.
   /** 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.
    * \param[in] newPath New path name for the file/directory.
    *
    *
    * \return true for success or false for failure.
    * \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. */
   /** Set the file's current position to zero. */
   void rewind() {
   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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * MIT License
@@ -25,19 +25,19 @@
 #include "FsLib.h"
 #include "FsLib.h"
 FsVolume* FsVolume::m_cwv = nullptr;
 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_blockDev = blockDev;
   m_fVol = nullptr;
   m_fVol = nullptr;
   m_xVol = new (m_volMem) ExFatVolume;
   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;
     goto done;
   }
   }
   m_xVol = nullptr;
   m_xVol = nullptr;
   m_fVol = new (m_volMem) FatVolume;
   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;
     goto done;
   }
   }
-  m_cwv = nullptr;
   m_fVol = nullptr;
   m_fVol = nullptr;
   return false;
   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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * MIT License
@@ -42,15 +42,35 @@ class FsVolume {
   FsVolume() {}
   FsVolume() {}
 
 
   ~FsVolume() {end();}
   ~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.
    * Initialize an FatVolume object.
    * \param[in] blockDev Device block driver.
    * \param[in] blockDev Device block driver.
    * \param[in] setCwv Set current working volume if true.
    * \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.
    * \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
 #ifndef DOXYGEN_SHOULD_SKIP_THIS
   uint32_t __attribute__((error("use sectorsPerCluster()"))) blocksPerCluster();
   uint32_t __attribute__((error("use sectorsPerCluster()"))) blocksPerCluster();
 #endif  // DOXYGEN_SHOULD_SKIP_THIS
 #endif  // DOXYGEN_SHOULD_SKIP_THIS
@@ -119,10 +139,10 @@ class FsVolume {
     return m_fVol ? m_fVol->fatType() :
     return m_fVol ? m_fVol->fatType() :
            m_xVol ? m_xVol->fatType() : 0;
            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() :
     return m_fVol ? m_fVol->freeClusterCount() :
-           m_xVol ? m_xVol->freeClusterCount() : 0;
+           m_xVol ? m_xVol->freeClusterCount() : -1;
   }
   }
   /**
   /**
    * Check for device busy.
    * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * MIT License
@@ -32,10 +32,14 @@
 // Part 1
 // Part 1
 // Physical Layer
 // Physical Layer
 // Simplified Specification
 // Simplified Specification
-// Version 5.00
-// Aug 10, 2016
+// Version 8.00
+// Sep 23, 2020
 //
 //
 // https://www.sdcard.org/downloads/pls/
 // 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
 // SD card errors
 // See the SD Specification for command info.
 // See the SD Specification for command info.
@@ -64,6 +68,7 @@
   SD_CARD_ERROR(ACMD13, "Read extended status")\
   SD_CARD_ERROR(ACMD13, "Read extended status")\
   SD_CARD_ERROR(ACMD23, "Set pre-erased count")\
   SD_CARD_ERROR(ACMD23, "Set pre-erased count")\
   SD_CARD_ERROR(ACMD41, "Activate card initialization")\
   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_TOKEN, "Bad read data token")\
   SD_CARD_ERROR(READ_CRC, "Read CRC error")\
   SD_CARD_ERROR(READ_CRC, "Read CRC error")\
   SD_CARD_ERROR(READ_FIFO, "SDIO fifo read timeout")\
   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
 /** SD_SEND_OP_COMD - Sends host capacity support information and
     activates the card's initialization process */
     activates the card's initialization process */
 const uint8_t ACMD41 = 0X29;
 const uint8_t ACMD41 = 0X29;
+/** Reads the SD Configuration Register (SCR). */
+const uint8_t ACMD51 = 0X33;
 //==============================================================================
 //==============================================================================
 // CARD_STATUS
 // CARD_STATUS
 /** The command's argument was out of the allowed range for this card. */
 /** 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 {
 typedef struct CID {
   // byte 0
   // byte 0
   /** Manufacturer ID */
   /** Manufacturer ID */
-  unsigned char mid;
+  uint8_t mid;
   // byte 1-2
   // byte 1-2
-  /** OEM/Application ID */
+  /** OEM/Application ID. */
   char oid[2];
   char oid[2];
   // byte 3-7
   // byte 3-7
-  /** Product name */
+  /** Product name. */
   char pnm[5];
   char pnm[5];
   // byte 8
   // 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
   // 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
   // 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;
 } __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
 // fields are big endian
 typedef struct SdStatus {
 typedef struct SdStatus {
+  //
   uint8_t busWidthSecureMode;
   uint8_t busWidthSecureMode;
   uint8_t reserved1;
   uint8_t reserved1;
+  // byte 2
   uint8_t sdCardType[2];
   uint8_t sdCardType[2];
+  // byte 4
   uint8_t sizeOfProtectedArea[4];
   uint8_t sizeOfProtectedArea[4];
+  // byte 8
   uint8_t speedClass;
   uint8_t speedClass;
+  // byte 9
   uint8_t performanceMove;
   uint8_t performanceMove;
+  // byte 10
   uint8_t auSize;
   uint8_t auSize;
+  // byte 11
   uint8_t eraseSize[2];
   uint8_t eraseSize[2];
+  // byte 13
   uint8_t eraseTimeoutOffset;
   uint8_t eraseTimeoutOffset;
+  // byte 14
   uint8_t uhsSpeedAuSize;
   uint8_t uhsSpeedAuSize;
+  // byte 15
   uint8_t videoSpeed;
   uint8_t videoSpeed;
+  // byte 16
   uint8_t vscAuSize[2];
   uint8_t vscAuSize[2];
+  // byte 18
   uint8_t susAddr[3];
   uint8_t susAddr[3];
+  // byte 21
   uint8_t reserved2[3];
   uint8_t reserved2[3];
+  // byte 24
   uint8_t reservedManufacturer[40];
   uint8_t reservedManufacturer[40];
 } SdStatus_t;
 } SdStatus_t;
 #endif  // DOXYGEN_SHOULD_SKIP_THIS
 #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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * MIT License
@@ -32,6 +32,13 @@
  */
  */
 class SdCardInterface : public FsBlockDeviceInterface {
 class SdCardInterface : public FsBlockDeviceInterface {
  public:
  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 */
   /** end use of card */
   virtual void end() = 0;
   virtual void end() = 0;
    /** Erase a range of sectors.
    /** Erase a range of sectors.
@@ -82,6 +89,12 @@ class SdCardInterface : public FsBlockDeviceInterface {
    * \return true for success or false for failure.
    * \return true for success or false for failure.
    */
    */
   virtual bool readOCR(uint32_t* ocr) = 0;
   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.
    * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * MIT License
@@ -132,6 +132,7 @@ static uint16_t CRC_CCITT(const uint8_t* data, size_t n) {
 bool SharedSpiCard::begin(SdSpiConfig spiConfig) {
 bool SharedSpiCard::begin(SdSpiConfig spiConfig) {
   Timeout timeout;
   Timeout timeout;
   m_spiActive = false;
   m_spiActive = false;
+  m_beginCalled = false;
   m_errorCode = SD_CARD_ERROR_NONE;
   m_errorCode = SD_CARD_ERROR_NONE;
   m_type = 0;
   m_type = 0;
   m_csPin = spiConfig.csPin;
   m_csPin = spiConfig.csPin;
@@ -146,6 +147,7 @@ bool SharedSpiCard::begin(SdSpiConfig spiConfig) {
   spiUnselect();
   spiUnselect();
   spiSetSckSpeed(1000UL*SD_MAX_INIT_RATE_KHZ);
   spiSetSckSpeed(1000UL*SD_MAX_INIT_RATE_KHZ);
   spiBegin(spiConfig);
   spiBegin(spiConfig);
+  m_beginCalled = true;
   uint32_t arg;
   uint32_t arg;
   m_state = IDLE_STATE;
   m_state = IDLE_STATE;
   spiStart();
   spiStart();
@@ -165,6 +167,10 @@ bool SharedSpiCard::begin(SdSpiConfig spiConfig) {
       error(SD_CARD_ERROR_CMD0);
       error(SD_CARD_ERROR_CMD0);
       goto fail;
       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 USE_SD_CRC
   if (cardCommand(CMD59, 1) != R1_IDLE_STATE) {
   if (cardCommand(CMD59, 1) != R1_IDLE_STATE) {
@@ -172,7 +178,6 @@ bool SharedSpiCard::begin(SdSpiConfig spiConfig) {
     goto fail;
     goto fail;
   }
   }
 #endif  // USE_SD_CRC
 #endif  // USE_SD_CRC
-
   // check SD version
   // check SD version
   if (!(cardCommand(CMD8, 0x1AA) & R1_ILLEGAL_COMMAND)) {
   if (!(cardCommand(CMD8, 0x1AA) & R1_ILLEGAL_COMMAND)) {
     type(SD_CARD_TYPE_SD2);
     type(SD_CARD_TYPE_SD2);
@@ -220,6 +225,22 @@ bool SharedSpiCard::begin(SdSpiConfig spiConfig) {
   return false;
   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
 // send command and return error code.  Return zero for OK
 uint8_t SharedSpiCard::cardCommand(uint8_t cmd, uint32_t arg) {
 uint8_t SharedSpiCard::cardCommand(uint8_t cmd, uint32_t arg) {
   if (!syncDevice()) {
   if (!syncDevice()) {
@@ -229,12 +250,9 @@ uint8_t SharedSpiCard::cardCommand(uint8_t cmd, uint32_t arg) {
   if (!m_spiActive) {
   if (!m_spiActive) {
     spiStart();
     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
 #if USE_SD_CRC
   // form message
   // form message
   uint8_t buf[6];
   uint8_t buf[6];
@@ -274,15 +292,23 @@ uint8_t SharedSpiCard::cardCommand(uint8_t cmd, uint32_t arg) {
   return m_status;
   return m_status;
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
+void SharedSpiCard::end() {
+  if (m_beginCalled) {
+    spiStop();
+    spiEnd();
+    m_beginCalled = false;
+  }
+}
+//------------------------------------------------------------------------------
 bool SharedSpiCard::erase(uint32_t firstSector, uint32_t lastSector) {
 bool SharedSpiCard::erase(uint32_t firstSector, uint32_t lastSector) {
   csd_t csd;
   csd_t csd;
   if (!readCSD(&csd)) {
   if (!readCSD(&csd)) {
     goto fail;
     goto fail;
   }
   }
   // check for single sector erase
   // check for single sector erase
-  if (!csd.v1.erase_blk_en) {
+  if (!csd.eraseSingleBlock()) {
     // erase size mask
     // 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) {
     if ((firstSector & m) != 0 || ((lastSector + 1) & m) != 0) {
       // error card can't erase specified area
       // error card can't erase specified area
       error(SD_CARD_ERROR_ERASE_SINGLE_SECTOR);
       error(SD_CARD_ERROR_ERASE_SINGLE_SECTOR);
@@ -313,7 +339,7 @@ bool SharedSpiCard::erase(uint32_t firstSector, uint32_t lastSector) {
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 bool SharedSpiCard::eraseSingleSectorEnable() {
 bool SharedSpiCard::eraseSingleSectorEnable() {
   csd_t csd;
   csd_t csd;
-  return readCSD(&csd) ? csd.v1.erase_blk_en : false;
+  return readCSD(&csd) ? csd.eraseSingleBlock() : false;
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 bool SharedSpiCard::isBusy() {
 bool SharedSpiCard::isBusy() {
@@ -384,7 +410,11 @@ bool SharedSpiCard::readOCR(uint32_t* ocr) {
     goto fail;
     goto fail;
   }
   }
   for (uint8_t i = 0; i < 4; i++) {
   for (uint8_t i = 0; i < 4; i++) {
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
     p[3 - i] = spiReceive();
     p[3 - i] = spiReceive();
+#else  // __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+    p[i] = spiReceive();
+#endif  // __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
   }
   }
   spiStop();
   spiStop();
   return true;
   return true;
@@ -412,6 +442,23 @@ bool SharedSpiCard::readRegister(uint8_t cmd, void* buf) {
   return false;
   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) {
 bool SharedSpiCard::readSector(uint32_t sector, uint8_t* dst) {
   // use address if not SDHC card
   // use address if not SDHC card
   if (type() != SD_CARD_TYPE_SDHC) {
   if (type() != SD_CARD_TYPE_SDHC) {
@@ -462,13 +509,14 @@ bool SharedSpiCard::readStart(uint32_t sector) {
   return false;
   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.
   // retrun is R2 so read extra status byte.
   if (cardAcmd(ACMD13, 0) || spiReceive()) {
   if (cardAcmd(ACMD13, 0) || spiReceive()) {
     error(SD_CARD_ERROR_ACMD13);
     error(SD_CARD_ERROR_ACMD13);
     goto fail;
     goto fail;
   }
   }
-  if (!readData(status, 64)) {
+  if (!readData(dst, 64)) {
     goto fail;
     goto fail;
   }
   }
   spiStop();
   spiStop();
@@ -495,16 +543,16 @@ bool SharedSpiCard::readStop() {
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 uint32_t SharedSpiCard::sectorCount() {
 uint32_t SharedSpiCard::sectorCount() {
   csd_t csd;
   csd_t csd;
-  return readCSD(&csd) ? sdCardCapacity(&csd) : 0;
+  return readCSD(&csd) ? csd.capacity() : 0;
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 void SharedSpiCard::spiStart() {
 void SharedSpiCard::spiStart() {
   if (!m_spiActive) {
   if (!m_spiActive) {
     spiActivate();
     spiActivate();
+    m_spiActive = true;
     spiSelect();
     spiSelect();
     // Dummy byte to drive MISO busy status.
     // Dummy byte to drive MISO busy status.
     spiSend(0XFF);
     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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * MIT License
@@ -33,6 +33,17 @@
 #include "SdCardInfo.h"
 #include "SdCardInfo.h"
 #include "SdCardInterface.h"
 #include "SdCardInterface.h"
 #include "../SpiDriver/SdSpiDriver.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
  * \class SharedSpiCard
@@ -59,10 +70,15 @@ class SharedSpiCard {
    * \return true for success or false for failure.
    * \return true for success or false for failure.
    */
    */
   bool begin(SdSpiConfig spiConfig);
   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 */
   /** End use of card */
-  void end() {
-    spiEnd();
-  }
+  void end();
   /** Erase a range of sectors.
   /** Erase a range of sectors.
    *
    *
    * \param[in] firstSector The address of the first sector in the range.
    * \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.
    * \return true for success or false for failure.
    */
    */
   bool readOCR(uint32_t* ocr);
   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.
    * Read a 512 byte sector from an SD card.
    *
    *
@@ -178,7 +200,7 @@ class SharedSpiCard {
    * \param[out] status location for 64 status bytes.
    * \param[out] status location for 64 status bytes.
    * \return true for success or false for failure.
    * \return true for success or false for failure.
    */
    */
-  bool readStatus(uint8_t* status);
+  bool readStatus(SdStatus* status);
   /** End a read multiple sectors sequence.
   /** End a read multiple sectors sequence.
    *
    *
    * \return true for success or false for failure.
    * \return true for success or false for failure.
@@ -293,15 +315,19 @@ class SharedSpiCard {
     m_spiDriver.end();
     m_spiDriver.end();
   }
   }
   uint8_t spiReceive() {
   uint8_t spiReceive() {
+    SPI_ASSERT_ACTIVE;
     return m_spiDriver.receive();
     return m_spiDriver.receive();
   }
   }
   uint8_t spiReceive(uint8_t* buf, size_t n) {
   uint8_t spiReceive(uint8_t* buf, size_t n) {
+    SPI_ASSERT_ACTIVE;
     return m_spiDriver.receive(buf, n);
     return m_spiDriver.receive(buf, n);
   }
   }
   void spiSend(uint8_t data) {
   void spiSend(uint8_t data) {
+    SPI_ASSERT_ACTIVE;
     m_spiDriver.send(data);
     m_spiDriver.send(data);
   }
   }
   void spiSend(const uint8_t* buf, size_t n) {
   void spiSend(const uint8_t* buf, size_t n) {
+    SPI_ASSERT_ACTIVE;
     m_spiDriver.send(buf, n);
     m_spiDriver.send(buf, n);
   }
   }
   void spiSetSckSpeed(uint32_t maxSck) {
   void spiSetSckSpeed(uint32_t maxSck) {
@@ -322,22 +348,28 @@ class SharedSpiCard {
     m_spiDriverPtr->end();
     m_spiDriverPtr->end();
   }
   }
   uint8_t spiReceive() {
   uint8_t spiReceive() {
+    SPI_ASSERT_ACTIVE;
     return m_spiDriverPtr->receive();
     return m_spiDriverPtr->receive();
   }
   }
   uint8_t spiReceive(uint8_t* buf, size_t n) {
   uint8_t spiReceive(uint8_t* buf, size_t n) {
+    SPI_ASSERT_ACTIVE;
     return m_spiDriverPtr->receive(buf, n);
     return m_spiDriverPtr->receive(buf, n);
   }
   }
   void spiSend(uint8_t data) {
   void spiSend(uint8_t data) {
+    SPI_ASSERT_ACTIVE;
     m_spiDriverPtr->send(data);
     m_spiDriverPtr->send(data);
   }
   }
   void spiSend(const uint8_t* buf, size_t n) {
   void spiSend(const uint8_t* buf, size_t n) {
+    SPI_ASSERT_ACTIVE;
     m_spiDriverPtr->send(buf, n);
     m_spiDriverPtr->send(buf, n);
   }
   }
   void spiSetSckSpeed(uint32_t maxSck) {
   void spiSetSckSpeed(uint32_t maxSck) {
     m_spiDriverPtr->setSckSpeed(maxSck);
     m_spiDriverPtr->setSckSpeed(maxSck);
   }
   }
   SdSpiDriver* m_spiDriverPtr;
   SdSpiDriver* m_spiDriverPtr;
+
 #endif  // SPI_DRIVER_SELECT < 2
 #endif  // SPI_DRIVER_SELECT < 2
+  bool m_beginCalled = false;
   SdCsPin_t m_csPin;
   SdCsPin_t m_csPin;
   uint8_t m_errorCode = SD_CARD_ERROR_INIT_NOT_CALLED;
   uint8_t m_errorCode = SD_CARD_ERROR_INIT_NOT_CALLED;
   bool    m_spiActive;
   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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * MIT License
@@ -60,6 +60,13 @@ class SdioCard : public SdCardInterface {
    * \return true for success or false for failure.
    * \return true for success or false for failure.
    */
    */
   bool begin(SdioConfig sdioConfig);
   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.
   /** Disable an SDIO card.
    * not implemented.
    * not implemented.
    */
    */
@@ -146,6 +153,12 @@ class SdioCard : public SdCardInterface {
    * \return true for success or false for failure.
    * \return true for success or false for failure.
    */
    */
   bool readOCR(uint32_t* ocr);
   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.
   /** Start a read multiple sectors sequence.
    *
    *
    * \param[in] sector Address of first sector in 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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 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 CMD0_XFERTYP = SDHC_XFERTYP_CMDINX(CMD0) | CMD_RESP_NONE;
 
 
 const uint32_t CMD2_XFERTYP = SDHC_XFERTYP_CMDINX(CMD2) | CMD_RESP_R2;
 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 uint32_t m_ocr;
 static cid_t m_cid;
 static cid_t m_cid;
 static csd_t m_csd;
 static csd_t m_csd;
+static scr_t m_scr;
 //==============================================================================
 //==============================================================================
 #define DBG_TRACE Serial.print("TRACE."); Serial.println(__LINE__); delay(200);
 #define DBG_TRACE Serial.print("TRACE."); Serial.println(__LINE__); delay(200);
 #define USE_DEBUG_MODE 0
 #define USE_DEBUG_MODE 0
@@ -402,17 +406,17 @@ static bool cardCommand(uint32_t xfertyp, uint32_t arg) {
          !(m_irqstat & SDHC_IRQSTAT_CMD_ERROR);
          !(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)) {
   if (waitTimeout(isBusyCMD13)) {
     return sdError(SD_CARD_ERROR_CMD13);
     return sdError(SD_CARD_ERROR_CMD13);
   }
   }
   enableDmaIrs();
   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;
   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()) {
   if (!waitDmaStatus()) {
     return sdError(SD_CARD_ERROR_DMA);
     return sdError(SD_CARD_ERROR_DMA);
@@ -662,7 +666,10 @@ bool SdioCard::begin(SdioConfig sdioConfig) {
       m_version2 = true;
       m_version2 = true;
       break;
       break;
     }
     }
+    SDHC_SYSCTL |= SDHC_SYSCTL_RSTA;
+    while (SDHC_SYSCTL & SDHC_SYSCTL_RSTA) {}
   }
   }
+  // Must support 3.2-3.4 Volts
   arg = m_version2 ? 0X40300000 : 0x00300000;
   arg = m_version2 ? 0X40300000 : 0x00300000;
   int m = micros();
   int m = micros();
   do {
   do {
@@ -703,10 +710,14 @@ bool SdioCard::begin(SdioConfig sdioConfig) {
 
 
   SDHC_WML = SDHC_WML_RDWML(FIFO_WML) | SDHC_WML_WRWML(FIFO_WML);
   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.
   // Determine if High Speed mode is supported and set frequency.
   // Check status[16] for error 0XF or status[16] for new mode 0X1.
   // Check status[16] for error 0XF or status[16] for new mode 0X1.
   uint8_t status[64];
   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) {
       cardCMD6(0X80FFFFF1, status) && (status[16] & 0XF) == 1) {
     kHzSdClk = 50000;
     kHzSdClk = 50000;
   } else {
   } else {
@@ -724,16 +735,32 @@ bool SdioCard::begin(SdioConfig sdioConfig) {
   return true;
   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) {
 bool SdioCard::erase(uint32_t firstSector, uint32_t lastSector) {
-#if ENABLE_TEENSY_SDIO_MOD
   if (m_curState != IDLE_STATE && !syncDevice()) {
   if (m_curState != IDLE_STATE && !syncDevice()) {
     return false;
     return false;
   }
   }
-#endif  // ENABLE_TEENSY_SDIO_MOD
   // check for single sector erase
   // check for single sector erase
-  if (!m_csd.v1.erase_blk_en) {
+  if (!m_csd.eraseSingleBlock()) {
     // erase size mask
     // 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) {
     if ((firstSector & m) != 0 || ((lastSector + 1) & m) != 0) {
       // error card can't erase specified area
       // error card can't erase specified area
       return sdError(SD_CARD_ERROR_ERASE_SINGLE_SECTOR);
       return sdError(SD_CARD_ERROR_ERASE_SINGLE_SECTOR);
@@ -843,6 +870,11 @@ bool SdioCard::readOCR(uint32_t* ocr) {
   return true;
   return true;
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
+bool SdioCard::readSCR(scr_t* scr) {
+  memcpy(scr, &m_scr, 8);
+  return true;
+}
+//------------------------------------------------------------------------------
 bool SdioCard::readSector(uint32_t sector, uint8_t* dst) {
 bool SdioCard::readSector(uint32_t sector, uint8_t* dst) {
   if (m_sdioConfig.useDma()) {
   if (m_sdioConfig.useDma()) {
     uint8_t aligned[512];
     uint8_t aligned[512];
@@ -933,7 +965,7 @@ bool SdioCard::readStop() {
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 uint32_t SdioCard::sectorCount() {
 uint32_t SdioCard::sectorCount() {
-  return sdCardCapacity(&m_csd);
+  return m_csd.capacity();
 }
 }
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 uint32_t SdioCard::status() {
 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * MIT License
@@ -38,9 +38,9 @@
 #endif  // INCLUDE_SDIOS
 #endif  // INCLUDE_SDIOS
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 /** SdFat version for cpp use. */
 /** SdFat version for cpp use. */
-#define SD_FAT_VERSION 20102
+#define SD_FAT_VERSION 20200
 /** SdFat version as string. */
 /** SdFat version as string. */
-#define SD_FAT_VERSION_STR "2.1.2"
+#define SD_FAT_VERSION_STR "2.2.0"
 //==============================================================================
 //==============================================================================
 /**
 /**
  * \class SdBase
  * \class SdBase
@@ -174,11 +174,11 @@ class SdBase : public Vol {
     }
     }
     bool switchSpi = hasDedicatedSpi() && !isDedicatedSpi();
     bool switchSpi = hasDedicatedSpi() && !isDedicatedSpi();
     if (switchSpi && !setDedicatedSpi(true)) {
     if (switchSpi && !setDedicatedSpi(true)) {
-      return 0;
+      return false;
     }
     }
     bool rtn = fmt.format(card(), mem, pr);
     bool rtn = fmt.format(card(), mem, pr);
     if (switchSpi && !setDedicatedSpi(false)) {
     if (switchSpi && !setDedicatedSpi(false)) {
-      return 0;
+      return false;
     }
     }
     return rtn;
     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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * MIT License
@@ -41,6 +41,7 @@
 // #define ENABLE_DEDICATED_SPI 0
 // #define ENABLE_DEDICATED_SPI 0
 // #define USE_LONG_FILE_NAMES 0
 // #define USE_LONG_FILE_NAMES 0
 // #define SDFAT_FILE_TYPE 1
 // #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
 // Options can be set in a makefile or an IDE like platformIO
 // if they are in a #ifndef/#endif block below.
 // if they are in a #ifndef/#endif block below.
@@ -258,7 +259,7 @@ typedef uint8_t SdCsPin_t;
  * is non-zero.
  * is non-zero.
  */
  */
 #ifndef CHECK_FLASH_PROGRAMMING
 #ifndef CHECK_FLASH_PROGRAMMING
-#define CHECK_FLASH_PROGRAMMING 0
+#define CHECK_FLASH_PROGRAMMING 1
 #endif  // CHECK_FLASH_PROGRAMMING
 #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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * 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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * MIT License
@@ -26,9 +26,9 @@
 #define FsStructs_h
 #define FsStructs_h
 #include <stddef.h>
 #include <stddef.h>
 #include <stdint.h>
 #include <stdint.h>
-//-----------------------------------------------------------------------------
+//------------------------------------------------------------------------------
 void lbaToMbrChs(uint8_t* chs, uint32_t capacityMB, uint32_t lba);
 void lbaToMbrChs(uint8_t* chs, uint32_t capacityMB, uint32_t lba);
-//-----------------------------------------------------------------------------
+//------------------------------------------------------------------------------
 #if !defined(USE_SIMPLE_LITTLE_ENDIAN) || USE_SIMPLE_LITTLE_ENDIAN
 #if !defined(USE_SIMPLE_LITTLE_ENDIAN) || USE_SIMPLE_LITTLE_ENDIAN
 // assumes CPU is little-endian and handles alignment issues.
 // assumes CPU is little-endian and handles alignment issues.
 inline uint16_t getLe16(const uint8_t* src) {
 inline uint16_t getLe16(const uint8_t* src) {
@@ -149,7 +149,7 @@ typedef struct {
   uint64_t position;
   uint64_t position;
   uint32_t cluster;
   uint32_t cluster;
 } fspos_t;
 } fspos_t;
-//=============================================================================
+//==============================================================================
 const uint8_t EXTENDED_BOOT_SIGNATURE = 0X29;
 const uint8_t EXTENDED_BOOT_SIGNATURE = 0X29;
 typedef struct biosParameterBlockFat16 {
 typedef struct biosParameterBlockFat16 {
   uint8_t  bytesPerSector[2];
   uint8_t  bytesPerSector[2];
@@ -172,7 +172,7 @@ typedef struct biosParameterBlockFat16 {
   uint8_t  volumeLabel[11];
   uint8_t  volumeLabel[11];
   uint8_t  volumeType[8];
   uint8_t  volumeType[8];
 } BpbFat16_t;
 } BpbFat16_t;
-//-----------------------------------------------------------------------------
+//------------------------------------------------------------------------------
 typedef struct biosParameterBlockFat32 {
 typedef struct biosParameterBlockFat32 {
   uint8_t  bytesPerSector[2];
   uint8_t  bytesPerSector[2];
   uint8_t  sectorsPerCluster;
   uint8_t  sectorsPerCluster;
@@ -202,7 +202,7 @@ typedef struct biosParameterBlockFat32 {
   uint8_t  volumeLabel[11];
   uint8_t  volumeLabel[11];
   uint8_t  volumeType[8];
   uint8_t  volumeType[8];
 } BpbFat32_t;
 } BpbFat32_t;
-//-----------------------------------------------------------------------------
+//------------------------------------------------------------------------------
 typedef struct partitionBootSectorFat {
 typedef struct partitionBootSectorFat {
   uint8_t  jmpInstruction[3];
   uint8_t  jmpInstruction[3];
   char     oemName[8];
   char     oemName[8];
@@ -214,7 +214,7 @@ typedef struct partitionBootSectorFat {
   uint8_t  bootCode[390];
   uint8_t  bootCode[390];
   uint8_t  signature[2];
   uint8_t  signature[2];
 } PbsFat_t;
 } PbsFat_t;
-//-----------------------------------------------------------------------------
+//------------------------------------------------------------------------------
 const uint32_t FSINFO_LEAD_SIGNATURE = 0X41615252;
 const uint32_t FSINFO_LEAD_SIGNATURE = 0X41615252;
 const uint32_t FSINFO_STRUCT_SIGNATURE = 0x61417272;
 const uint32_t FSINFO_STRUCT_SIGNATURE = 0x61417272;
 const uint32_t FSINFO_TRAIL_SIGNATURE = 0xAA550000;
 const uint32_t FSINFO_TRAIL_SIGNATURE = 0xAA550000;
@@ -227,17 +227,25 @@ typedef struct FsInfoSector {
   uint8_t reserved2[12];
   uint8_t reserved2[12];
   uint8_t trailSignature[4];
   uint8_t trailSignature[4];
 } FsInfo_t;
 } 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 */
 /** name[0] value for entry that is free and no allocated entries follow */
 const uint8_t FAT_NAME_FREE = 0X00;
 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_LABEL     = 0x08;
-const uint8_t FAT_ATTRIB_DIRECTORY = 0x10;
-const uint8_t FAT_ATTRIB_ARCHIVE   = 0x20;
 const uint8_t FAT_ATTRIB_LONG_NAME = 0X0F;
 const uint8_t FAT_ATTRIB_LONG_NAME = 0X0F;
 /** Filename base-name is all lower case */
 /** Filename base-name is all lower case */
 const uint8_t FAT_CASE_LC_BASE = 0X08;
 const uint8_t FAT_CASE_LC_BASE = 0X08;
@@ -259,20 +267,20 @@ typedef struct {
   uint8_t  fileSize[4];
   uint8_t  fileSize[4];
 } DirFat_t;
 } 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;
   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;
   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
  * 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
  * 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  mustBeZero2[2];
   uint8_t  unicode3[4];
   uint8_t  unicode3[4];
 } DirLfn_t;
 } DirLfn_t;
-//=============================================================================
+//==============================================================================
 inline uint32_t exFatChecksum(uint32_t sum, uint8_t data) {
 inline uint32_t exFatChecksum(uint32_t sum, uint8_t data) {
   return (sum << 31) + (sum >> 1) + data;
   return (sum << 31) + (sum >> 1) + data;
 }
 }
-//-----------------------------------------------------------------------------
+//------------------------------------------------------------------------------
 typedef struct biosParameterBlockExFat {
 typedef struct biosParameterBlockExFat {
   uint8_t mustBeZero[53];
   uint8_t mustBeZero[53];
   uint8_t partitionOffset[8];
   uint8_t partitionOffset[8];
@@ -316,7 +324,7 @@ typedef struct biosParameterBlockExFat {
   uint8_t percentInUse;
   uint8_t percentInUse;
   uint8_t reserved[7];
   uint8_t reserved[7];
 } BpbExFat_t;
 } BpbExFat_t;
-//-----------------------------------------------------------------------------
+//------------------------------------------------------------------------------
 typedef struct ExFatBootSector {
 typedef struct ExFatBootSector {
   uint8_t  jmpInstruction[3];
   uint8_t  jmpInstruction[3];
   char     oemName[8];
   char     oemName[8];
@@ -324,7 +332,7 @@ typedef struct ExFatBootSector {
   uint8_t  bootCode[390];
   uint8_t  bootCode[390];
   uint8_t  signature[2];
   uint8_t  signature[2];
 } ExFatPbs_t;
 } ExFatPbs_t;
-//-----------------------------------------------------------------------------
+//------------------------------------------------------------------------------
 const uint32_t EXFAT_EOC = 0XFFFFFFFF;
 const uint32_t EXFAT_EOC = 0XFFFFFFFF;
 
 
 const uint8_t EXFAT_TYPE_BITMAP = 0X81;
 const uint8_t EXFAT_TYPE_BITMAP = 0X81;
@@ -335,7 +343,7 @@ typedef struct {
   uint8_t  firstCluster[4];
   uint8_t  firstCluster[4];
   uint8_t  size[8];
   uint8_t  size[8];
 } DirBitmap_t;
 } DirBitmap_t;
-//-----------------------------------------------------------------------------
+//------------------------------------------------------------------------------
 const uint8_t EXFAT_TYPE_UPCASE = 0X82;
 const uint8_t EXFAT_TYPE_UPCASE = 0X82;
 typedef struct {
 typedef struct {
   uint8_t  type;
   uint8_t  type;
@@ -345,7 +353,7 @@ typedef struct {
   uint8_t  firstCluster[4];
   uint8_t  firstCluster[4];
   uint8_t  size[8];
   uint8_t  size[8];
 } DirUpcase_t;
 } DirUpcase_t;
-//-----------------------------------------------------------------------------
+//------------------------------------------------------------------------------
 const uint8_t EXFAT_TYPE_LABEL = 0X83;
 const uint8_t EXFAT_TYPE_LABEL = 0X83;
 typedef struct {
 typedef struct {
   uint8_t  type;
   uint8_t  type;
@@ -353,14 +361,14 @@ typedef struct {
   uint8_t  unicode[22];
   uint8_t  unicode[22];
   uint8_t  reserved[8];
   uint8_t  reserved[8];
 } DirLabel_t;
 } 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_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_RESERVED  = 0x08;
-const uint8_t EXFAT_ATTRIB_DIRECTORY = 0x10;
-const uint8_t EXFAT_ATTRIB_ARCHIVE   = 0x20;
 
 
 typedef struct {
 typedef struct {
   uint8_t  type;
   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.
  * This file is part of the SdFat library for SD memory cards.
  *
  *
  * MIT License
  * MIT License

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно