ソースを参照

New core FAT implementation

Bill Greiman 10 年 前
コミット
c092796599
100 ファイル変更9501 行追加6703 行削除
  1. 0 62
      ArduinoDue.txt
  2. 0 13
      MultipleCards.txt
  3. 0 21
      QuickStart.txt
  4. 0 23
      SPI_Transactions.txt
  5. 6 19
      SdFat/MinimumSerial.cpp
  6. 17 3
      SdFat/MinimumSerial.h
  7. 0 202
      SdFat/Sd2Card.h
  8. 0 2024
      SdFat/SdBaseFile.cpp
  9. 0 289
      SdFat/SdBaseFile.h
  10. 0 351
      SdFat/SdBaseFilePrint.cpp
  11. 0 261
      SdFat/SdFat.cpp
  12. 305 76
      SdFat/SdFat.h
  13. 99 0
      SdFat/SdFatBase.cpp
  14. 70 87
      SdFat/SdFatConfig.h
  15. 0 145
      SdFat/SdFatErrorPrint.cpp
  16. 12 37
      SdFat/SdFatUtil.cpp
  17. 32 5
      SdFat/SdFatUtil.h
  18. 173 45
      SdFat/SdFatmainpage.h
  19. 0 83
      SdFat/SdFile.cpp
  20. 0 257
      SdFat/SdFile.h
  21. 97 19
      SdFat/SdInfo.h
  22. 280 83
      SdFat/SdSpi.h
  23. 0 86
      SdFat/SdSpiAVR.cpp
  24. 0 70
      SdFat/SdSpiArduino.cpp
  25. 172 206
      SdFat/SdSpiCard.cpp
  26. 308 0
      SdFat/SdSpiCard.h
  27. 25 23
      SdFat/SdSpiSAM3X.cpp
  28. 0 61
      SdFat/SdSpiSoft.cpp
  29. 83 5
      SdFat/SdSpiTeensy3.cpp
  30. 0 595
      SdFat/SdVolume.cpp
  31. 38 183
      SdFat/SdVolume.h
  32. 40 24
      SdFat/examples/#attic/AnalogLogger/AnalogLogger.ino
  33. 38 0
      SdFat/examples/#attic/BaseExtCaseTest/BaseExtCaseTest.ino
  34. 2 1
      SdFat/examples/#attic/HelloWorld/HelloWorld.ino
  35. 17 6
      SdFat/examples/#attic/MiniSerial/MiniSerial.ino
  36. 23 15
      SdFat/examples/#attic/PrintBenchmarkSD/PrintBenchmarkSD.ino
  37. 2 2
      SdFat/examples/#attic/SD_Size/SD_Size.ino
  38. 4 3
      SdFat/examples/#attic/SdFatSize/SdFatSize.ino
  39. 22 13
      SdFat/examples/#attic/append/append.ino
  40. 15 8
      SdFat/examples/#attic/average/average.ino
  41. 20 13
      SdFat/examples/#attic/benchSD/benchSD.ino
  42. 2 1
      SdFat/examples/#attic/bufstream/bufstream.ino
  43. 13 8
      SdFat/examples/#attic/eventlog/eventlog.ino
  44. 52 37
      SdFat/examples/#attic/fgetsRewrite/fgetsRewrite.ino
  45. 14 7
      SdFat/examples/#attic/readlog/readlog.ino
  46. 0 2
      SdFat/examples/#attic/readme.txt
  47. 3 3
      SdFat/examples/AnalogBinLogger/AnalogBinLogger.h
  48. 121 79
      SdFat/examples/AnalogBinLogger/AnalogBinLogger.ino
  49. 94 0
      SdFat/examples/LongFileName/LongFileName.ino
  50. 4 0
      SdFat/examples/LongFileName/testFiles/A long name can be 255 characters.txt
  51. 1 0
      SdFat/examples/LongFileName/testFiles/LFN,NAME.TXT
  52. 5 0
      SdFat/examples/LongFileName/testFiles/MIXCASE.txt
  53. 2 0
      SdFat/examples/LongFileName/testFiles/Not_8_3.txt
  54. 1 0
      SdFat/examples/LongFileName/testFiles/OK%83.TXT
  55. 1 0
      SdFat/examples/LongFileName/testFiles/STD_8_3.TXT
  56. 2 0
      SdFat/examples/LongFileName/testFiles/With Blank.txt
  57. 2 0
      SdFat/examples/LongFileName/testFiles/With.Two dots.txt
  58. 5 0
      SdFat/examples/LongFileName/testFiles/lower.txt
  59. 5 0
      SdFat/examples/LongFileName/testFiles/mixed.TXT
  60. 76 50
      SdFat/examples/LowLatencyLogger/LowLatencyLogger.ino
  61. 7 3
      SdFat/examples/OpenNext/OpenNext.ino
  62. 43 36
      SdFat/examples/PrintBenchmark/PrintBenchmark.ino
  63. 80 79
      SdFat/examples/QuickStart/QuickStart.ino
  64. 33 34
      SdFat/examples/RawWrite/RawWrite.ino
  65. 88 0
      SdFat/examples/ReadWrite/ReadWrite.ino
  66. 12 7
      SdFat/examples/ReadWriteSdFat/ReadWriteSdFat.ino
  67. 103 78
      SdFat/examples/SdFormatter/SdFormatter.ino
  68. 107 83
      SdFat/examples/SdInfo/SdInfo.ino
  69. 48 0
      SdFat/examples/SoftwareSpi/SoftwareSpi.ino
  70. 54 47
      SdFat/examples/StdioBench/StdioBench.ino
  71. 13 6
      SdFat/examples/StreamParseInt/StreamParseInt.ino
  72. 228 0
      SdFat/examples/ThreeCards/ThreeCards.ino
  73. 33 28
      SdFat/examples/Timestamp/Timestamp.ino
  74. 82 57
      SdFat/examples/TwoCards/TwoCards.ino
  75. 51 39
      SdFat/examples/bench/bench.ino
  76. 1 0
      SdFat/examples/cin_cout/cin_cout.ino
  77. 32 27
      SdFat/examples/dataLogger/dataLogger.ino
  78. 55 44
      SdFat/examples/directoryFunctions/directoryFunctions.ino
  79. 35 28
      SdFat/examples/fgets/fgets.ino
  80. 7 4
      SdFat/examples/formatting/formatting.ino
  81. 13 10
      SdFat/examples/getline/getline.ino
  82. 47 32
      SdFat/examples/readCSV/readCSV.ino
  83. 51 26
      SdFat/examples/rename/rename.ino
  84. 248 0
      SdFat/utility/ArduinoFiles.h
  85. 41 19
      SdFat/utility/ArduinoStream.h
  86. 82 40
      SdFat/utility/DigitalPin.h
  87. 8 6
      SdFat/utility/FatApiConstants.h
  88. 1494 0
      SdFat/utility/FatFile.cpp
  89. 990 0
      SdFat/utility/FatFile.h
  90. 683 0
      SdFat/utility/FatFileLFN.cpp
  91. 248 0
      SdFat/utility/FatFilePrint.cpp
  92. 273 0
      SdFat/utility/FatFileSFN.cpp
  93. 310 0
      SdFat/utility/FatFileSystem.h
  94. 33 0
      SdFat/utility/FatLib.h
  95. 143 0
      SdFat/utility/FatLibConfig.h
  96. 412 335
      SdFat/utility/FatStructs.h
  97. 585 0
      SdFat/utility/FatVolume.cpp
  98. 338 0
      SdFat/utility/FatVolume.h
  99. 78 28
      SdFat/utility/FmtNumber.cpp
  100. 9 1
      SdFat/utility/FmtNumber.h

+ 0 - 62
ArduinoDue.txt

@@ -1,62 +0,0 @@
-Support has been added for the Arduino Due.
-
-You must connect your SD socket to the 6-pin "ISP connector".  You must have short
-wires or a custom shield to run at full speed, 42 MHz.
-
-If you have problems use a lower SPI speed.  You can also check for SPI
-errors by editing SdFatCobfig.h to enable CRC checking.
-
-You should be be able to use any digital pin for SD chip select.  The default
-pin is SS which is pin 10 for Due.
-
-The default SPI rate is 42 MHz.  You can set SD chip select and the SPI rate
-by calling:
-
-bool SdFat::begin(uint8_t chipSelectPin, uint8_t spiRateID);
-
-The second argument, spiRateID, sets the SCK rate and can be these symbols:
-
-SPI_FULL_SPEED - 42 MHz
-
-SPI_DIV3_SPEED - 28 MHz
-
-SPI_HALF_SPEED - 21 MHz
-
-SPI_DIV6_SPEED - 14 MHz
-
-SPI_QUARTER_SPEED 10.5 MHz
-
-SPI_EIGHTH_SPEED 5.25 MHz
-
-Large reads and writes use fast multi-block SD read/write commands. For optimal
-speed, use records that are a multiple of 512 bytes.
-
-Run the bench.ino example to explore large read/write speed.
-
-Replace this line:
-
-#define BUF_SIZE 100
-
-With a large size like this:
-
-#define BUF_SIZE 8192
-
-For best results the record size should be a power of two (512, 1024, 2048,
-4096, 8192). In this case records will be aligned with FAT cluster boundaries.
-
-Since Due is fast, increase the test file size by editing this line:
-
-#define FILE_SIZE_MB 5
-
-Run the PrintBenchmark.ino example to compare text formatting speed of Due
-with AVR boards.
-
-A number of options are available to configure SPI for the Due board.
-
-You can use the standard SPI.h library by editing SdFatConfig.h and set
-USE_ARDUINO_SPI_LIBRARY nonzero.  You must include SPI.h in your sketch.
-
-Several options can be set in Sd2Card.cpp in the USE_NATIVE_SAM3X_SPI
-section.  These include USE_SAM3X_DMAC to control use of DMA and
-USE_SAM3X_BUS_MATRIX_FIX to change Bus Matrix operation.  Most people
-will not need to change these.

+ 0 - 13
MultipleCards.txt

@@ -1,13 +0,0 @@
-SdFat has support for multiple SD cards.  This requires multiple instances
-of SdFat objects.
-
-You must edit SdFatConfig.h to enable multiple instances of SdFat. Set
-USE_MULTIPLE_CARDS nonzero like this:
-
-#define USE_MULTIPLE_CARDS 1
-
-Look at TwoCards.pde in the SdFat/examples folder. This example demonstrates
-use of two SD cards.
-
-Read WorkingDirectory.txt for more information on volume working
-directories and the current working directory.

+ 0 - 21
QuickStart.txt

@@ -1,21 +0,0 @@
-For those who don't like too much documentation.
-
-To use this library place the SdFat folder into the libraries
-subfolder in your main sketches folder.  You may need to 
-create the libraries folder.  Restart the Arduino IDE if
-it was open.
-
-Run the QuickStart.ino sketch from the 
-libraries/SdFat/examples/QuickStart folder. Click the 
-IDE up-arrow icon then -> libraries -> SdFat -> QuickStart.
-
-You can also click File -> Examples -> SdFat -> QuickStart.
-
-If problems occur try reading more documentation and use these
-forums for help:
-
-http://forums.adafruit.com/
-
-http://arduino.cc/forum/
-
-If QuickStart.ino runs successfully try more examples.

+ 0 - 23
SPI_Transactions.txt

@@ -1,23 +0,0 @@
-To enable support for SPI transactions, edit SfFatCinfig.h and modify these
-defines.
-
-//------------------------------------------------------------------------------
-/**
- * Set ENABLE_SPI_TRANSACTION nonzero to enable the SPI transaction feature
- * of the standard Arduino SPI library.  You must include SPI.h in your
- * sketches when ENABLE_SPI_TRANSACTION is nonzero.
- */
-#define ENABLE_SPI_TRANSACTION 0
-//------------------------------------------------------------------------------
-/**
- * Set ENABLE_SPI_YIELD nonzero to enable release of the SPI bus during
- * SD card busy waits.  
- *
- * This will allow interrupt routines to access the SPI bus if 
- * ENABLE_SPI_TRANSACTION is nonzero.
- * 
- * Setting ENABLE_SPI_YIELD will introduce some extra overhead and will
- * slightly slow transfer rates.  A few older SD cards may fail when 
- * ENABLE_SPI_YIELD is nonzero.
- */
-#define ENABLE_SPI_YIELD 0

+ 6 - 19
SdFat/MinimumSerial.cpp

@@ -19,17 +19,13 @@
  */
 #include <Arduino.h>
 #if defined(UDR0) || defined(DOXYGEN)
-#include <MinimumSerial.h>
+#include "MinimumSerial.h"
+const uint16_t MIN_2X_BAUD = F_CPU/(4*(2*0XFFF + 1)) + 1;
 //------------------------------------------------------------------------------
-/**
- * Set baud rate for serial port zero and enable in non interrupt mode.
- * Do not call this function if you use another serial library.
- * \param[in] baud rate
- */
 void MinimumSerial::begin(uint32_t baud) {
   uint16_t baud_setting;
   // don't worry, the compiler will squeeze out F_CPU != 16000000UL
-  if (F_CPU != 16000000UL || baud != 57600) {
+  if ((F_CPU != 16000000UL || baud != 57600) && baud > MIN_2X_BAUD) {
     // Double the USART Transmission Speed
     UCSR0A = 1 << U2X0;
     baud_setting = (F_CPU / 4 / baud - 1) / 2;
@@ -47,25 +43,16 @@ void MinimumSerial::begin(uint32_t baud) {
   UCSR0B |= (1 << TXEN0) | (1 << RXEN0);
 }
 //------------------------------------------------------------------------------
-/**
- *  Unbuffered read
- *  \return -1 if no character is available or an available character.
- */
 int MinimumSerial::read() {
-  if (UCSR0A & (1 << RXC0)) return UDR0;
+  if (UCSR0A & (1 << RXC0)) {
+    return UDR0;
+  }
   return -1;
 }
 //------------------------------------------------------------------------------
-/**
- * Unbuffered write
- *
- * \param[in] b byte to write.
- * \return 1
- */
 size_t MinimumSerial::write(uint8_t b) {
   while (((1 << UDRIE0) & UCSR0B) || !(UCSR0A & (1 << UDRE0))) {}
   UDR0 = b;
   return 1;
 }
-MinimumSerial MiniSerial;
 #endif  //  defined(UDR0) || defined(DOXYGEN)

+ 17 - 3
SdFat/MinimumSerial.h

@@ -19,18 +19,32 @@
  */
 #ifndef MinimumSerial_h
 #define MinimumSerial_h
+#include <Arduino.h>
+//==============================================================================
 /**
  * \class MinimumSerial
  * \brief mini serial class for the %SdFat library.
  */
 class MinimumSerial : public Print {
  public:
+  /**
+   * Set baud rate for serial port zero and enable in non interrupt mode.
+   * Do not call this function if you use another serial library.
+   * \param[in] baud rate
+   */
   void begin(uint32_t baud);
+  /**
+   *  Unbuffered read
+   *  \return -1 if no character is available or an available character.
+   */
   int read();
+  /**
+   * Unbuffered write
+   *
+   * \param[in] b byte to write.
+   * \return 1
+   */
   size_t write(uint8_t b);
   using Print::write;
 };
-#ifdef UDR0
-extern MinimumSerial MiniSerial;
-#endif  // UDR0
 #endif  // MinimumSerial_h

+ 0 - 202
SdFat/Sd2Card.h

@@ -1,202 +0,0 @@
-/* Arduino Sd2Card Library
- * Copyright (C) 2012 by William Greiman
- *
- * This file is part of the Arduino Sd2Card Library
- *
- * This Library is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This Library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with the Arduino Sd2Card Library.  If not, see
- * <http://www.gnu.org/licenses/>.
- */
-#ifndef SpiCard_h
-#define SpiCard_h
-/**
- * \file
- * \brief Sd2Card class for V2 SD/SDHC cards
- */
-#include <Arduino.h>
-#include <SdFatConfig.h>
-#include <SdInfo.h>
-#include <SdSpi.h>
-//------------------------------------------------------------------------------
-// SD card errors
-/** timeout error for command CMD0 (initialize card in SPI mode) */
-uint8_t const SD_CARD_ERROR_CMD0 = 0X1;
-/** CMD8 was not accepted - not a valid SD card*/
-uint8_t const SD_CARD_ERROR_CMD8 = 0X2;
-/** card returned an error response for CMD12 (stop multiblock read) */
-uint8_t const SD_CARD_ERROR_CMD12 = 0X3;
-/** card returned an error response for CMD17 (read block) */
-uint8_t const SD_CARD_ERROR_CMD17 = 0X4;
-/** card returned an error response for CMD18 (read multiple block) */
-uint8_t const SD_CARD_ERROR_CMD18 = 0X5;
-/** card returned an error response for CMD24 (write block) */
-uint8_t const SD_CARD_ERROR_CMD24 = 0X6;
-/**  WRITE_MULTIPLE_BLOCKS command failed */
-uint8_t const SD_CARD_ERROR_CMD25 = 0X7;
-/** card returned an error response for CMD58 (read OCR) */
-uint8_t const SD_CARD_ERROR_CMD58 = 0X8;
-/** SET_WR_BLK_ERASE_COUNT failed */
-uint8_t const SD_CARD_ERROR_ACMD23 = 0X9;
-/** ACMD41 initialization process timeout */
-uint8_t const SD_CARD_ERROR_ACMD41 = 0XA;
-/** card returned a bad CSR version field */
-uint8_t const SD_CARD_ERROR_BAD_CSD = 0XB;
-/** erase block group command failed */
-uint8_t const SD_CARD_ERROR_ERASE = 0XC;
-/** card not capable of single block erase */
-uint8_t const SD_CARD_ERROR_ERASE_SINGLE_BLOCK = 0XD;
-/** Erase sequence timed out */
-uint8_t const SD_CARD_ERROR_ERASE_TIMEOUT = 0XE;
-/** card returned an error token instead of read data */
-uint8_t const SD_CARD_ERROR_READ = 0XF;
-/** read CID or CSD failed */
-uint8_t const SD_CARD_ERROR_READ_REG = 0X10;
-/** timeout while waiting for start of read data */
-uint8_t const SD_CARD_ERROR_READ_TIMEOUT = 0X11;
-/** card did not accept STOP_TRAN_TOKEN */
-uint8_t const SD_CARD_ERROR_STOP_TRAN = 0X12;
-/** card returned an error token as a response to a write operation */
-uint8_t const SD_CARD_ERROR_WRITE = 0X13;
-/** attempt to write protected block zero */
-uint8_t const SD_CARD_ERROR_WRITE_BLOCK_ZERO = 0X14;  // REMOVE - not used
-/** card did not go ready for a multiple block write */
-uint8_t const SD_CARD_ERROR_WRITE_MULTIPLE = 0X15;
-/** card returned an error to a CMD13 status check after a write */
-uint8_t const SD_CARD_ERROR_WRITE_PROGRAMMING = 0X16;
-/** timeout occurred during write programming */
-uint8_t const SD_CARD_ERROR_WRITE_TIMEOUT = 0X17;
-/** incorrect rate selected */
-uint8_t const SD_CARD_ERROR_SCK_RATE = 0X18;
-/** init() not called */
-uint8_t const SD_CARD_ERROR_INIT_NOT_CALLED = 0X19;
-/** card returned an error for CMD59 (CRC_ON_OFF) */
-uint8_t const SD_CARD_ERROR_CMD59 = 0X1A;
-/** invalid read CRC */
-uint8_t const SD_CARD_ERROR_READ_CRC = 0X1B;
-/** SPI DMA error */
-uint8_t const SD_CARD_ERROR_SPI_DMA = 0X1C;
-//------------------------------------------------------------------------------
-// card types
-/** Standard capacity V1 SD card */
-uint8_t const SD_CARD_TYPE_SD1  = 1;
-/** Standard capacity V2 SD card */
-uint8_t const SD_CARD_TYPE_SD2  = 2;
-/** High Capacity SD card */
-uint8_t const SD_CARD_TYPE_SDHC = 3;
-//------------------------------------------------------------------------------
-/**
- * \class Sd2Card
- * \brief Raw access to SD and SDHC flash memory cards.
- */
-class Sd2Card {
- public:
-  /** Construct an instance of Sd2Card. */
-  Sd2Card() : m_errorCode(SD_CARD_ERROR_INIT_NOT_CALLED), m_type(0) {}
-  bool begin(uint8_t chipSelectPin = SD_CHIP_SELECT_PIN,
-            uint8_t sckDivisor = SPI_FULL_SPEED);
-  uint32_t cardSize();
-  bool erase(uint32_t firstBlock, uint32_t lastBlock);
-  bool eraseSingleBlockEnable();
-  /**
-   *  Set SD error code.
-   *  \param[in] code value for error code.
-   */
-  void error(uint8_t code) {m_errorCode = code;}
-  /**
-   * \return error code for last error. See Sd2Card.h for a list of error codes.
-   */
-  int errorCode() const {return m_errorCode;}
-  /** \return error data for last error. */
-  int errorData() const {return m_status;}
-  /**
- * Initialize an SD flash memory card.
- *
- * \param[in] chipSelectPin SD chip select pin number.
- * \param[in] sckDivisor SPI SCK clock rate divisor.
- *
- * \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.  The reason for failure
- * can be determined by calling errorCode() and errorData().
- */
-  bool init(uint8_t sckDivisor = SPI_FULL_SPEED,
-            uint8_t chipSelectPin = SD_CHIP_SELECT_PIN) {
-    return begin(chipSelectPin, sckDivisor);
-  }
-  bool isBusy();
-  bool readBlock(uint32_t block, uint8_t* dst);
-  /**
-   * Read a card's CID register. The CID contains card identification
-   * information such as Manufacturer ID, Product name, Product serial
-   * number and Manufacturing date. 
-   *
-   * \param[out] cid pointer to area for returned data.
-   *
-   * \return true for success or false for failure.
-   */
-  bool readCID(cid_t* cid) {
-    return readRegister(CMD10, cid);
-  }
-  /**
-   * Read a card's CSD register. The CSD contains Card-Specific Data that
-   * provides information regarding access to the card's contents.
-   *
-   * \param[out] csd pointer to area for returned data.
-   *
-   * \return true for success or false for failure.
-   */
-  bool readCSD(csd_t* csd) {
-    return readRegister(CMD9, csd);
-  }
-  bool readData(uint8_t *dst);
-  bool readOCR(uint32_t* ocr);
-  bool readStart(uint32_t blockNumber);
-  bool readStop();
-  /** Return SCK divisor.
-   *
-   * \return Requested SCK divisor.
-   */
-  uint8_t sckDivisor() {return m_sckDivisor;}
-  /** Return the card type: SD V1, SD V2 or SDHC
-   * \return 0 - SD V1, 1 - SD V2, or 3 - SDHC.
-   */
-  int type() const {return m_type;}
-  bool writeBlock(uint32_t blockNumber, const uint8_t* src);
-  bool writeData(const uint8_t* src);
-  bool writeStart(uint32_t blockNumber, uint32_t eraseCount);
-  bool writeStop();
-
- private:
-  //----------------------------------------------------------------------------
-  // private functions
-  uint8_t cardAcmd(uint8_t cmd, uint32_t arg) {
-    cardCommand(CMD55, 0);
-    return cardCommand(cmd, arg);
-  }
-  uint8_t cardCommand(uint8_t cmd, uint32_t arg);
-  bool readData(uint8_t* dst, size_t count);
-  bool readRegister(uint8_t cmd, void* buf);
-  void chipSelectHigh();
-  void chipSelectLow();
-  void spiYield();
-  void type(uint8_t value) {m_type = value;}
-  bool waitNotBusy(uint16_t timeoutMillis);
-  bool writeData(uint8_t token, const uint8_t* src);
-  // private data
-  static SdSpi m_spi;
-  uint8_t m_chipSelectPin;
-  uint8_t m_errorCode;
-  uint8_t m_sckDivisor;
-  uint8_t m_status;
-  uint8_t m_type;
-};
-#endif  // SpiCard_h

+ 0 - 2024
SdFat/SdBaseFile.cpp

@@ -1,2024 +0,0 @@
-/* Arduino SdFat Library
- * Copyright (C) 2012 by William Greiman
- *
- * This file is part of the Arduino SdFat Library
- *
- * This Library is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This Library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with the Arduino SdFat Library.  If not, see
- * <http://www.gnu.org/licenses/>.
- */
-#include <SdFat.h>
-//------------------------------------------------------------------------------
-// pointer to cwd directory
-SdBaseFile* SdBaseFile::m_cwd = 0;
-// callback function for date/time
-void (*SdBaseFile::m_dateTime)(uint16_t* date, uint16_t* time) = 0;
-//------------------------------------------------------------------------------
-// add a cluster to a file
-bool SdBaseFile::addCluster() {
-  if (!m_vol->allocContiguous(1, &m_curCluster)) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // if first cluster of file link to directory entry
-  if (m_firstCluster == 0) {
-    m_firstCluster = m_curCluster;
-    m_flags |= F_FILE_DIR_DIRTY;
-  }
-  return true;
-
- fail:
-  return false;
-}
-//------------------------------------------------------------------------------
-// Add a cluster to a directory file and zero the cluster.
-// return with first block of cluster in the cache
-cache_t* SdBaseFile::addDirCluster() {
-  uint32_t block;
-  cache_t* pc;
-  // max folder size
-  if (m_fileSize/sizeof(dir_t) >= 0XFFFF) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  if (!addCluster()) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  block = m_vol->clusterStartBlock(m_curCluster);
-  pc = m_vol->cacheFetch(block, SdVolume::CACHE_RESERVE_FOR_WRITE);
-  if (!pc) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  memset(pc, 0, 512);
-  // zero rest of clusters
-  for (uint8_t i = 1; i < m_vol->blocksPerCluster(); i++) {
-    if (!m_vol->writeBlock(block + i, pc->data)) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-  }
-  // Increase directory file size by cluster size
-  m_fileSize += 512UL*m_vol->blocksPerCluster();
-  return pc;
-
- fail:
-  return 0;
-}
-//------------------------------------------------------------------------------
-// cache a file's directory entry
-// return pointer to cached entry or null for failure
-dir_t* SdBaseFile::cacheDirEntry(uint8_t action) {
-  cache_t* pc;
-  pc = m_vol->cacheFetch(m_dirBlock, action);
-  if (!pc) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  return pc->dir + m_dirIndex;
-
- fail:
-  return 0;
-}
-//------------------------------------------------------------------------------
-/** Close a file and force cached data and directory information
- *  to be written to the storage device.
- *
- * \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.
- * Reasons for failure include no file is open or an I/O error.
- */
-bool SdBaseFile::close() {
-  bool rtn = sync();
-  m_type = FAT_FILE_TYPE_CLOSED;
-  return rtn;
-}
-//------------------------------------------------------------------------------
-/** Check for contiguous file and return its raw block range.
- *
- * \param[out] bgnBlock the first block address for the file.
- * \param[out] endBlock the last  block address for the file.
- *
- * \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.
- * Reasons for failure include file is not contiguous, file has zero length
- * or an I/O error occurred.
- */
-bool SdBaseFile::contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock) {
-  // error if no blocks
-  if (m_firstCluster == 0) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  for (uint32_t c = m_firstCluster; ; c++) {
-    uint32_t next;
-    if (!m_vol->fatGet(c, &next)) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    // check for contiguous
-    if (next != (c + 1)) {
-      // error if not end of chain
-      if (!m_vol->isEOC(next)) {
-        DBG_FAIL_MACRO;
-        goto fail;
-      }
-      *bgnBlock = m_vol->clusterStartBlock(m_firstCluster);
-      *endBlock = m_vol->clusterStartBlock(c)
-                  + m_vol->blocksPerCluster() - 1;
-      return true;
-    }
-  }
-
- fail:
-  return false;
-}
-//------------------------------------------------------------------------------
-/** Create and open a new contiguous file of a specified size.
- *
- * \note This function only supports short DOS 8.3 names.
- * See open() for more information.
- *
- * \param[in] dirFile The directory where the file will be created.
- * \param[in] path A path with a valid DOS 8.3 file name.
- * \param[in] size The desired file size.
- *
- * \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.
- * Reasons for failure include \a path contains
- * an invalid DOS 8.3 file name, the FAT volume has not been initialized,
- * a file is already open, the file already exists, the root
- * directory is full or an I/O error.
- *
- */
-bool SdBaseFile::createContiguous(SdBaseFile* dirFile,
-        const char* path, uint32_t size) {
-  uint32_t count;
-  // don't allow zero length file
-  if (size == 0) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  if (!open(dirFile, path, O_CREAT | O_EXCL | O_RDWR)) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // calculate number of clusters needed
-  count = ((size - 1) >> (m_vol->clusterSizeShift() + 9)) + 1;
-
-  // allocate clusters
-  if (!m_vol->allocContiguous(count, &m_firstCluster)) {
-    remove();
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  m_fileSize = size;
-
-  // insure sync() will update dir entry
-  m_flags |= F_FILE_DIR_DIRTY;
-
-  return sync();
-
- fail:
-  return false;
-}
-//------------------------------------------------------------------------------
-/** Return a file's directory entry.
- *
- * \param[out] dir Location for return of the file's directory entry.
- *
- * \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.
- */
-bool SdBaseFile::dirEntry(dir_t* dir) {
-  dir_t* p;
-  // make sure fields on SD are correct
-  if (!sync()) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // read entry
-  p = cacheDirEntry(SdVolume::CACHE_FOR_READ);
-  if (!p) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // copy to caller's struct
-  memcpy(dir, p, sizeof(dir_t));
-  return true;
-
- fail:
-  return false;
-}
-//------------------------------------------------------------------------------
-/** Format the name field of \a dir into the 13 byte array
- * \a name in standard 8.3 short name format.
- *
- * \param[in] dir The directory structure containing the name.
- * \param[out] name A 13 byte char array for the formatted name.
- */
-void SdBaseFile::dirName(const dir_t& dir, char* name) {
-  uint8_t j = 0;
-  for (uint8_t i = 0; i < 11; i++) {
-    if (dir.name[i] == ' ')continue;
-    if (i == 8) name[j++] = '.';
-    name[j++] = dir.name[i];
-  }
-  name[j] = 0;
-}
-//------------------------------------------------------------------------------
-/** Test for the existence of a file in a directory
- *
- * \param[in] name Name of the file to be tested for.
- *
- * The calling instance must be an open directory file.
- *
- * dirFile.exists("TOFIND.TXT") searches for "TOFIND.TXT" in  the directory
- * dirFile.
- *
- * \return true if the file exists else false.
- */
-bool SdBaseFile::exists(const char* name) {
-  SdBaseFile file;
-  return file.open(this, name, O_READ);
-}
-//------------------------------------------------------------------------------
-/**
- * Get a string from a file.
- *
- * fgets() reads bytes from a file into the array pointed to by \a str, until
- * \a num - 1 bytes are read, or a delimiter is read and transferred to \a str,
- * or end-of-file is encountered. The string is then terminated
- * with a null byte.
- *
- * fgets() deletes CR, '\\r', from the string.  This insures only a '\\n'
- * terminates the string for Windows text files which use CRLF for newline.
- *
- * \param[out] str Pointer to the array where the string is stored.
- * \param[in] num Maximum number of characters to be read
- * (including the final null byte). Usually the length
- * of the array \a str is used.
- * \param[in] delim Optional set of delimiters. The default is "\n".
- *
- * \return For success fgets() returns the length of the string in \a str.
- * If no data is read, fgets() returns zero for EOF or -1 if an error occurred.
- **/
-int16_t SdBaseFile::fgets(char* str, int16_t num, char* delim) {
-  char ch;
-  int16_t n = 0;
-  int16_t r = -1;
-  while ((n + 1) < num && (r = read(&ch, 1)) == 1) {
-    // delete CR
-    if (ch == '\r') continue;
-    str[n++] = ch;
-    if (!delim) {
-      if (ch == '\n') break;
-    } else {
-      if (strchr(delim, ch)) break;
-    }
-  }
-  if (r < 0) {
-    // read error
-    return -1;
-  }
-  str[n] = '\0';
-  return n;
-}
-//------------------------------------------------------------------------------
-/** Get a file's name
- *
- * \param[out] name An array of 13 characters for the file's name.
- *
- * \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.
- */
-bool SdBaseFile::getFilename(char* name) {
-  dir_t* p;
-  if (!isOpen()) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  if (isRoot()) {
-    name[0] = '/';
-    name[1] = '\0';
-    return true;
-  }
-  // cache entry
-  p = cacheDirEntry(SdVolume::CACHE_FOR_READ);
-  if (!p) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // format name
-  dirName(*p, name);
-  return true;
-
- fail:
-  return false;
-}
-//------------------------------------------------------------------------------
-void SdBaseFile::getpos(FatPos_t* pos) {
-  pos->position = m_curPosition;
-  pos->cluster = m_curCluster;
-}
-//------------------------------------------------------------------------------
-// format directory name field from a 8.3 name string
-bool SdBaseFile::make83Name(const char* str, uint8_t* name, const char** ptr) {
-  uint8_t c;
-  uint8_t n = 7;  // max index for part before dot
-  uint8_t i = 0;
-  // blank fill name and extension
-  while (i < 11) name[i++] = ' ';
-  i = 0;
-  while (*str != '\0' && *str != '/') {
-    c = *str++;
-    if (c == '.') {
-      if (n == 10) {
-        // only one dot allowed
-        DBG_FAIL_MACRO;
-        goto fail;
-      }
-      n = 10;  // max index for full 8.3 name
-      i = 8;   // place for extension
-    } else {
-      // illegal FAT characters
-#ifdef __AVR__
-      // store chars in flash
-      PGM_P p = PSTR("|<>^+=?/[];,*\"\\");
-      uint8_t b;
-      while ((b = pgm_read_byte(p++))) if (b == c) {
-        DBG_FAIL_MACRO;
-        goto fail;
-      }
-#else  // __AVR__
-      // store chars in RAM
-      if (strchr("|<>^+=?/[];,*\"\\", c)) {
-        DBG_FAIL_MACRO;
-        goto fail;
-      }
-#endif  // __AVR__
-
-      // check size and only allow ASCII printable characters
-      if (i > n || c < 0X21 || c > 0X7E) {
-        DBG_FAIL_MACRO;
-        goto fail;
-      }
-      // only upper case allowed in 8.3 names - convert lower to upper
-      name[i++] = c < 'a' || c > 'z' ?  c : c + ('A' - 'a');
-    }
-  }
-  *ptr = str;
-  // must have a file name, extension is optional
-  return name[0] != ' ';
-
- fail:
-  return false;
-}
-//------------------------------------------------------------------------------
-/** Make a new directory.
- *
- * \param[in] parent An open SdFat instance for the directory that will contain
- * the new directory.
- *
- * \param[in] path A path with a valid 8.3 DOS name for the new directory.
- *
- * \param[in] pFlag Create missing parent directories if true.
- *
- * \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.
- * Reasons for failure include this file is already open, \a parent is not a
- * directory, \a path is invalid or already exists in \a parent.
- */
-bool SdBaseFile::mkdir(SdBaseFile* parent, const char* path, bool pFlag) {
-  uint8_t dname[11];
-  SdBaseFile dir1, dir2;
-  SdBaseFile* sub = &dir1;
-  SdBaseFile* start = parent;
-
-  if (!parent || isOpen()) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  if (*path == '/') {
-    while (*path == '/') path++;
-    if (!parent->isRoot()) {
-      if (!dir2.openRoot(parent->m_vol)) {
-        DBG_FAIL_MACRO;
-        goto fail;
-      }
-      parent = &dir2;
-    }
-  }
-  while (1) {
-    if (!make83Name(path, dname, &path)) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    while (*path == '/') path++;
-    if (!*path) break;
-    if (!sub->open(parent, dname, O_READ)) {
-      if (!pFlag || !sub->mkdir(parent, dname)) {
-        DBG_FAIL_MACRO;
-        goto fail;
-      }
-    }
-    if (parent != start) parent->close();
-    parent = sub;
-    sub = parent != &dir1 ? &dir1 : &dir2;
-  }
-  return mkdir(parent, dname);
-
- fail:
-  return false;
-}
-//------------------------------------------------------------------------------
-bool SdBaseFile::mkdir(SdBaseFile* parent, const uint8_t dname[11]) {
-  uint32_t block;
-  dir_t d;
-  dir_t* p;
-  cache_t* pc;
-
-  if (!parent->isDir()) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // create a normal file
-  if (!open(parent, dname, O_CREAT | O_EXCL | O_RDWR)) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // convert file to directory
-  m_flags = O_READ;
-  m_type = FAT_FILE_TYPE_SUBDIR;
-
-  // allocate and zero first cluster
-  if (!addDirCluster()) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // force entry to SD
-  if (!sync()) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // cache entry - should already be in cache due to sync() call
-  p = cacheDirEntry(SdVolume::CACHE_FOR_WRITE);
-  if (!p) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // change directory entry  attribute
-  p->attributes = DIR_ATT_DIRECTORY;
-
-  // make entry for '.'
-  memcpy(&d, p, sizeof(d));
-  d.name[0] = '.';
-  for (uint8_t i = 1; i < 11; i++) d.name[i] = ' ';
-
-  // cache block for '.'  and '..'
-  block = m_vol->clusterStartBlock(m_firstCluster);
-  pc = m_vol->cacheFetch(block, SdVolume::CACHE_FOR_WRITE);
-  if (!pc) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // copy '.' to block
-  memcpy(&pc->dir[0], &d, sizeof(d));
-  // make entry for '..'
-  d.name[1] = '.';
-  if (parent->isRoot()) {
-    d.firstClusterLow = 0;
-    d.firstClusterHigh = 0;
-  } else {
-    d.firstClusterLow = parent->m_firstCluster & 0XFFFF;
-    d.firstClusterHigh = parent->m_firstCluster >> 16;
-  }
-  // copy '..' to block
-  memcpy(&pc->dir[1], &d, sizeof(d));
-  // write first block
-  return m_vol->cacheSync();
-
- fail:
-  return false;
-}
-//------------------------------------------------------------------------------
- /** Open a file in the current working directory.
-  *
-  * \param[in] path A path with a valid 8.3 DOS name for a file to be opened.
-  *
-  * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive
-  * OR of open flags. see SdBaseFile::open(SdBaseFile*, const char*, uint8_t).
-  *
-  * \return The value one, true, is returned for success and
-  * the value zero, false, is returned for failure.
-  */
-  bool SdBaseFile::open(const char* path, uint8_t oflag) {
-    return open(m_cwd, path, oflag);
-  }
-//------------------------------------------------------------------------------
-/** Open a file or directory by name.
- *
- * \param[in] dirFile An open SdFat instance for the directory containing the
- * file to be opened.
- *
- * \param[in] path A path with a valid 8.3 DOS name for a file to be opened.
- *
- * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive
- * OR of flags from the following list
- *
- * O_READ - Open for reading.
- *
- * O_RDONLY - Same as O_READ.
- *
- * O_WRITE - Open for writing.
- *
- * O_WRONLY - Same as O_WRITE.
- *
- * O_RDWR - Open for reading and writing.
- *
- * O_APPEND - If set, the file offset shall be set to the end of the
- * file prior to each write.
- *
- * O_AT_END - Set the initial position at the end of the file.
- *
- * O_CREAT - If the file exists, this flag has no effect except as noted
- * under O_EXCL below. Otherwise, the file shall be created
- *
- * O_EXCL - If O_CREAT and O_EXCL are set, open() shall fail if the file exists.
- *
- * O_SYNC - Call sync() after each write.  This flag should not be used with
- * write(uint8_t), write_P(PGM_P), writeln_P(PGM_P), or the Arduino Print class.
- * These functions do character at a time writes so sync() will be called
- * after each byte.
- *
- * O_TRUNC - If the file exists and is a regular file, and the file is
- * successfully opened and is not read only, its length shall be truncated to 0.
- *
- * WARNING: A given file must not be opened by more than one SdBaseFile object
- * or file corruption may occur.
- *
- * \note Directory files must be opened read only.  Write and truncation is
- * not allowed for directory files.
- *
- * \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.
- * Reasons for failure include this file is already open, \a dirFile is not
- * a directory, \a path is invalid, the file does not exist
- * or can't be opened in the access mode specified by oflag.
- */
-bool SdBaseFile::open(SdBaseFile* dirFile, const char* path, uint8_t oflag) {
-  uint8_t dname[11];
-  SdBaseFile dir1, dir2;
-  SdBaseFile *parent = dirFile;
-  SdBaseFile *sub = &dir1;
-
-  if (!dirFile) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // error if already open
-  if (isOpen()) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  if (*path == '/') {
-    while (*path == '/') path++;
-    if (*path == 0) return openRoot(dirFile->m_vol);
-    if (!dirFile->isRoot()) {
-      if (!dir2.openRoot(dirFile->m_vol)) {
-        DBG_FAIL_MACRO;
-        goto fail;
-      }
-      parent = &dir2;
-    }
-  }
-  while (1) {
-    if (!make83Name(path, dname, &path)) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    while (*path == '/') path++;
-    if (!*path) break;
-    if (!sub->open(parent, dname, O_READ)) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    if (parent != dirFile) parent->close();
-    parent = sub;
-    sub = parent != &dir1 ? &dir1 : &dir2;
-  }
-  return open(parent, dname, oflag);
-
- fail:
-  return false;
-}
-//------------------------------------------------------------------------------
-// open with filename in dname
-bool SdBaseFile::open(SdBaseFile* dirFile,
-  const uint8_t dname[11], uint8_t oflag) {
-  cache_t* pc;
-  bool emptyFound = false;
-  bool fileFound = false;
-  uint8_t index;
-  dir_t* p;
-
-  m_vol = dirFile->m_vol;
-
-  dirFile->rewind();
-  // search for file
-
-  while (dirFile->m_curPosition < dirFile->m_fileSize) {
-    // Cache directory block.
-    if (dirFile->read() < 0) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    // Position to to next block
-    dirFile->m_curPosition += 511;
-
-    for (index = 0; index < 16; index++) {
-      p = &m_vol->cacheAddress()->dir[index];
-      if (p->name[0] == DIR_NAME_FREE || p->name[0] == DIR_NAME_DELETED) {
-        // remember first empty slot
-        if (!emptyFound) {
-          m_dirBlock = m_vol->cacheBlockNumber();
-          m_dirIndex = index;
-          emptyFound = true;
-        }
-        // done if no entries follow
-        if (p->name[0] == DIR_NAME_FREE) {
-          goto done;
-        }
-      } else if (!memcmp(dname, p->name, 11)) {
-         fileFound = true;
-         goto done;
-      }
-    }
-  }
- done:
-
-  if (fileFound) {
-    // don't open existing file if O_EXCL
-    if (oflag & O_EXCL) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-  } else {
-    // don't create unless O_CREAT and O_WRITE
-    if (!(oflag & O_CREAT) || !(oflag & O_WRITE)) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    if (emptyFound) {
-      index = m_dirIndex;
-      p = cacheDirEntry(SdVolume::CACHE_FOR_WRITE);
-      if (!p) {
-        DBG_FAIL_MACRO;
-        goto fail;
-      }
-    } else {
-      if (dirFile->m_type == FAT_FILE_TYPE_ROOT_FIXED) {
-        DBG_FAIL_MACRO;
-        goto fail;
-      }
-      // add and zero cluster for dirFile - first cluster is in cache for write
-      pc = dirFile->addDirCluster();
-      if (!pc) {
-        DBG_FAIL_MACRO;
-        goto fail;
-      }
-      // use first entry in cluster
-      p = pc->dir;
-      index = 0;
-    }
-    // initialize as empty file
-    memset(p, 0, sizeof(dir_t));
-    memcpy(p->name, dname, 11);
-
-    // set timestamps
-    if (m_dateTime) {
-      // call user date/time function
-      m_dateTime(&p->creationDate, &p->creationTime);
-    } else {
-      // use default date/time
-      p->creationDate = FAT_DEFAULT_DATE;
-      p->creationTime = FAT_DEFAULT_TIME;
-    }
-    p->lastAccessDate = p->creationDate;
-    p->lastWriteDate = p->creationDate;
-    p->lastWriteTime = p->creationTime;
-
-    // write entry to SD
-    if (!dirFile->m_vol->cacheSync()) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-  }
-  // open entry in cache
-  return openCachedEntry(index, oflag);
-
- fail:
-  return false;
-}
-//------------------------------------------------------------------------------
-/** Open a file by index.
- *
- * \param[in] dirFile An open SdFat instance for the 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 flags O_READ, O_WRITE, O_TRUNC, and O_SYNC.
- *
- * See open() by path for definition of flags.
- * \return true for success or false for failure.
- */
-bool SdBaseFile::open(SdBaseFile* dirFile, uint16_t index, uint8_t oflag) {
-  dir_t* p;
-
-  m_vol = dirFile->m_vol;
-
-  // error if already open
-  if (isOpen() || !dirFile) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-
-  // don't open existing file if O_EXCL - user call error
-  if (oflag & O_EXCL) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // seek to location of entry
-  if (!dirFile->seekSet(32 * index)) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // read entry into cache
-  p = dirFile->readDirCache();
-  if (!p) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // error if empty slot or '.' or '..'
-  if (p->name[0] == DIR_NAME_FREE ||
-      p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // open cached entry
-  return openCachedEntry(index & 0XF, oflag);
-
- fail:
-  return false;
-}
-//------------------------------------------------------------------------------
-// open a cached directory entry. Assumes m_vol is initialized
-bool SdBaseFile::openCachedEntry(uint8_t dirIndex, uint8_t oflag) {
-  // location of entry in cache
-  dir_t* p = &m_vol->cacheAddress()->dir[dirIndex];
-
-  // write or truncate is an error for a directory or read-only file
-  if (p->attributes & (DIR_ATT_READ_ONLY | DIR_ATT_DIRECTORY)) {
-    if (oflag & (O_WRITE | O_TRUNC)) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-  }
-  // remember location of directory entry on SD
-  m_dirBlock = m_vol->cacheBlockNumber();
-  m_dirIndex = dirIndex;
-
-  // copy first cluster number for directory fields
-  m_firstCluster = (uint32_t)p->firstClusterHigh << 16;
-  m_firstCluster |= p->firstClusterLow;
-
-  // make sure it is a normal file or subdirectory
-  if (DIR_IS_FILE(p)) {
-    m_fileSize = p->fileSize;
-    m_type = FAT_FILE_TYPE_NORMAL;
-  } else if (DIR_IS_SUBDIR(p)) {
-    if (!setDirSize()) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    m_type = FAT_FILE_TYPE_SUBDIR;
-  } else {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // save open flags for read/write
-  m_flags = oflag & F_OFLAG;
-
-  // set to start of file
-  m_curCluster = 0;
-  m_curPosition = 0;
-  if ((oflag & O_TRUNC) && !truncate(0)) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  return oflag & O_AT_END ? seekEnd(0) : true;
-
- fail:
-  m_type = FAT_FILE_TYPE_CLOSED;
-  return false;
-}
-//------------------------------------------------------------------------------
-/** Open the next file or subdirectory in a directory.
- *
- * \param[in] dirFile An open SdFat instance for the directory containing the
- * file to be opened.
- *
- * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive
- * OR of flags O_READ, O_WRITE, O_TRUNC, and O_SYNC.
- *
- * See open() by path for definition of flags.
- * \return true for success or false for failure.
- */
-bool SdBaseFile::openNext(SdBaseFile* dirFile, uint8_t oflag) {
-  dir_t* p;
-  uint8_t index;
-
-  if (!dirFile) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // error if already open
-  if (isOpen()) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  m_vol = dirFile->m_vol;
-
-  while (1) {
-    index = 0XF & (dirFile->m_curPosition >> 5);
-
-    // read entry into cache
-    p = dirFile->readDirCache();
-    if (!p) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    // done if last entry
-    if (p->name[0] == DIR_NAME_FREE) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    // skip empty slot or '.' or '..'
-    if (p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') {
-      continue;
-    }
-    // must be file or dir
-    if (DIR_IS_FILE_OR_SUBDIR(p)) {
-      return openCachedEntry(index, oflag);
-    }
-  }
-
- fail:
-  return false;
-}
-//------------------------------------------------------------------------------
-/** Open a directory's parent directory.
- *
- * \param[in] dir Parent of this directory will be opened.  Must not be root.
- *
- * \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.
- */
-bool SdBaseFile::openParent(SdBaseFile* dir) {
-  dir_t entry;
-  dir_t* p;
-  SdBaseFile file;
-  uint32_t c;
-  uint32_t cluster;
-  uint32_t lbn;
-  cache_t* pc;
-  // error if already open or dir is root or dir is not a directory
-  if (isOpen() || !dir || dir->isRoot() || !dir->isDir()) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  m_vol = dir->m_vol;
-  // position to '..'
-  if (!dir->seekSet(32)) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // read '..' entry
-  if (dir->read(&entry, sizeof(entry)) != 32) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // verify it is '..'
-  if (entry.name[0] != '.' || entry.name[1] != '.') {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // start cluster for '..'
-  cluster = entry.firstClusterLow;
-  cluster |= (uint32_t)entry.firstClusterHigh << 16;
-  if (cluster == 0) return openRoot(m_vol);
-  // start block for '..'
-  lbn = m_vol->clusterStartBlock(cluster);
-  // first block of parent dir
-    pc = m_vol->cacheFetch(lbn, SdVolume::CACHE_FOR_READ);
-    if (!pc) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  p = &pc->dir[1];
-  // verify name for '../..'
-  if (p->name[0] != '.' || p->name[1] != '.') {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // '..' is pointer to first cluster of parent. open '../..' to find parent
-  if (p->firstClusterHigh == 0 && p->firstClusterLow == 0) {
-    if (!file.openRoot(dir->volume())) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-  } else {
-    if (!file.openCachedEntry(1, O_READ)) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-  }
-  // search for parent in '../..'
-  do {
-    if (file.readDir(&entry) != 32) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    c = entry.firstClusterLow;
-    c |= (uint32_t)entry.firstClusterHigh << 16;
-  } while (c != cluster);
-  // open parent
-  return open(&file, file.curPosition()/32 - 1, O_READ);
-
- fail:
-  return false;
-}
-//------------------------------------------------------------------------------
-/** Open a volume's root directory.
- *
- * \param[in] vol The FAT volume containing the root directory to be opened.
- *
- * \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.
- * Reasons for failure include the file is already open, the FAT volume has
- * not been initialized or it a FAT12 volume.
- */
-bool SdBaseFile::openRoot(SdVolume* vol) {
-  // error if file is already open
-  if (isOpen()) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  m_vol = vol;
-  if (vol->fatType() == 16 || (FAT12_SUPPORT && vol->fatType() == 12)) {
-    m_type = FAT_FILE_TYPE_ROOT_FIXED;
-    m_firstCluster = 0;
-    m_fileSize = 32 * vol->rootDirEntryCount();
-  } else if (vol->fatType() == 32) {
-    m_type = FAT_FILE_TYPE_ROOT32;
-    m_firstCluster = vol->rootDirStart();
-    if (!setDirSize()) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-  } else {
-    // volume is not initialized, invalid, or FAT12 without support
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // read only
-  m_flags = O_READ;
-
-  // set to start of file
-  m_curCluster = 0;
-  m_curPosition = 0;
-
-  // root has no directory entry
-  m_dirBlock = 0;
-  m_dirIndex = 0;
-  return true;
-
- fail:
-  return false;
-}
-//------------------------------------------------------------------------------
-/** Return the next available byte without consuming it.
- *
- * \return The byte if no error and not at eof else -1;
- */
-int SdBaseFile::peek() {
-  FatPos_t pos;
-  getpos(&pos);
-  int c = read();
-  if (c >= 0) setpos(&pos);
-  return c;
-}
-//------------------------------------------------------------------------------
-/** Read the next byte from a file.
- *
- * \return For success read returns the next byte in the file as an int.
- * If an error occurs or end of file is reached -1 is returned.
- */
-int16_t SdBaseFile::read() {
-  uint8_t b;
-  return read(&b, 1) == 1 ? b : -1;
-}
-//------------------------------------------------------------------------------
-/** Read data from a file starting at the current position.
- *
- * \param[out] buf Pointer to the location that will receive the data.
- *
- * \param[in] nbyte Maximum number of bytes to read.
- *
- * \return For success read() returns the number of bytes read.
- * A value less than \a nbyte, including zero, will be returned
- * if end of file is reached.
- * If an error occurs, read() returns -1.  Possible errors include
- * read() called before a file has been opened, corrupt file system
- * or an I/O error occurred.
- */
-int SdBaseFile::read(void* buf, size_t nbyte) {
-  uint8_t blockOfCluster;
-  uint8_t* dst = reinterpret_cast<uint8_t*>(buf);
-  uint16_t offset;
-  size_t toRead;
-  uint32_t block;  // raw device block number
-  cache_t* pc;
-
-  // error if not open or write only
-  if (!isOpen() || !(m_flags & O_READ)) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // max bytes left in file
-  if (nbyte >= (m_fileSize - m_curPosition)) {
-    nbyte = m_fileSize - m_curPosition;
-  }
-  // amount left to read
-  toRead = nbyte;
-  while (toRead > 0) {
-    size_t n;
-    offset = m_curPosition & 0X1FF;  // offset in block
-    blockOfCluster = m_vol->blockOfCluster(m_curPosition);
-    if (m_type == FAT_FILE_TYPE_ROOT_FIXED) {
-      block = m_vol->rootDirStart() + (m_curPosition >> 9);
-    } else {
-      if (offset == 0 && blockOfCluster == 0) {
-        // start of new cluster
-        if (m_curPosition == 0) {
-          // use first cluster in file
-          m_curCluster = m_firstCluster;
-        } else {
-          // get next cluster from FAT
-          if (!m_vol->fatGet(m_curCluster, &m_curCluster)) {
-            DBG_FAIL_MACRO;
-            goto fail;
-          }
-        }
-      }
-
-      block = m_vol->clusterStartBlock(m_curCluster) + blockOfCluster;
-    }
-    if (offset != 0 || toRead < 512 || block == m_vol->cacheBlockNumber()) {
-      // amount to be read from current block
-      n = 512 - offset;
-      if (n > toRead) n = toRead;
-      // read block to cache and copy data to caller
-      pc = m_vol->cacheFetch(block, SdVolume::CACHE_FOR_READ);
-      if (!pc) {
-        DBG_FAIL_MACRO;
-        goto fail;
-      }
-      uint8_t* src = pc->data + offset;
-      memcpy(dst, src, n);
-    } else if (!USE_MULTI_BLOCK_SD_IO || toRead < 1024) {
-      // read single block
-      n = 512;
-      if (!m_vol->readBlock(block, dst)) {
-        DBG_FAIL_MACRO;
-        goto fail;
-      }
-    } else {
-      uint8_t nb = toRead >> 9;
-      if (m_type != FAT_FILE_TYPE_ROOT_FIXED) {
-        uint8_t mb = m_vol->blocksPerCluster() - blockOfCluster;
-        if (mb < nb) nb = mb;
-      }
-      n = 512*nb;
-      if (m_vol->cacheBlockNumber() <= block
-        && block < (m_vol->cacheBlockNumber() + nb)) {
-        // flush cache if a block is in the cache
-        if (!m_vol->cacheSync()) {
-          DBG_FAIL_MACRO;
-          goto fail;
-        }
-      }
-      if (!m_vol->sdCard()->readStart(block)) {
-        DBG_FAIL_MACRO;
-        goto fail;
-      }
-      for (uint8_t b = 0; b < nb; b++) {
-        if (!m_vol->sdCard()->readData(dst + b*512)) {
-          DBG_FAIL_MACRO;
-          goto fail;
-        }
-      }
-      if (!m_vol->sdCard()->readStop()) {
-        DBG_FAIL_MACRO;
-        goto fail;
-      }
-    }
-    dst += n;
-    m_curPosition += n;
-    toRead -= n;
-  }
-  return nbyte;
-
- fail:
-  return -1;
-}
-//------------------------------------------------------------------------------
-/** Read the next directory entry from a directory file.
- *
- * \param[out] dir The dir_t struct that will receive the data.
- *
- * \return For success readDir() returns the number of bytes read.
- * A value of zero will be returned if end of file is reached.
- * If an error occurs, readDir() returns -1.  Possible errors include
- * readDir() called before a directory has been opened, this is not
- * a directory file or an I/O error occurred.
- */
-int8_t SdBaseFile::readDir(dir_t* dir) {
-  int16_t n;
-  // if not a directory file or miss-positioned return an error
-  if (!isDir() || (0X1F & m_curPosition)) return -1;
-
-  while (1) {
-    n = read(dir, sizeof(dir_t));
-    if (n != sizeof(dir_t)) return n == 0 ? 0 : -1;
-    // last entry if DIR_NAME_FREE
-    if (dir->name[0] == DIR_NAME_FREE) return 0;
-    // skip empty entries and entry for .  and ..
-    if (dir->name[0] == DIR_NAME_DELETED || dir->name[0] == '.') continue;
-    // return if normal file or subdirectory
-    if (DIR_IS_FILE_OR_SUBDIR(dir)) return n;
-  }
-}
-//------------------------------------------------------------------------------
-// Read next directory entry into the cache
-// Assumes file is correctly positioned
-dir_t* SdBaseFile::readDirCache() {
-  uint8_t i;
-  // error if not directory
-  if (!isDir()) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // index of entry in cache
-  i = (m_curPosition >> 5) & 0XF;
-
-  // use read to locate and cache block
-  if (read() < 0) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // advance to next entry
-  m_curPosition += 31;
-
-  // return pointer to entry
-  return m_vol->cacheAddress()->dir + i;
-
- fail:
-  return 0;
-}
-//------------------------------------------------------------------------------
-/** Remove a file.
- *
- * The directory entry and all data for the file are deleted.
- *
- * \note This function should not be used to delete the 8.3 version of a
- * file that has a long name. For example if a file has the long name
- * "New Text Document.txt" you should not delete the 8.3 name "NEWTEX~1.TXT".
- *
- * \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.
- * Reasons for failure include the file read-only, is a directory,
- * or an I/O error occurred.
- */
-bool SdBaseFile::remove() {
-  dir_t* d;
-  // free any clusters - will fail if read-only or directory
-  if (!truncate(0)) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // cache directory entry
-  d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE);
-  if (!d) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // mark entry deleted
-  d->name[0] = DIR_NAME_DELETED;
-
-  // set this file closed
-  m_type = FAT_FILE_TYPE_CLOSED;
-
-  // write entry to SD
-  return m_vol->cacheSync();
-  return true;
-
- fail:
-  return false;
-}
-//------------------------------------------------------------------------------
-/** Remove a file.
- *
- * The directory entry and all data for the file are deleted.
- *
- * \param[in] dirFile The directory that contains the file.
- * \param[in] path Path for the file to be removed.
- *
- * \note This function should not be used to delete the 8.3 version of a
- * file that has a long name. For example if a file has the long name
- * "New Text Document.txt" you should not delete the 8.3 name "NEWTEX~1.TXT".
- *
- * \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.
- * Reasons for failure include the file is a directory, is read only,
- * \a dirFile is not a directory, \a path is not found
- * or an I/O error occurred.
- */
-bool SdBaseFile::remove(SdBaseFile* dirFile, const char* path) {
-  SdBaseFile file;
-  if (!file.open(dirFile, path, O_WRITE)) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  return file.remove();
-
- fail:
-  return false;
-}
-//------------------------------------------------------------------------------
-/** Rename a file or subdirectory.
- *
- * \param[in] dirFile Directory for the new path.
- * \param[in] newPath New path name for the file/directory.
- *
- * \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.
- * Reasons for failure include \a dirFile is not open or is not a directory
- * file, newPath is invalid or already exists, or an I/O error occurs.
- */
-bool SdBaseFile::rename(SdBaseFile* dirFile, const char* newPath) {
-  dir_t entry;
-  uint32_t dirCluster = 0;
-  SdBaseFile file;
-  cache_t* pc;
-  dir_t* d;
-
-  // must be an open file or subdirectory
-  if (!(isFile() || isSubDir())) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // can't move file
-  if (m_vol != dirFile->m_vol) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // sync() and cache directory entry
-  sync();
-  d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE);
-  if (!d) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // save directory entry
-  memcpy(&entry, d, sizeof(entry));
-
-  // mark entry deleted
-  d->name[0] = DIR_NAME_DELETED;
-
-  // make directory entry for new path
-  if (isFile()) {
-    if (!file.open(dirFile, newPath, O_CREAT | O_EXCL | O_WRITE)) {
-      goto restore;
-    }
-  } else {
-    // don't create missing path prefix components
-    if (!file.mkdir(dirFile, newPath, false)) {
-      goto restore;
-    }
-    // save cluster containing new dot dot
-    dirCluster = file.m_firstCluster;
-  }
-  // change to new directory entry
-  m_dirBlock = file.m_dirBlock;
-  m_dirIndex = file.m_dirIndex;
-
-  // mark closed to avoid possible destructor close call
-  file.m_type = FAT_FILE_TYPE_CLOSED;
-
-  // cache new directory entry
-  d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE);
-  if (!d) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // copy all but name field to new directory entry
-  memcpy(&d->attributes, &entry.attributes, sizeof(entry) - sizeof(d->name));
-
-  // update dot dot if directory
-  if (dirCluster) {
-    // get new dot dot
-    uint32_t block = m_vol->clusterStartBlock(dirCluster);
-    pc = m_vol->cacheFetch(block, SdVolume::CACHE_FOR_READ);
-    if (!pc) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    memcpy(&entry, &pc->dir[1], sizeof(entry));
-
-    // free unused cluster
-    if (!m_vol->freeChain(dirCluster)) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    // store new dot dot
-    block = m_vol->clusterStartBlock(m_firstCluster);
-    pc = m_vol->cacheFetch(block, SdVolume::CACHE_FOR_WRITE);
-    if (!pc) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    memcpy(&pc->dir[1], &entry, sizeof(entry));
-  }
-  return m_vol->cacheSync();
-
- restore:
-  d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE);
-  if (!d) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // restore entry
-  d->name[0] = entry.name[0];
-  m_vol->cacheSync();
-
- fail:
-  return false;
-}
-//------------------------------------------------------------------------------
-/** Remove a directory file.
- *
- * The directory file will be removed only if it is empty and is not the
- * root directory.  rmdir() follows DOS and Windows and ignores the
- * read-only attribute for the directory.
- *
- * \note This function should not be used to delete the 8.3 version of a
- * directory that has a long name. For example if a directory has the
- * long name "New folder" you should not delete the 8.3 name "NEWFOL~1".
- *
- * \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.
- * Reasons for failure include the file is not a directory, is the root
- * directory, is not empty, or an I/O error occurred.
- */
-bool SdBaseFile::rmdir() {
-  // must be open subdirectory
-  if (!isSubDir()) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  rewind();
-
-  // make sure directory is empty
-  while (m_curPosition < m_fileSize) {
-    dir_t* p = readDirCache();
-    if (!p) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    // done if past last used entry
-    if (p->name[0] == DIR_NAME_FREE) break;
-    // skip empty slot, '.' or '..'
-    if (p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') continue;
-    // error not empty
-    if (DIR_IS_FILE_OR_SUBDIR(p)) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-  }
-  // convert empty directory to normal file for remove
-  m_type = FAT_FILE_TYPE_NORMAL;
-  m_flags |= O_WRITE;
-  return remove();
-
- fail:
-  return false;
-}
-//------------------------------------------------------------------------------
-/** Recursively delete a directory and all contained files.
- *
- * This is like the Unix/Linux 'rm -rf *' if called with the root directory
- * hence the name.
- *
- * Warning - This will remove all contents of the directory including
- * subdirectories.  The directory will then be removed if it is not root.
- * The read-only attribute for files will be ignored.
- *
- * \note This function should not be used to delete the 8.3 version of
- * a directory that has a long name.  See remove() and rmdir().
- *
- * \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.
- */
-bool SdBaseFile::rmRfStar() {
-  uint16_t index;
-  SdBaseFile f;
-  rewind();
-  while (m_curPosition < m_fileSize) {
-    // remember position
-    index = m_curPosition/32;
-
-    dir_t* p = readDirCache();
-    if (!p) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    // done if past last entry
-    if (p->name[0] == DIR_NAME_FREE) break;
-
-    // skip empty slot or '.' or '..'
-    if (p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') continue;
-
-    // skip if part of long file name or volume label in root
-    if (!DIR_IS_FILE_OR_SUBDIR(p)) continue;
-
-    if (!f.open(this, index, O_READ)) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    if (f.isSubDir()) {
-      // recursively delete
-      if (!f.rmRfStar()) {
-        DBG_FAIL_MACRO;
-        goto fail;
-      }
-    } else {
-      // ignore read-only
-      f.m_flags |= O_WRITE;
-      if (!f.remove()) {
-        DBG_FAIL_MACRO;
-        goto fail;
-      }
-    }
-    // position to next entry if required
-    if (m_curPosition != (32UL*(index + 1))) {
-      if (!seekSet(32UL*(index + 1))) {
-        DBG_FAIL_MACRO;
-        goto fail;
-      }
-    }
-  }
-  // don't try to delete root
-  if (!isRoot()) {
-    if (!rmdir()) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-  }
-  return true;
-
- fail:
-  return false;
-}
-//------------------------------------------------------------------------------
-/**  Create a file object and open it in the current working directory.
- *
- * \param[in] path A path with a valid 8.3 DOS name for a file to be opened.
- *
- * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive
- * OR of open flags. see SdBaseFile::open(SdBaseFile*, const char*, uint8_t).
- */
-SdBaseFile::SdBaseFile(const char* path, uint8_t oflag) {
-  m_type = FAT_FILE_TYPE_CLOSED;
-  writeError = false;
-  open(path, oflag);
-}
-//------------------------------------------------------------------------------
-/** Sets a file's position.
- *
- * \param[in] pos The new position in bytes from the beginning of the file.
- *
- * \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.
- */
-bool SdBaseFile::seekSet(uint32_t pos) {
-  uint32_t nCur;
-  uint32_t nNew;
-  // error if file not open or seek past end of file
-  if (!isOpen() || pos > m_fileSize) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  if (m_type == FAT_FILE_TYPE_ROOT_FIXED) {
-    m_curPosition = pos;
-    goto done;
-  }
-  if (pos == 0) {
-    // set position to start of file
-    m_curCluster = 0;
-    m_curPosition = 0;
-    goto done;
-  }
-  // calculate cluster index for cur and new position
-  nCur = (m_curPosition - 1) >> (m_vol->clusterSizeShift() + 9);
-  nNew = (pos - 1) >> (m_vol->clusterSizeShift() + 9);
-
-  if (nNew < nCur || m_curPosition == 0) {
-    // must follow chain from first cluster
-    m_curCluster = m_firstCluster;
-  } else {
-    // advance from curPosition
-    nNew -= nCur;
-  }
-  while (nNew--) {
-    if (!m_vol->fatGet(m_curCluster, &m_curCluster)) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-  }
-  m_curPosition = pos;
-
- done:
-  return true;
-
- fail:
-  return false;
-}
-//------------------------------------------------------------------------------
-// set m_fileSize for a directory
-bool SdBaseFile::setDirSize() {
-  uint16_t s = 0;
-  uint32_t cluster = m_firstCluster;
-  do {
-    if (!m_vol->fatGet(cluster, &cluster)) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    s += m_vol->blocksPerCluster();
-    // max size if a directory file is 4096 blocks
-    if (s >= 4096) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-  } while (!m_vol->isEOC(cluster));
-  m_fileSize = 512L*s;
-  return true;
-
- fail:
-  return false;
-}
-//------------------------------------------------------------------------------
-void SdBaseFile::setpos(FatPos_t* pos) {
-  m_curPosition = pos->position;
-  m_curCluster = pos->cluster;
-}
-//------------------------------------------------------------------------------
-/** The sync() call causes all modified data and directory fields
- * to be written to the storage device.
- *
- * \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.
- * Reasons for failure include a call to sync() before a file has been
- * opened or an I/O error.
- */
-bool SdBaseFile::sync() {
-  // only allow open files and directories
-  if (!isOpen()) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  if (m_flags & F_FILE_DIR_DIRTY) {
-    dir_t* d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE);
-    // check for deleted by another open file object
-    if (!d || d->name[0] == DIR_NAME_DELETED) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    // do not set filesize for dir files
-    if (!isDir()) d->fileSize = m_fileSize;
-
-    // update first cluster fields
-    d->firstClusterLow = m_firstCluster & 0XFFFF;
-    d->firstClusterHigh = m_firstCluster >> 16;
-
-    // set modify time if user supplied a callback date/time function
-    if (m_dateTime) {
-      m_dateTime(&d->lastWriteDate, &d->lastWriteTime);
-      d->lastAccessDate = d->lastWriteDate;
-    }
-    // clear directory dirty
-    m_flags &= ~F_FILE_DIR_DIRTY;
-  }
-  return m_vol->cacheSync();
-
- fail:
-  writeError = true;
-  return false;
-}
-//------------------------------------------------------------------------------
-/** Copy a file's timestamps
- *
- * \param[in] file File to copy timestamps from.
- *
- * \note
- * Modify and access timestamps may be overwritten if a date time callback
- * function has been set by dateTimeCallback().
- *
- * \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.
- */
-bool SdBaseFile::timestamp(SdBaseFile* file) {
-  dir_t* d;
-  dir_t dir;
-
-  // get timestamps
-  if (!file->dirEntry(&dir)) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // update directory fields
-  if (!sync()) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE);
-  if (!d) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // copy timestamps
-  d->lastAccessDate = dir.lastAccessDate;
-  d->creationDate = dir.creationDate;
-  d->creationTime = dir.creationTime;
-  d->creationTimeTenths = dir.creationTimeTenths;
-  d->lastWriteDate = dir.lastWriteDate;
-  d->lastWriteTime = dir.lastWriteTime;
-
-  // write back entry
-  return m_vol->cacheSync();
-
- fail:
-  return false;
-}
-//------------------------------------------------------------------------------
-/** Set a file's timestamps in its directory entry.
- *
- * \param[in] flags Values for \a flags are constructed by a bitwise-inclusive
- * OR of flags from the following list
- *
- * T_ACCESS - Set the file's last access date.
- *
- * T_CREATE - Set the file's creation date and time.
- *
- * T_WRITE - Set the file's last write/modification date and time.
- *
- * \param[in] year Valid range 1980 - 2107 inclusive.
- *
- * \param[in] month Valid range 1 - 12 inclusive.
- *
- * \param[in] day Valid range 1 - 31 inclusive.
- *
- * \param[in] hour Valid range 0 - 23 inclusive.
- *
- * \param[in] minute Valid range 0 - 59 inclusive.
- *
- * \param[in] second Valid range 0 - 59 inclusive
- *
- * \note It is possible to set an invalid date since there is no check for
- * the number of days in a month.
- *
- * \note
- * Modify and access timestamps may be overwritten if a date time callback
- * function has been set by dateTimeCallback().
- *
- * \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.
- */
-bool SdBaseFile::timestamp(uint8_t flags, uint16_t year, uint8_t month,
-         uint8_t day, uint8_t hour, uint8_t minute, uint8_t second) {
-  uint16_t dirDate;
-  uint16_t dirTime;
-  dir_t* d;
-
-  if (!isOpen()
-    || year < 1980
-    || year > 2107
-    || month < 1
-    || month > 12
-    || day < 1
-    || day > 31
-    || hour > 23
-    || minute > 59
-    || second > 59) {
-      DBG_FAIL_MACRO;
-      goto fail;
-  }
-  // update directory entry
-  if (!sync()) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE);
-  if (!d) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  dirDate = FAT_DATE(year, month, day);
-  dirTime = FAT_TIME(hour, minute, second);
-  if (flags & T_ACCESS) {
-    d->lastAccessDate = dirDate;
-  }
-  if (flags & T_CREATE) {
-    d->creationDate = dirDate;
-    d->creationTime = dirTime;
-    // seems to be units of 1/100 second not 1/10 as Microsoft states
-    d->creationTimeTenths = second & 1 ? 100 : 0;
-  }
-  if (flags & T_WRITE) {
-    d->lastWriteDate = dirDate;
-    d->lastWriteTime = dirTime;
-  }
-  return m_vol->cacheSync();
-
- fail:
-  return false;
-}
-//------------------------------------------------------------------------------
-/** Truncate a file to a specified length.  The current file position
- * will be maintained if it is less than or equal to \a length otherwise
- * it will be set to end of file.
- *
- * \param[in] length The desired length for the file.
- *
- * \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.
- * Reasons for failure include file is read only, file is a directory,
- * \a length is greater than the current file size or an I/O error occurs.
- */
-bool SdBaseFile::truncate(uint32_t length) {
-  uint32_t newPos;
-  // error if not a normal file or read-only
-  if (!isFile() || !(m_flags & O_WRITE)) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // error if length is greater than current size
-  if (length > m_fileSize) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // fileSize and length are zero - nothing to do
-  if (m_fileSize == 0) return true;
-
-  // remember position for seek after truncation
-  newPos = m_curPosition > length ? length : m_curPosition;
-
-  // position to last cluster in truncated file
-  if (!seekSet(length)) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  if (length == 0) {
-    // free all clusters
-    if (!m_vol->freeChain(m_firstCluster)) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    m_firstCluster = 0;
-  } else {
-    uint32_t toFree;
-    if (!m_vol->fatGet(m_curCluster, &toFree)) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    if (!m_vol->isEOC(toFree)) {
-      // free extra clusters
-      if (!m_vol->freeChain(toFree)) {
-        DBG_FAIL_MACRO;
-        goto fail;
-      }
-      // current cluster is end of chain
-      if (!m_vol->fatPutEOC(m_curCluster)) {
-        DBG_FAIL_MACRO;
-        goto fail;
-      }
-    }
-  }
-  m_fileSize = length;
-
-  // need to update directory entry
-  m_flags |= F_FILE_DIR_DIRTY;
-
-  if (!sync()) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // set file to correct position
-  return seekSet(newPos);
-
- fail:
-  return false;
-}
-//------------------------------------------------------------------------------
-/** Write data to an open file.
- *
- * \note Data is moved to the cache but may not be written to the
- * storage device until sync() is called.
- *
- * \param[in] buf Pointer to the location of the data to be written.
- *
- * \param[in] nbyte Number of bytes to write.
- *
- * \return For success write() returns the number of bytes written, always
- * \a nbyte.  If an error occurs, write() returns -1.  Possible errors
- * include write() is called before a file has been opened, write is called
- * for a read-only file, device is full, a corrupt file system or an I/O error.
- *
- */
-int SdBaseFile::write(const void* buf, size_t nbyte) {
-  // convert void* to uint8_t*  -  must be before goto statements
-  const uint8_t* src = reinterpret_cast<const uint8_t*>(buf);
-  cache_t* pc;
-  uint8_t cacheOption;
-  // number of bytes left to write  -  must be before goto statements
-  size_t nToWrite = nbyte;
-  size_t n;
-  // error if not a normal file or is read-only
-  if (!isFile() || !(m_flags & O_WRITE)) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // seek to end of file if append flag
-  if ((m_flags & O_APPEND) && m_curPosition != m_fileSize) {
-    if (!seekEnd()) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-  }
-  // Don't exceed max fileSize.
-  if (nbyte > (0XFFFFFFFF - m_curPosition)) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  while (nToWrite) {
-    uint8_t blockOfCluster = m_vol->blockOfCluster(m_curPosition);
-    uint16_t blockOffset = m_curPosition & 0X1FF;
-    if (blockOfCluster == 0 && blockOffset == 0) {
-      // start of new cluster
-      if (m_curCluster != 0) {
-        uint32_t next;
-        if (!m_vol->fatGet(m_curCluster, &next)) {
-          DBG_FAIL_MACRO;
-          goto fail;
-        }
-        if (m_vol->isEOC(next)) {
-          // add cluster if at end of chain
-          if (!addCluster()) {
-            DBG_FAIL_MACRO;
-            goto fail;
-          }
-        } else {
-          m_curCluster = next;
-        }
-      } else {
-        if (m_firstCluster == 0) {
-          // allocate first cluster of file
-          if (!addCluster()) {
-            DBG_FAIL_MACRO;
-            goto fail;
-          }
-        } else {
-          m_curCluster = m_firstCluster;
-        }
-      }
-    }
-    // block for data write
-    uint32_t block = m_vol->clusterStartBlock(m_curCluster) + blockOfCluster;
-
-    if (blockOffset != 0 || nToWrite < 512) {
-      // partial block - must use cache
-     if (blockOffset == 0 && m_curPosition >= m_fileSize) {
-        // start of new block don't need to read into cache
-        cacheOption = SdVolume::CACHE_RESERVE_FOR_WRITE;
-      } else {
-        // rewrite part of block
-        cacheOption = SdVolume::CACHE_FOR_WRITE;
-      }
-      pc = m_vol->cacheFetch(block, cacheOption);
-      if (!pc) {
-        DBG_FAIL_MACRO;
-        goto fail;
-      }
-
-      // max space in block
-      uint16_t space = 512 - blockOffset;
-
-      // lesser of space and amount to write
-      n = space < nToWrite ? space : nToWrite;
-
-      uint8_t* dst = pc->data + blockOffset;
-      memcpy(dst, src, n);
-
-      // flush cache if all space used.
-      if (n == space) {
-        if (!m_vol->cacheWriteData()) {
-          DBG_FAIL_MACRO;
-          goto fail;
-        }
-      }
-
-    } else if (!USE_MULTI_BLOCK_SD_IO || nToWrite < 1024) {
-      // use single block write command
-      n = 512;
-      if (m_vol->cacheBlockNumber() == block) {
-        m_vol->cacheInvalidate();
-      }
-      if (!m_vol->writeBlock(block, src)) {
-        DBG_FAIL_MACRO;
-        goto fail;
-      }
-    } else {
-      // use multiple block write command
-      uint8_t maxBlocks = m_vol->blocksPerCluster() - blockOfCluster;
-      uint8_t nBlock = nToWrite >> 9;
-      if (nBlock > maxBlocks) nBlock = maxBlocks;
-
-      n = 512*nBlock;
-      if (!m_vol->sdCard()->writeStart(block, nBlock)) {
-        DBG_FAIL_MACRO;
-        goto fail;
-      }
-      for (uint8_t b = 0; b < nBlock; b++) {
-        // invalidate cache if block is in cache
-        if ((block + b) == m_vol->cacheBlockNumber()) {
-          m_vol->cacheInvalidate();
-        }
-        if (!m_vol->sdCard()->writeData(src + 512*b)) {
-          DBG_FAIL_MACRO;
-          goto fail;
-        }
-      }
-      if (!m_vol->sdCard()->writeStop()) {
-        DBG_FAIL_MACRO;
-        goto fail;
-      }
-    }
-    m_curPosition += n;
-    src += n;
-    nToWrite -= n;
-  }
-  if (m_curPosition > m_fileSize) {
-    // update fileSize and insure sync will update dir entry
-    m_fileSize = m_curPosition;
-    m_flags |= F_FILE_DIR_DIRTY;
-  } else if (m_dateTime && nbyte) {
-    // insure sync will update modified date and time
-    m_flags |= F_FILE_DIR_DIRTY;
-  }
-
-  if (m_flags & O_SYNC) {
-    if (!sync()) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-  }
-  return nbyte;
-
- fail:
-  // return for write error
-  writeError = true;
-  return -1;
-}

+ 0 - 289
SdFat/SdBaseFile.h

@@ -1,289 +0,0 @@
-/* Arduino SdFat Library
- * Copyright (C) 2012 by William Greiman
- *
- * This file is part of the Arduino SdFat Library
- *
- * This Library is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This Library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with the Arduino SdFat Library.  If not, see
- * <http://www.gnu.org/licenses/>.
- */
-#ifndef SdBaseFile_h
-#define SdBaseFile_h
-/**
- * \file
- * \brief SdBaseFile class
- */
-#ifdef __AVR__
-#include <avr/pgmspace.h>
-#else  // __AVR__
-#ifndef PGM_P
-/** pointer to flash for ARM */
-#define PGM_P const char*
-#endif  // PGM_P
-#ifndef PSTR
-/** store literal string in flash for ARM */
-#define PSTR(x) (x)
-#endif  // PSTR
-#ifndef pgm_read_byte
-/** read 8-bits from flash for ARM */
-#define pgm_read_byte(addr) (*(const unsigned char*)(addr))
-#endif  // pgm_read_byte
-#ifndef pgm_read_word
-/** read 16-bits from flash for ARM */
-#define pgm_read_word(addr) (*(const uint16_t*)(addr))
-#endif  // pgm_read_word
-#ifndef PROGMEM
-/** store in flash for ARM */
-#define PROGMEM const
-#endif  // PROGMEM
-#endif  // __AVR__
-#include <Arduino.h>
-#include <SdFatConfig.h>
-#include <SdVolume.h>
-#include <utility/FatApiConstants.h>
-//------------------------------------------------------------------------------
-/**
- * \struct FatPos_t
- * \brief internal type for istream
- * do not use in user apps
- */
-struct FatPos_t {
-  /** stream position */
-  uint32_t position;
-  /** cluster for position */
-  uint32_t cluster;
-  FatPos_t() : position(0), cluster(0) {}
-};
-
-// values for m_type
-/** This file has not been opened. */
-uint8_t const FAT_FILE_TYPE_CLOSED = 0;
-/** A normal file */
-uint8_t const FAT_FILE_TYPE_NORMAL = 1;
-/** A FAT12 or FAT16 root directory */
-uint8_t const FAT_FILE_TYPE_ROOT_FIXED = 2;
-/** A FAT32 root directory */
-uint8_t const FAT_FILE_TYPE_ROOT32 = 3;
-/** A subdirectory file*/
-uint8_t const FAT_FILE_TYPE_SUBDIR = 4;
-/** Test value for directory type */
-uint8_t const FAT_FILE_TYPE_MIN_DIR = FAT_FILE_TYPE_ROOT_FIXED;
-
-//------------------------------------------------------------------------------
-/**
- * \class SdBaseFile
- * \brief Base class for SdFile with Print and C++ streams.
- */
-class SdBaseFile {
- public:
-  /** Create an instance. */
-  SdBaseFile() : writeError(false), m_type(FAT_FILE_TYPE_CLOSED) {}
-  SdBaseFile(const char* path, uint8_t oflag);
-#if DESTRUCTOR_CLOSES_FILE
-  ~SdBaseFile() {if(isOpen()) close();}
-#endif  // DESTRUCTOR_CLOSES_FILE
-  /**
-   * writeError is set to true if an error occurs during a write().
-   * Set writeError to false before calling print() and/or write() and check
-   * for true after calls to print() and/or write().
-   */
-  bool writeError;
-  /** \return value of writeError */
-  bool getWriteError() {return writeError;}
-  /** Set writeError to zero */
-  void clearWriteError() {writeError = 0;}
-  //----------------------------------------------------------------------------
-  // helpers for stream classes
-  /** get position for streams
-   * \param[out] pos struct to receive position
-   */
-  void getpos(FatPos_t* pos);
-  /** set position for streams
-   * \param[out] pos struct with value for new position
-   */
-  void setpos(FatPos_t* pos);
-  //----------------------------------------------------------------------------
-  /** \return number of bytes available from yhe current position to EOF */
-  uint32_t available() {return fileSize() - curPosition();}
-  bool close();
-  bool contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock);
-  bool createContiguous(SdBaseFile* dirFile,
-          const char* path, uint32_t size);
-  /** \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. */
-  uint32_t curPosition() const {return m_curPosition;}
-  /** \return Current working directory */
-  static SdBaseFile* cwd() {return m_cwd;}
-  /** Set the date/time callback function
-   *
-   * \param[in] dateTime The user's call back function.  The callback
-   * function is of the form:
-   *
-   * \code
-   * void dateTime(uint16_t* date, uint16_t* time) {
-   *   uint16_t year;
-   *   uint8_t month, day, hour, minute, second;
-   *
-   *   // User gets date and time from GPS or real-time clock here
-   *
-   *   // return date using FAT_DATE macro to format fields
-   *   *date = FAT_DATE(year, month, day);
-   *
-   *   // return time using FAT_TIME macro to format fields
-   *   *time = FAT_TIME(hour, minute, second);
-   * }
-   * \endcode
-   *
-   * Sets the function that is called when a file is created or when
-   * a file's directory entry is modified by sync(). All timestamps,
-   * access, creation, and modify, are set when a file is created.
-   * sync() maintains the last access date and last modify date/time.
-   *
-   * See the timestamp() function.
-   */
-  static void dateTimeCallback(
-    void (*dateTime)(uint16_t* date, uint16_t* time)) {
-    m_dateTime = dateTime;
-  }
-  /**  Cancel the date/time callback function. */
-  static void dateTimeCallbackCancel() {m_dateTime = 0;}
-  bool dirEntry(dir_t* dir);
-  static void dirName(const dir_t& dir, char* name);
-  bool exists(const char* name);
-  int16_t fgets(char* str, int16_t num, char* delim = 0);
-  /** \return The total number of bytes in a file or directory. */
-  uint32_t fileSize() const {return m_fileSize;}
-  /** \return The first cluster number for a file or directory. */
-  uint32_t firstCluster() const {return m_firstCluster;}
-  bool getFilename(char* name);
-  /** \return True if this is a directory else false. */
-  bool isDir() const {return m_type >= FAT_FILE_TYPE_MIN_DIR;}
-  /** \return True if this is a normal file else false. */
-  bool isFile() const {return m_type == FAT_FILE_TYPE_NORMAL;}
-  /** \return True if this is an open file/directory else false. */
-  bool isOpen() const {return m_type != FAT_FILE_TYPE_CLOSED;}
-  /** \return True if this is a subdirectory else false. */
-  bool isSubDir() const {return m_type == FAT_FILE_TYPE_SUBDIR;}
-  /** \return True if this is the root directory. */
-  bool isRoot() const {
-    return m_type == FAT_FILE_TYPE_ROOT_FIXED || m_type == FAT_FILE_TYPE_ROOT32;
-  }
-  void ls(Print* pr, uint8_t flags = 0, uint8_t indent = 0);
-  void ls(uint8_t flags = 0);
-  bool mkdir(SdBaseFile* dir, const char* path, bool pFlag = true);
-  // alias for backward compactability
-  bool makeDir(SdBaseFile* dir, const char* path) {
-    return mkdir(dir, path, false);
-  }
-  bool open(SdBaseFile* dirFile, uint16_t index, uint8_t oflag);
-  bool open(SdBaseFile* dirFile, const char* path, uint8_t oflag);
-  bool open(const char* path, uint8_t oflag = O_READ);
-  bool openNext(SdBaseFile* dirFile, uint8_t oflag);
-  bool openRoot(SdVolume* vol);
-  int peek();
-  bool printCreateDateTime(Print* pr);
-  static void printFatDate(uint16_t fatDate);
-  static void printFatDate(Print* pr, uint16_t fatDate);
-  static void printFatTime(uint16_t fatTime);
-  static void printFatTime(Print* pr, uint16_t fatTime);
-  int printField(float value, char term, uint8_t prec = 2);
-  int printField(int16_t value, char term);
-  int printField(uint16_t value, char term);
-  int printField(int32_t value, char term);
-  int printField(uint32_t value, char term);
-  bool printModifyDateTime(Print* pr);
-  size_t printName();
-  size_t printName(Print* pr);
-  size_t printFileSize(Print* pr);
-  int16_t read();
-  int read(void* buf, size_t nbyte);
-  int8_t readDir(dir_t* dir);
-  static bool remove(SdBaseFile* dirFile, const char* path);
-  bool remove();
-  /** Set the file's current position to zero. */
-  void rewind() {seekSet(0);}
-  bool rename(SdBaseFile* dirFile, const char* newPath);
-  bool rmdir();
-  // for backward compatibility
-  bool rmDir() {return rmdir();}
-  bool rmRfStar();
-  /** Set the files position to current position + \a pos. See seekSet().
-   * \param[in] offset The new position in bytes from the current position.
-   * \return true for success or false for failure.
-   */
-  bool seekCur(int32_t offset) {
-    return seekSet(m_curPosition + offset);
-  }
-  /** Set the files position to end-of-file + \a offset. See seekSet().
-   * \param[in] offset The new position in bytes from end-of-file.
-   * \return true for success or false for failure.
-   */
-  bool seekEnd(int32_t offset = 0) {return seekSet(m_fileSize + offset);}
-  bool seekSet(uint32_t pos);
-  bool sync();
-  bool timestamp(SdBaseFile* file);
-  bool timestamp(uint8_t flag, uint16_t year, uint8_t month, uint8_t day,
-          uint8_t hour, uint8_t minute, uint8_t second);
-  /** Type of file.  You should use isFile() or isDir() instead of type()
-   * if possible.
-   *
-   * \return The file or directory type.
-   */
-  uint8_t type() const {return m_type;}
-  bool truncate(uint32_t size);
-  /** \return SdVolume that contains this file. */
-  SdVolume* volume() const {return m_vol;}
-  int write(const void* buf, size_t nbyte);
-//------------------------------------------------------------------------------
- private:
-  // allow SdFat to set m_cwd
-  friend class SdFat;
-  /** experimental don't use */
-  bool openParent(SdBaseFile* dir);
-
-  // private functions
-  bool addCluster();
-  cache_t* addDirCluster();
-  dir_t* cacheDirEntry(uint8_t action);
-  int8_t lsPrintNext(Print *pr, uint8_t flags, uint8_t indent);
-  static bool make83Name(const char* str, uint8_t* name, const char** ptr);
-  bool mkdir(SdBaseFile* parent, const uint8_t dname[11]);
-  bool open(SdBaseFile* dirFile, const uint8_t dname[11], uint8_t oflag);
-  bool openCachedEntry(uint8_t cacheIndex, uint8_t oflags);
-  dir_t* readDirCache();
-  static void setCwd(SdBaseFile* cwd) {m_cwd = cwd;}
-  bool setDirSize();
-
-  // bits defined in m_flags
-  // should be 0X0F
-  static uint8_t const F_OFLAG = (O_ACCMODE | O_APPEND | O_SYNC);
-  // sync of directory entry required
-  static uint8_t const F_FILE_DIR_DIRTY = 0X80;
-
-  // global pointer to cwd dir
-  static SdBaseFile* m_cwd;
-  // data time callback function
-  static void (*m_dateTime)(uint16_t* date, uint16_t* time);
-  // private data
-  uint8_t   m_flags;         // See above for definition of m_flags bits
-  uint8_t   m_type;          // type of file see above for values
-  uint8_t   m_dirIndex;      // index of directory entry in dirBlock
-  SdVolume* m_vol;           // volume where file is located
-  uint32_t  m_curCluster;    // cluster for current file position
-  uint32_t  m_curPosition;   // current file position in bytes from beginning
-  uint32_t  m_dirBlock;      // block for this files directory entry
-  uint32_t  m_fileSize;      // file size in bytes
-  uint32_t  m_firstCluster;  // first cluster of file
-};
-#endif  // SdBaseFile_h

+ 0 - 351
SdFat/SdBaseFilePrint.cpp

@@ -1,351 +0,0 @@
-/* Arduino SdFat Library
- * Copyright (C) 2012 by William Greiman
- *
- * This file is part of the Arduino SdFat Library
- *
- * This Library is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This Library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with the Arduino SdFat Library.  If not, see
- * <http://www.gnu.org/licenses/>.
- */
-#include <SdFat.h>
-#include <utility/FmtNumber.h>
-//------------------------------------------------------------------------------
-/** List directory contents to stdOut.
- *
- * \param[in] flags The inclusive OR of
- *
- * LS_DATE - %Print file modification date
- *
- * LS_SIZE - %Print file size.
- *
- * LS_R - Recursive list of subdirectories.
- */
-void SdBaseFile::ls(uint8_t flags) {
-  ls(SdFat::stdOut(), flags, 0);
-}
-//------------------------------------------------------------------------------
-/** List directory contents.
- *
- * \param[in] pr Print stream for list.
- *
- * \param[in] flags The inclusive OR of
- *
- * LS_DATE - %Print file modification date
- *
- * LS_SIZE - %Print file size.
- *
- * LS_R - Recursive list of subdirectories.
- *
- * \param[in] indent Amount of space before file name. Used for recursive
- * list to indicate subdirectory level.
- */
-void SdBaseFile::ls(Print* pr, uint8_t flags, uint8_t indent) {
-  if (!isDir()) {
-    pr->println(F("bad dir"));
-    return;
-  }
-  rewind();
-  int8_t status;
-  while ((status = lsPrintNext(pr, flags, indent))) {
-    if (status > 1 && (flags & LS_R)) {
-      uint16_t index = curPosition()/32 - 1;
-      SdBaseFile s;
-      if (s.open(this, index, O_READ)) s.ls(pr, flags, indent + 2);
-      seekSet(32 * (index + 1));
-    }
-  }
-}
-//------------------------------------------------------------------------------
-// saves 32 bytes on stack for ls recursion
-// return 0 - EOF, 1 - normal file, or 2 - directory
-int8_t SdBaseFile::lsPrintNext(Print *pr, uint8_t flags, uint8_t indent) {
-  dir_t dir;
-  uint8_t w = 0;
-
-  while (1) {
-    if (read(&dir, sizeof(dir)) != sizeof(dir)) return 0;
-    if (dir.name[0] == DIR_NAME_FREE) return 0;
-
-    // skip deleted entry and entries for . and  ..
-    if (dir.name[0] != DIR_NAME_DELETED && dir.name[0] != '.'
-      && DIR_IS_FILE_OR_SUBDIR(&dir)) break;
-  }
-  // indent for dir level
-  for (uint8_t i = 0; i < indent; i++) pr->write(' ');
-
-  // print name
-  for (uint8_t i = 0; i < 11; i++) {
-    if (dir.name[i] == ' ')continue;
-    if (i == 8) {
-      pr->write('.');
-      w++;
-    }
-    pr->write(dir.name[i]);
-    w++;
-  }
-  if (DIR_IS_SUBDIR(&dir)) {
-    pr->write('/');
-    w++;
-  }
-  if (flags & (LS_DATE | LS_SIZE)) {
-    while (w++ < 14) pr->write(' ');
-  }
-  // print modify date/time if requested
-  if (flags & LS_DATE) {
-    pr->write(' ');
-    printFatDate(pr, dir.lastWriteDate);
-    pr->write(' ');
-    printFatTime(pr, dir.lastWriteTime);
-  }
-  // print size if requested
-  if (!DIR_IS_SUBDIR(&dir) && (flags & LS_SIZE)) {
-    pr->write(' ');
-    pr->print(dir.fileSize);
-  }
-  pr->println();
-  return DIR_IS_FILE(&dir) ? 1 : 2;
-}
-//------------------------------------------------------------------------------
-// print uint8_t with width 2
-static void print2u(Print* pr, uint8_t v) {
-  if (v < 10) pr->write('0');
-  pr->print(v, DEC);
-}
-//------------------------------------------------------------------------------
-/** Print a file's creation date and time
- *
- * \param[in] pr Print stream for output.
- *
- * \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.
- */
-bool SdBaseFile::printCreateDateTime(Print* pr) {
-  dir_t dir;
-  if (!dirEntry(&dir)) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  printFatDate(pr, dir.creationDate);
-  pr->write(' ');
-  printFatTime(pr, dir.creationTime);
-  return true;
-
- fail:
-  return false;
-}
-//------------------------------------------------------------------------------
-/** %Print a directory date field to stdOut.
- *
- *  Format is yyyy-mm-dd.
- *
- * \param[in] fatDate The date field from a directory entry.
- */
-void SdBaseFile::printFatDate(uint16_t fatDate) {
-  printFatDate(SdFat::stdOut(), fatDate);
-}
-//------------------------------------------------------------------------------
-/** %Print a directory date field.
- *
- *  Format is yyyy-mm-dd.
- *
- * \param[in] pr Print stream for output.
- * \param[in] fatDate The date field from a directory entry.
- */
-void SdBaseFile::printFatDate(Print* pr, uint16_t fatDate) {
-  pr->print(FAT_YEAR(fatDate));
-  pr->write('-');
-  print2u(pr, FAT_MONTH(fatDate));
-  pr->write('-');
-  print2u(pr, FAT_DAY(fatDate));
-}
-//------------------------------------------------------------------------------
-/** %Print a directory time field to stdOut.
- *
- * Format is hh:mm:ss.
- *
- * \param[in] fatTime The time field from a directory entry.
- */
-void SdBaseFile::printFatTime(uint16_t fatTime) {
-  printFatTime(SdFat::stdOut(), fatTime);
-}
-//------------------------------------------------------------------------------
-/** %Print a directory time field.
- *
- * Format is hh:mm:ss.
- *
- * \param[in] pr Print stream for output.
- * \param[in] fatTime The time field from a directory entry.
- */
-void SdBaseFile::printFatTime(Print* pr, uint16_t fatTime) {
-  print2u(pr, FAT_HOUR(fatTime));
-  pr->write(':');
-  print2u(pr, FAT_MINUTE(fatTime));
-  pr->write(':');
-  print2u(pr, FAT_SECOND(fatTime));
-}
-//------------------------------------------------------------------------------
-/** Template for SdBaseFile::printField() */
-template <typename Type>
-static int printFieldT(SdBaseFile* file, char sign, Type value, char term) {
-  char buf[3*sizeof(Type) + 3];
-  char* str = &buf[sizeof(buf)];
-
-  if (term) {
-    *--str = term;
-    if (term == '\n') {
-      *--str = '\r';
-    }
-  }
-#ifdef OLD_FMT
-  do {
-    Type m = value;
-    value /= 10;
-    *--str = '0' + m - 10*value;
-  } while (value);
-#else  // OLD_FMT
-  str = fmtDec(value, str);
-#endif  // OLD_FMT
-  if (sign) {
-    *--str = sign;
-  }
-  return file->write(str, &buf[sizeof(buf)] - str);
-}
-//------------------------------------------------------------------------------
-/** Print a number followed by a field terminator.
- * \param[in] value The number to be printed.
- * \param[in] term The field terminator.  Use '\\n' for CR LF.
- * \param[in] prec Number of digits after decimal point.
- * \return The number of bytes written or -1 if an error occurs.
- */
-int SdBaseFile::printField(float value, char term, uint8_t prec) {
-  char buf[24];
-  char* str = &buf[sizeof(buf)];
-  if (term) {
-    *--str = term;
-    if (term == '\n') {
-      *--str = '\r';
-    }
-  }
-  str = fmtFloat(value, str, prec);
-  return write(str, buf + sizeof(buf) - str);
-}
-//------------------------------------------------------------------------------
-/** Print a number followed by a field terminator.
- * \param[in] value The number to be printed.
- * \param[in] term The field terminator.  Use '\\n' for CR LF.
- * \return The number of bytes written or -1 if an error occurs.
- */
-int SdBaseFile::printField(uint16_t value, char term) {
-  return printFieldT(this, 0, value, term);
-}
-//------------------------------------------------------------------------------
-/** Print a number followed by a field terminator.
- * \param[in] value The number to be printed.
- * \param[in] term The field terminator.  Use '\\n' for CR LF.
- * \return The number of bytes written or -1 if an error occurs.
- */
-int SdBaseFile::printField(int16_t value, char term) {
-  char sign = 0;
-  if (value < 0) {
-    sign = '-';
-    value = -value;
-  }
-  return printFieldT(this, sign, (uint16_t)value, term);
-}
-//------------------------------------------------------------------------------
-/** Print a number followed by a field terminator.
- * \param[in] value The number to be printed.
- * \param[in] term The field terminator.  Use '\\n' for CR LF.
- * \return The number of bytes written or -1 if an error occurs.
- */
-int SdBaseFile::printField(uint32_t value, char term) {
-  return printFieldT(this, 0, value, term);
-}
-//------------------------------------------------------------------------------
-/** Print a number followed by a field terminator.
- * \param[in] value The number to be printed.
- * \param[in] term The field terminator.  Use '\\n' for CR LF.
- * \return The number of bytes written or -1 if an error occurs.
- */
-int SdBaseFile::printField(int32_t value, char term) {
-  char sign = 0;
-  if (value < 0) {
-    sign = '-';
-    value = -value;
-  }
-  return printFieldT(this, sign, (uint32_t)value, term);
-}
-//------------------------------------------------------------------------------
-/** Print a file's modify date and time
- *
- * \param[in] pr Print stream for output.
- *
- * \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.
- */
-bool SdBaseFile::printModifyDateTime(Print* pr) {
-  dir_t dir;
-  if (!dirEntry(&dir)) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  printFatDate(pr, dir.lastWriteDate);
-  pr->write(' ');
-  printFatTime(pr, dir.lastWriteTime);
-  return true;
-
- fail:
-  return false;
-}
-//------------------------------------------------------------------------------
-/** Print a file's name
- *
- * \param[in] pr Print stream for output.
- *
- * \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.
- */
-size_t SdBaseFile::printName(Print* pr) {
-  char name[13];
-  if (!getFilename(name)) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  return pr->print(name);
-
- fail:
-  return 0;
-}
-//------------------------------------------------------------------------------
-/** Print a file's name to stdOut
- *
- * \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.
- */
-size_t SdBaseFile::printName() {
-  return printName(SdFat::stdOut());
-}
-//------------------------------------------------------------------------------
-/** Print a file's size.
- *
- * \param[in] pr Print stream for output.
- *
- * \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure. 
- */
-size_t SdBaseFile::printFileSize(Print* pr) {
-  char buf[10];
-  char *ptr = fmtDec(fileSize(), buf + sizeof(buf));
-  while (ptr > buf) *--ptr = ' ';
-  return pr->write(reinterpret_cast<uint8_t *>(buf), sizeof(buf));
-}

+ 0 - 261
SdFat/SdFat.cpp

@@ -1,261 +0,0 @@
-/* Arduino SdFat Library
- * Copyright (C) 2012 by William Greiman
- *
- * This file is part of the Arduino SdFat Library
- *
- * This Library is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This Library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with the Arduino SdFat Library.  If not, see
- * <http://www.gnu.org/licenses/>.
- */
-#include <SdFat.h>
-//------------------------------------------------------------------------------
-#if USE_SERIAL_FOR_STD_OUT || !defined(UDR0)
-Print* SdFat::m_stdOut = &Serial;
-#else  // USE_SERIAL_FOR_STD_OUT
-#include <MinimumSerial.h>
-Print* SdFat::m_stdOut = &MiniSerial;
-#endif  // USE_SERIAL_FOR_STD_OUT
-//------------------------------------------------------------------------------
-/**
- * Initialize an SdFat object.
- *
- * Initializes the SD card, SD volume, and root directory.
- *
- * \param[in] chipSelectPin SD chip select pin. See Sd2Card::init().
- * \param[in] sckDivisor value for SPI SCK divisor. See Sd2Card::init().
- *
- * \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.
- */
-bool SdFat::begin(uint8_t chipSelectPin, uint8_t sckDivisor) {
-  return m_card.begin(chipSelectPin, sckDivisor)
-         && m_vol.init(&m_card) && chdir(1);
-}
-//------------------------------------------------------------------------------
-/** Change a volume's working directory to root
- *
- * Changes the volume's working directory to the SD's root directory.
- * Optionally set the current working directory to the volume's
- * working directory.
- *
- * \param[in] set_cwd Set the current working directory to this volume's
- *  working directory if true.
- *
- * \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.
- */
-bool SdFat::chdir(bool set_cwd) {
-  if (set_cwd) SdBaseFile::setCwd(&m_vwd);
-  if (m_vwd.isOpen()) m_vwd.close();
-  return m_vwd.openRoot(&m_vol);
-}
-//------------------------------------------------------------------------------
-/** Change a volume's working directory
- *
- * Changes the volume working directory to the \a path subdirectory.
- * Optionally set the current working directory to the volume's
- * working directory.
- *
- * Example: If the volume's working directory is "/DIR", chdir("SUB")
- * will change the volume's working directory from "/DIR" to "/DIR/SUB".
- *
- * If path is "/", the volume's working directory will be changed to the
- * root directory
- *
- * \param[in] path The name of the subdirectory.
- *
- * \param[in] set_cwd Set the current working directory to this volume's
- *  working directory if true.
- *
- * \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.
- */
-bool SdFat::chdir(const char *path, bool set_cwd) {
-  SdBaseFile dir;
-  dir.open(&m_vwd, path, O_READ);
-  // Check for correctly open directory.
-  if (!dir.isDir()) goto fail;
-  m_vwd = dir;
-  if (set_cwd) SdBaseFile::setCwd(&m_vwd);
-  return true;
-
- fail:
-  return false;
-}
-//------------------------------------------------------------------------------
-/** Set the current working directory to a volume's working directory.
- *
- * This is useful with multiple SD cards.
- *
- * The current working directory is changed to this volume's working directory.
- *
- * This is like the Windows/DOS \<drive letter>: command.
- */
-void SdFat::chvol() {
-  SdBaseFile::setCwd(&m_vwd);
-}
-//------------------------------------------------------------------------------
-/**
- * Test for the existence of a file.
- *
- * \param[in] name Name of the file to be tested for.
- *
- * \return true if the file exists else false.
- */
-bool SdFat::exists(const char* name) {
-  return m_vwd.exists(name);
-}
-//------------------------------------------------------------------------------
-/** List the directory contents of the volume working directory to stdOut.
- *
- * \param[in] flags The inclusive OR of
- *
- * LS_DATE - %Print file modification date
- *
- * LS_SIZE - %Print file size.
- *
- * LS_R - Recursive list of subdirectories.
- */
-void SdFat::ls(uint8_t flags) {
-  m_vwd.ls(m_stdOut, flags);
-}
-//------------------------------------------------------------------------------
-/** List the directory contents of the volume working directory to stdOut.
- *
- * \param[in] path directory to list.
- *
- * \param[in] flags The inclusive OR of
- *
- * LS_DATE - %Print file modification date
- *
- * LS_SIZE - %Print file size.
- *
- * LS_R - Recursive list of subdirectories.
- */
-void SdFat::ls(const char* path, uint8_t flags) {
-  ls(m_stdOut, path, flags);
-}
-//------------------------------------------------------------------------------
-/** List the directory contents of the volume working directory.
- *
- * \param[in] pr Print stream for the list.
- *
- * \param[in] flags The inclusive OR of
- *
- * LS_DATE - %Print file modification date
- *
- * LS_SIZE - %Print file size.
- *
- * LS_R - Recursive list of subdirectories.
- */
-void SdFat::ls(Print* pr, uint8_t flags) {
-  m_vwd.ls(pr, flags);
-}
-//------------------------------------------------------------------------------
-/** List the directory contents of the volume working directory to stdOut.
- *
- * \param[in] pr Print stream for the list.
- *
- * \param[in] path directory to list.
- *
- * \param[in] flags The inclusive OR of
- *
- * LS_DATE - %Print file modification date
- *
- * LS_SIZE - %Print file size.
- *
- * LS_R - Recursive list of subdirectories.
- */
-void SdFat::ls(Print* pr, const char* path, uint8_t flags) {
-  SdBaseFile dir(path, O_READ);
-  dir.ls(pr, flags);
-}
-//------------------------------------------------------------------------------
-/** Make a subdirectory in the volume working directory.
- *
- * \param[in] path A path with a valid 8.3 DOS name for the subdirectory.
- *
- * \param[in] pFlag Create missing parent directories if true.
- *
- * \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.
- */
-bool SdFat::mkdir(const char* path, bool pFlag) {
-  SdBaseFile sub;
-  return sub.mkdir(&m_vwd, path, pFlag);
-}
-//------------------------------------------------------------------------------
-/** Remove a file from the volume working directory.
-*
-* \param[in] path A path with a valid 8.3 DOS name for the file.
-*
-* \return The value one, true, is returned for success and
-* the value zero, false, is returned for failure.
-*/
-bool SdFat::remove(const char* path) {
-  return SdBaseFile::remove(&m_vwd, path);
-}
-//------------------------------------------------------------------------------
-/** Rename a file or subdirectory.
- *
- * \param[in] oldPath Path name to the file or subdirectory to be renamed.
- *
- * \param[in] newPath New path name of the file or subdirectory.
- *
- * The \a newPath object must not exist before the rename call.
- *
- * The file to be renamed must not be open.  The directory entry may be
- * moved and file system corruption could occur if the file is accessed by
- * a file object that was opened before the rename() call.
- *
- * \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.
- */
-bool SdFat::rename(const char *oldPath, const char *newPath) {
-  SdBaseFile file;
-  if (!file.open(oldPath, O_READ)) return false;
-  return file.rename(&m_vwd, newPath);
-}
-//------------------------------------------------------------------------------
-/** Remove a subdirectory from the volume's working directory.
- *
- * \param[in] path A path with a valid 8.3 DOS name for the subdirectory.
- *
- * The subdirectory file will be removed only if it is empty.
- *
- * \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.
- */
-bool SdFat::rmdir(const char* path) {
-  SdBaseFile sub;
-  if (!sub.open(path, O_READ)) return false;
-  return sub.rmdir();
-}
-//------------------------------------------------------------------------------
-/** Truncate a file to a specified length.  The current file position
- * will be maintained if it is less than or equal to \a length otherwise
- * it will be set to end of file.
- *
- * \param[in] path A path with a valid 8.3 DOS name for the file.
- * \param[in] length The desired length for the file.
- *
- * \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.
- * Reasons for failure include file is read only, file is a directory,
- * \a length is greater than the current file size or an I/O error occurs.
- */
-bool SdFat::truncate(const char* path, uint32_t length) {
-  SdBaseFile file;
-  if (!file.open(path, O_WRITE)) return false;
-  return file.truncate(length);
-}

+ 305 - 76
SdFat/SdFat.h

@@ -23,90 +23,319 @@
  * \file
  * \brief SdFat class
  */
-//------------------------------------------------------------------------------
-/** Macro for debug. */
-#define DBG_FAIL_MACRO  // Serial.print(__FILE__);Serial.println(__LINE__)
+#include "SdSpiCard.h"
+#include "utility/FatLib.h"
 //------------------------------------------------------------------------------
 /** SdFat version YYYYMMDD */
-#define SD_FAT_VERSION 20141111
-//------------------------------------------------------------------------------
-/** error if old IDE */
-#if !defined(ARDUINO) || ARDUINO < 100
-#error Arduino IDE must be 1.0 or greater
-#endif  // ARDUINO < 100
-//------------------------------------------------------------------------------
-#include <SdFile.h>
-#include <SdStream.h>
-#include <StdioStream.h>
-#include <ArduinoStream.h>
-#include <MinimumSerial.h>
-//------------------------------------------------------------------------------
+#define SD_FAT_VERSION 20150321
+//==============================================================================
+/**
+ * \class SdBaseFile
+ * \brief Class for backward compatibility.
+ */
+class SdBaseFile : public FatFile {
+ public:
+  SdBaseFile() {}
+  /**  Create a file object and open it in the current working directory.
+   *
+   * \param[in] path A path for a file to be opened.
+   *
+   * \param[in] oflag Values for \a oflag are constructed by a
+   * bitwise-inclusive OR of open flags. see
+   * FatFile::open(FatFile*, const char*, uint8_t).
+   */
+  SdBaseFile(const char* path, uint8_t oflag) : FatFile(path, oflag) {}
+};
+#if ENABLE_ARDUINO_FEATURES
+/**
+ * \class SdFile
+ * \brief Class for backward compatibility.
+ */
+
+class SdFile : public PrintFile {
+ public:
+  SdFile() {}
+  /**  Create a file object and open it in the current working directory.
+   *
+   * \param[in] path A path for a file to be opened.
+   *
+   * \param[in] oflag Values for \a oflag are constructed by a
+   * bitwise-inclusive OR of open flags. see
+   * FatFile::open(FatFile*, const char*, uint8_t).
+   */
+  SdFile(const char* path, uint8_t oflag) : PrintFile(path, oflag) {}
+};
+#endif  // #if ENABLE_ARDUINO_FEATURES
+/**
+ * \class SdFatBase
+ * \brief Virtual base class for %SdFat library.
+ */
+class SdFatBase : public FatFileSystem {
+ public:
+  /** Initialize SD card and file system.
+   * \param[in] spi SPI object for the card.
+   * \param[in] csPin SD card chip select pin.
+   * \param[in] divisor SPI divisor.
+   * \return true for success else false.
+   */
+  bool begin(SdSpiCard::m_spi_t* spi, uint8_t csPin = SS, uint8_t divisor = 2) {
+    return m_sdCard.begin(spi, csPin, divisor) &&
+           FatFileSystem::begin();
+  }
+  /** \return Pointer to SD card object */
+  SdSpiCard *card() {
+    return &m_sdCard;
+  }
+  /** %Print any SD error code to Serial and halt. */
+  void errorHalt() {
+    errorHalt(&Serial);
+  }
+  /** %Print any SD error code and halt.
+   *
+   * \param[in] pr Print destination.
+   */
+  void errorHalt(Print* pr);
+  /** %Print msg, any SD error code and halt.
+   *
+   * \param[in] msg Message to print.
+   */
+  void errorHalt(char const* msg) {
+    errorHalt(&Serial, msg);
+  }
+  /** %Print msg, any SD error code, and halt.
+   *
+   * \param[in] pr Print destination.
+   * \param[in] msg Message to print.
+   */
+  void errorHalt(Print* pr, char const* msg);
+  /** %Print msg, any SD error code, and halt.
+   *
+   * \param[in] msg Message to print.
+   */
+  void errorHalt(const __FlashStringHelper* msg) {
+    errorHalt(&Serial, msg);
+  }
+  /** %Print msg, any SD error code, and halt.
+   *
+   * \param[in] pr Print destination.
+   * \param[in] msg Message to print.
+   */
+  void errorHalt(Print* pr, const __FlashStringHelper* msg);
+  /** %Print any SD error code to Serial */
+  void errorPrint() {
+    errorPrint(&Serial);
+  }
+  /** %Print any SD error code.
+   * \param[in] pr Print device.
+   */
+  void errorPrint(Print* pr);
+  /** %Print msg, any SD error code.
+   *
+   * \param[in] msg Message to print.
+   */
+  void errorPrint(const char* msg) {
+    errorPrint(&Serial, msg);
+  }
+  /** %Print msg, any SD error code.
+   *
+   * \param[in] pr Print destination.
+   * \param[in] msg Message to print.
+   */
+  void errorPrint(Print* pr, char const* msg);
+  /** %Print msg, any SD error code.
+   *
+   * \param[in] msg Message to print.
+   */
+  void errorPrint(const __FlashStringHelper* msg) {
+    errorPrint(&Serial, msg);
+  }
+  /** %Print msg, any SD error code.
+   *
+   * \param[in] pr Print destination.
+   * \param[in] msg Message to print.
+   */
+  void errorPrint(Print* pr, const __FlashStringHelper* msg);
+  /** Diagnostic call to initialize FatFileSystem - use for
+   *  diagnostic purposes only.
+   *  \return true for success else false.
+   */
+  bool fsBegin() {
+    return FatFileSystem::begin();
+  }
+  /** %Print any SD error code and halt. */
+  void initErrorHalt() {
+    initErrorHalt(&Serial);
+  }
+  /** %Print error details and halt after begin fails.
+   *
+   * \param[in] pr Print destination.
+   */
+  void initErrorHalt(Print* pr);
+  /**Print message, error details, and halt after SdFat::init() fails.
+   *
+   * \param[in] msg Message to print.
+   */
+  void initErrorHalt(char const *msg) {
+    initErrorHalt(&Serial, msg);
+  }
+  /**Print message, error details, and halt after SdFatBase::init() fails.
+   * \param[in] pr Print device.
+   * \param[in] msg Message to print.
+   */
+  void initErrorHalt(Print* pr, char const *msg);
+  /**Print message, error details, and halt after SdFat::init() fails.
+    *
+    * \param[in] msg Message to print.
+    */
+  void initErrorHalt(const __FlashStringHelper* msg) {
+    initErrorHalt(&Serial, msg);
+  }
+  /**Print message, error details, and halt after SdFatBase::init() fails.
+   * \param[in] pr Print device for message.
+   * \param[in] msg Message to print.
+   */
+  void initErrorHalt(Print* pr, const __FlashStringHelper* msg);
+  /** Print error details after SdFat::init() fails. */
+  void initErrorPrint() {
+    initErrorPrint(&Serial);
+  }
+  /** Print error details after SdFatBase::init() fails.
+   *
+   * \param[in] pr Print destination.
+   */
+  void initErrorPrint(Print* pr);
+  /**Print message and error details and halt after SdFat::init() fails.
+   *
+   * \param[in] msg Message to print.
+   */
+  void initErrorPrint(char const *msg) {
+    initErrorPrint(&Serial, msg);
+  }
+  /**Print message and error details and halt after SdFatBase::init() fails.
+   *
+   * \param[in] pr Print destination.
+   * \param[in] msg Message to print.
+   */
+  void initErrorPrint(Print* pr, char const *msg);
+  /**Print message and error details and halt after SdFat::init() fails.
+   *
+   * \param[in] msg Message to print.
+   */
+  void initErrorPrint(const __FlashStringHelper* msg) {
+    initErrorPrint(&Serial, msg);
+  }
+  /**Print message and error details and halt after SdFatBase::init() fails.
+   *
+   * \param[in] pr Print destination.
+   * \param[in] msg Message to print.
+   */
+  void initErrorPrint(Print* pr, const __FlashStringHelper* msg);
+
+ private:
+  uint8_t cardErrorCode() {
+    return m_sdCard.errorCode();
+  }
+  uint8_t cardErrorData() {
+    return m_sdCard.errorData();
+  }
+  bool readBlock(uint32_t block, uint8_t* dst) {
+    return m_sdCard.readBlock(block, dst);
+  }
+  bool writeBlock(uint32_t block, const uint8_t* src) {
+    return m_sdCard.writeBlock(block, src);
+  }
+  bool readBlocks(uint32_t block, uint8_t* dst, size_t n) {
+    return m_sdCard.readBlocks(block, dst, n);
+  }
+  bool writeBlocks(uint32_t block, const uint8_t* src, size_t n) {
+    return m_sdCard.writeBlocks(block, src, n);
+  }
+  SdSpiCard m_sdCard;
+};
+//==============================================================================
 /**
  * \class SdFat
- * \brief Integration class for the %SdFat library.
+ * \brief Main file system class for %SdFat library.
+ */
+class SdFat : public SdFatBase {
+ public:
+  /** Initialize SD card and file system.
+   *
+   * \param[in] csPin SD card chip select pin.
+   * \param[in] divisor SPI divisor.
+   * \return true for success else false.
+   */
+  bool begin(uint8_t csPin = SS, uint8_t divisor = 2) {
+    return SdFatBase::begin(&m_spi, csPin, divisor);
+  }
+  /** Diagnostic call to initialize SD card - use for diagnostic purposes only.
+   * \param[in] csPin SD card chip select pin.
+   * \param[in] divisor SPI divisor.
+   * \return true for success else false.
+   */
+  bool cardBegin(uint8_t csPin = SS, uint8_t divisor = 2) {
+    return card()->begin(&m_spi, csPin, divisor);
+  }
+ private:
+  SpiDefault_t m_spi;
+};
+//==============================================================================
+#if SD_SPI_CONFIGURATION >= 3 || defined(DOXYGEN)
+/**
+ * \class SdFatLibSpi
+ * \brief SdFat class using the standard Arduino SPI library.
  */
-class SdFat {
+class SdFatLibSpi: public SdFatBase {
  public:
-  SdFat() {}
-  /** \return a pointer to the Sd2Card object. */
-  Sd2Card* card() {return &m_card;}
-  bool chdir(bool set_cwd = false);
-  bool chdir(const char* path, bool set_cwd = false);
-  void chvol();
-  void errorHalt();
-  void errorHalt(char const *msg);
-  void errorPrint();
-  void errorPrint(char const *msg);
-  bool exists(const char* name);
-  bool begin(uint8_t chipSelectPin = SD_CHIP_SELECT_PIN,
-    uint8_t sckDivisor = SPI_FULL_SPEED);
-  void initErrorHalt();
-  void initErrorHalt(char const *msg);
-  void initErrorPrint();
-  void initErrorPrint(char const *msg);
-  void ls(uint8_t flags = 0);
-  void ls(const char* path, uint8_t flags = 0);
-  void ls(Print* pr, uint8_t flags = 0);
-  void ls(Print* pr, const char* path, uint8_t flags = 0);
-  bool mkdir(const char* path, bool pFlag = true);
-  bool remove(const char* path);
-  bool rename(const char *oldPath, const char *newPath);
-  bool rmdir(const char* path);
-  bool truncate(const char* path, uint32_t length);
-  /** \return a pointer to the SdVolume object. */
-  SdVolume* vol() {return &m_vol;}
-  /** \return a pointer to the volume working directory. */
-  SdBaseFile* vwd() {return &m_vwd;}
-  //----------------------------------------------------------------------------
-  void errorHalt_P(PGM_P msg);
-  void errorPrint_P(PGM_P msg);
-  void initErrorHalt_P(PGM_P msg);
-  void initErrorPrint_P(PGM_P msg);
-  //----------------------------------------------------------------------------
-  /**
-   *  Set stdOut Print stream for messages.
-   * \param[in] stream The new Print stream.
-   */
-  static void setStdOut(Print* stream) {m_stdOut = stream;}
-  /** \return Print stream for messages. */
-  static Print* stdOut() {return m_stdOut;}
-  //----------------------------------------------------------------------------
-  /** open a file 
-   *
-   * \param[in] path location of file to be opened.
-   * \param[in] mode open mode flags.
-   * \return a File object.
-   */  
-  File open(const char *path, uint8_t mode = FILE_READ) {
-    File tmpFile;
-    tmpFile.open(&m_vwd, path, mode);
-    return tmpFile;
+  /** Initialize SD card and file system.
+  *
+  * \param[in] csPin SD card chip select pin.
+  * \param[in] divisor SPI divisor.
+  * \return true for success else false.
+  */
+  bool begin(uint8_t csPin = SS, uint8_t divisor = 2) {
+    return SdFatBase::begin(&m_spi, csPin, divisor);
+  }
+  /** Diagnostic call to initialize SD card - use for diagnostic purposes only.
+   * \param[in] csPin SD card chip select pin.
+   * \param[in] divisor SPI divisor.
+   * \return true for success else false.
+   */
+  bool cardBegin(uint8_t csPin = SS, uint8_t divisor = 2) {
+    return card()->begin(&m_spi, csPin, divisor);
+  }
+
+ private:
+  SdSpiLib m_spi;
+};
+//==============================================================================
+/**
+ * \class SdFatSoftSpi
+ * \brief SdFat class using software SPI.
+ */
+template<uint8_t MisoPin, uint8_t MosiPin, uint8_t SckPin>
+class SdFatSoftSpi : public SdFatBase {
+ public:
+  /** Initialize SD card and file system.
+   *
+   * \param[in] csPin SD card chip select pin.
+   * \param[in] divisor SPI divisor.
+   * \return true for success else false.
+   */
+  bool begin(uint8_t csPin = SS, uint8_t divisor = 2) {
+    return SdFatBase::begin(&m_spi, csPin, divisor);
+  }
+  /** Diagnostic call to initialize SD card - use for diagnostic purposes only.
+   * \param[in] csPin SD card chip select pin.
+   * \param[in] divisor SPI divisor.
+   * \return true for success else false.
+   */
+  bool cardBegin(uint8_t csPin = SS, uint8_t divisor = 2) {
+    return card()->begin(&m_spi, csPin, divisor);
   }
 
  private:
-  Sd2Card m_card;
-  SdVolume m_vol;
-  SdBaseFile m_vwd;
-  static Print* m_stdOut;
+  SdSpiSoft<MisoPin, MosiPin, SckPin> m_spi;
 };
+#endif  /// SD_SPI_CONFIGURATION >= 3 || defined(DOXYGEN)
 #endif  // SdFat_h

+ 99 - 0
SdFat/SdFatBase.cpp

@@ -0,0 +1,99 @@
+/* Arduino SdFat Library
+ * Copyright (C) 2012 by William Greiman
+ *
+ * This file is part of the Arduino SdFat Library
+ *
+ * This Library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the Arduino SdFat Library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+#include "SdFat.h"
+//------------------------------------------------------------------------------
+void SdFatBase::errorHalt(Print* pr) {
+  errorPrint(pr);
+  while (1) {}
+}
+//------------------------------------------------------------------------------
+void SdFatBase::errorHalt(Print* pr, char const* msg) {
+  errorPrint(pr, msg);
+  while (1) {}
+}
+//------------------------------------------------------------------------------
+void SdFatBase::errorHalt(Print* pr, const __FlashStringHelper* msg) {
+  errorPrint(pr, msg);
+  while (1) {}
+}
+//------------------------------------------------------------------------------
+void SdFatBase::errorPrint(Print* pr) {
+  if (!cardErrorCode()) {
+    return;
+  }
+  pr->print(F("SD errorCode: 0X"));
+  pr->print(cardErrorCode(), HEX);
+  pr->print(F(",0X"));
+  pr->println(cardErrorData(), HEX);
+}
+//------------------------------------------------------------------------------
+void SdFatBase::errorPrint(Print* pr, char const* msg) {
+  pr->print(F("error: "));
+  pr->println(msg);
+  errorPrint(pr);
+}
+//------------------------------------------------------------------------------
+void SdFatBase::errorPrint(Print* pr, const __FlashStringHelper* msg) {
+  pr->print(F("error: "));
+  pr->println(msg);
+  errorPrint(pr);
+}
+//------------------------------------------------------------------------------
+void SdFatBase::initErrorHalt(Print* pr) {
+  initErrorPrint(pr);
+  while (1) {}
+}
+//------------------------------------------------------------------------------
+void SdFatBase::initErrorHalt(Print* pr, char const *msg) {
+  pr->println(msg);
+  initErrorHalt(pr);
+}
+//------------------------------------------------------------------------------
+void SdFatBase::initErrorHalt(Print* pr, const __FlashStringHelper* msg) {
+  pr->println(msg);
+  initErrorHalt(pr);
+}
+//------------------------------------------------------------------------------
+void SdFatBase::initErrorPrint(Print* pr) {
+  if (cardErrorCode()) {
+    pr->println(F("Can't access SD card. Do not reformat."));
+    if (cardErrorCode() == SD_CARD_ERROR_CMD0) {
+      pr->println(F("No card, wrong chip select pin, or SPI problem?"));
+    }
+    errorPrint(pr);
+  } else if (vol()->fatType() == 0) {
+    pr->println(F("Invalid format, reformat SD."));
+  } else if (!vwd()->isOpen()) {
+    pr->println(F("Can't open root directory."));
+  } else {
+    pr->println(F("No error found."));
+  }
+}
+//------------------------------------------------------------------------------
+void SdFatBase::initErrorPrint(Print* pr, char const *msg) {
+  pr->println(msg);
+  initErrorPrint(pr);
+}
+//------------------------------------------------------------------------------
+void SdFatBase::initErrorPrint(Print* pr, const __FlashStringHelper* msg) {
+  pr->println(msg);
+  initErrorPrint(pr);
+}
+

+ 70 - 87
SdFat/SdFatConfig.h

@@ -29,128 +29,111 @@
 #endif  // __AVR__
 //------------------------------------------------------------------------------
 /**
- * Set SD_FILE_USES_STREAM nonzero to use Stream instead of Print for SdFile.
- * Using Stream will use more flash and may cause compatibility problems
- * with code written for older versions of SdFat. 
+ * Set USE_LONG_FILE_NAMES nonzero to use long file names (LFN).
+ * Long File Name are limited to a maximum length of 255 characters.
+ *
+ * This implementation allows 7-bit characters in the range
+ * 0X20 to 0X7E except the following characters are not allowed:
+ *
+ *  < (less than)
+ *  > (greater than)
+ *  : (colon)
+ *  " (double quote)
+ *  / (forward slash)
+ *  \ (backslash)
+ *  | (vertical bar or pipe)
+ *  ? (question mark)
+ *  * (asterisk)
+ *
  */
-#define SD_FILE_USES_STREAM 0
-
+#define USE_LONG_FILE_NAMES 1
 //------------------------------------------------------------------------------
 /**
- * To enable SD card CRC checking set USE_SD_CRC nonzero.
+ * Set ARDUINO_FILE_USES_STREAM nonzero to use Stream as the base class
+ * for the Arduino File class.  If ARDUINO_FILE_USES_STREAM is zero, Print
+ * will be used as the base class for the Arduino File class.
  *
- * Set USE_SD_CRC to 1 to use a smaller slower CRC-CCITT function.
- *
- * Set USE_SD_CRC to 2 to used a larger faster table driven CRC-CCITT function.
+ * You can save some flash if you do not use Stream input functions such as
+ * find(), findUntil(), readBytesUntil(), readString(), readStringUntil(),
+ * parseInt(), and parseFloat().
  */
-#define USE_SD_CRC 0
+#define ARDUINO_FILE_USES_STREAM 1
 //------------------------------------------------------------------------------
 /**
- * To use multiple SD cards set USE_MULTIPLE_CARDS nonzero.
+ * The symbol SD_SPI_CONFIGURATION defines SPI access to the SD card.
  *
- * Using multiple cards costs about 200  bytes of flash.
+ * IF SD_SPI_CONFIGUTATION is define to be zero, only the SdFat class
+ * is define and SdFat uses a fast custom SPI implementation.
  *
- * Each card requires about 550 bytes of SRAM so use of a Mega is recommended.
+ * If SD_SPI_CONFIGURATION is define to be one, only the SdFat class is
+ * define and SdFat uses the standard Arduino SPI.h library.
+ *
+ * If SD_SPI_CONFIGURATION is define to be two, only the SdFat class is
+ * define and SdFat uses software SPI on the pins defined below.
+ *
+ * If SD_SPI_CONFIGURATION is define to be three, the three classes, SdFat,
+ * SdFatLibSpi, and SdFatSoftSpi are defined.  SdFat uses the fast
+ * custom SPI implementation. SdFatLibSpi uses the standard Arduino SPI
+ * library.  SdFatSoftSpi is a template class that uses Software SPI. The
+ * template parameters define the software SPI pins.  See the ThreeCard
+ * example for simultaneous use of all three classes.
  */
-#define USE_MULTIPLE_CARDS 0
+#define SD_SPI_CONFIGURATION 0
 //------------------------------------------------------------------------------
 /**
- * Set DESTRUCTOR_CLOSES_FILE nonzero to close a file in its destructor.
+ * If SD_SPI_CONFIGURATION is defined to be two, these definitions
+ * will define the pins used for software SPI.
  *
- * Causes use of lots of heap in ARM.
+ * The default definition allows Uno shields to be used on other boards.
  */
-#define DESTRUCTOR_CLOSES_FILE 0
+/** Software SPI Master Out Slave In pin */
+uint8_t const SOFT_SPI_MOSI_PIN = 11;
+/** Software SPI Master In Slave Out pin */
+uint8_t const SOFT_SPI_MISO_PIN = 12;
+/** Software SPI Clock pin */
+uint8_t const SOFT_SPI_SCK_PIN = 13;
 //------------------------------------------------------------------------------
 /**
- * For AVR
- *
- * Set USE_SERIAL_FOR_STD_OUT nonzero to use Serial (the HardwareSerial class)
- * for error messages and output from print functions like ls().
+ * To enable SD card CRC checking set USE_SD_CRC nonzero.
  *
- * If USE_SERIAL_FOR_STD_OUT is zero, a small non-interrupt driven class
- * is used to output messages to serial port zero.  This allows an alternate
- * Serial library like SerialPort to be used with SdFat.
+ * Set USE_SD_CRC to 1 to use a smaller slower CRC-CCITT function.
  *
- * You can redirect stdOut with SdFat::setStdOut(Print* stream) and
- * get the current stream with SdFat::stdOut().
- */
-#define USE_SERIAL_FOR_STD_OUT 0
-//------------------------------------------------------------------------------
-/**
- * Set FAT12_SUPPORT nonzero to enable use if FAT12 volumes.
- * FAT12 has not been well tested and requires additional flash.
+ * Set USE_SD_CRC to 2 to used a larger faster table driven CRC-CCITT function.
  */
-#define FAT12_SUPPORT 0
+#define USE_SD_CRC 0
 //------------------------------------------------------------------------------
 /**
  * Set ENABLE_SPI_TRANSACTION nonzero to enable the SPI transaction feature
  * of the standard Arduino SPI library.  You must include SPI.h in your
- * sketches when ENABLE_SPI_TRANSACTION is nonzero.
+ * programs when ENABLE_SPI_TRANSACTION is nonzero.
  */
 #define ENABLE_SPI_TRANSACTION 0
 //------------------------------------------------------------------------------
 /**
  * Set ENABLE_SPI_YIELD nonzero to enable release of the SPI bus during
- * SD card busy waits.  
+ * SD card busy waits.
  *
- * This will allow interrupt routines to access the SPI bus if 
+ * This will allow interrupt routines to access the SPI bus if
  * ENABLE_SPI_TRANSACTION is nonzero.
- * 
+ *
  * Setting ENABLE_SPI_YIELD will introduce some extra overhead and will
- * slightly slow transfer rates.  A few older SD cards may fail when 
+ * slightly slow transfer rates.  A few older SD cards may fail when
  * ENABLE_SPI_YIELD is nonzero.
  */
 #define ENABLE_SPI_YIELD 0
 //------------------------------------------------------------------------------
 /**
- * Set USE_ARDUINO_SPI_LIBRARY nonzero to force use of Arduino Standard
- * SPI library. This will override native and software SPI for all boards.
- */
-#define USE_ARDUINO_SPI_LIBRARY 0
-//------------------------------------------------------------------------------
-/**
- * Set AVR_SOFT_SPI nonzero to use software SPI on all AVR Arduinos.
- */
-#define AVR_SOFT_SPI 0
-//------------------------------------------------------------------------------
-/**
- * Set DUE_SOFT_SPI nonzero to use software SPI on Due Arduinos.
- */
-#define DUE_SOFT_SPI 0
-//------------------------------------------------------------------------------
-
-/**
- * Set LEONARDO_SOFT_SPI nonzero to use software SPI on Leonardo Arduinos.
- * LEONARDO_SOFT_SPI allows an unmodified 328 Shield to be used
- * on Leonardo Arduinos.
- */
-#define LEONARDO_SOFT_SPI 0
-//------------------------------------------------------------------------------
-/**
- * Set MEGA_SOFT_SPI nonzero to use software SPI on Mega Arduinos.
- * MEGA_SOFT_SPI allows an unmodified 328 Shield to be used
- * on Mega Arduinos.
+ * Set FAT12_SUPPORT nonzero to enable use if FAT12 volumes.
+ * FAT12 has not been well tested and requires additional flash.
  */
-#define MEGA_SOFT_SPI 0
+#define FAT12_SUPPORT 0
 //------------------------------------------------------------------------------
 /**
- * Set TEENSY3_SOFT_SPI nonzero to use software SPI on Teensy 3.x boards.
- */
-#define TEENSY3_SOFT_SPI 0
-//------------------------------------------------------------------------------
-/** 
- * Define software SPI pins.  Default allows Uno shields to be used on other 
- * boards.
+ * Set DESTRUCTOR_CLOSES_FILE nonzero to close a file in its destructor.
+ *
+ * Causes use of lots of heap in ARM.
  */
-// define software SPI pins
-/** Default Software SPI chip select pin */
-uint8_t const SOFT_SPI_CS_PIN = 10;
-/** Software SPI Master Out Slave In pin */
-uint8_t const SOFT_SPI_MOSI_PIN = 11;
-/** Software SPI Master In Slave Out pin */
-uint8_t const SOFT_SPI_MISO_PIN = 12;
-/** Software SPI Clock pin */
-uint8_t const SOFT_SPI_SCK_PIN = 13;
+#define DESTRUCTOR_CLOSES_FILE 0
 //------------------------------------------------------------------------------
 /**
  * Call flush for endl if ENDL_CALLS_FLUSH is nonzero
@@ -183,8 +166,8 @@ const uint8_t SPI_SCK_INIT_DIVISOR = 128;
 //------------------------------------------------------------------------------
 /**
  * Set USE_SEPARATE_FAT_CACHE nonzero to use a second 512 byte cache
- * for FAT table entries.  Improves performance for large writes that
- * are not a multiple of 512 bytes.
+ * for FAT table entries.  This improves performance for large writes
+ * that are not a multiple of 512 bytes.
  */
 #ifdef __arm__
 #define USE_SEPARATE_FAT_CACHE 1
@@ -193,13 +176,13 @@ const uint8_t SPI_SCK_INIT_DIVISOR = 128;
 #endif  // __arm__
 //------------------------------------------------------------------------------
 /**
- * Set USE_MULTI_BLOCK_SD_IO nonzero to use multi-block SD read/write.
+ * Set USE_MULTI_BLOCK_IO nonzero to use multi-block SD read/write.
  *
  * Don't use mult-block read/write on small AVR boards.
  */
 #if defined(RAMEND) && RAMEND < 3000
-#define USE_MULTI_BLOCK_SD_IO 0
+#define USE_MULTI_BLOCK_IO 0
 #else  // RAMEND
-#define USE_MULTI_BLOCK_SD_IO 1
+#define USE_MULTI_BLOCK_IO 1
 #endif  // RAMEND
 #endif  // SdFatConfig_h

+ 0 - 145
SdFat/SdFatErrorPrint.cpp

@@ -1,145 +0,0 @@
-/* Arduino SdFat Library
- * Copyright (C) 2012 by William Greiman
- *
- * This file is part of the Arduino SdFat Library
- *
- * This Library is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This Library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with the Arduino SdFat Library.  If not, see
- * <http://www.gnu.org/licenses/>.
- */
-#include <SdFat.h>
-#ifndef PSTR
-#define PSTR(x) x
-#define PGM_P const char*
-#endif
-//------------------------------------------------------------------------------
-static void pstrPrint(PGM_P str) {
-  for (uint8_t c; (c = pgm_read_byte(str)); str++) SdFat::stdOut()->write(c);
-}
-//------------------------------------------------------------------------------
-static void pstrPrintln(PGM_P str) {
-  pstrPrint(str);
-  SdFat::stdOut()->println();
-}
-//------------------------------------------------------------------------------
-/** %Print any SD error code and halt. */
-void SdFat::errorHalt() {
-  errorPrint();
-  while (1) {}
-}
-//------------------------------------------------------------------------------
-/** %Print msg, any SD error code, and halt.
- *
- * \param[in] msg Message to print.
- */
-void SdFat::errorHalt(char const* msg) {
-  errorPrint(msg);
-  while (1) {}
-}
-//------------------------------------------------------------------------------
-/** %Print msg, any SD error code, and halt.
- *
- * \param[in] msg Message in program space (flash memory) to print.
- */
-void SdFat::errorHalt_P(PGM_P msg) {
-  errorPrint_P(msg);
-  while (1) {}
-}
-//------------------------------------------------------------------------------
-/** %Print any SD error code. */
-void SdFat::errorPrint() {
-  if (!m_card.errorCode()) return;
-  pstrPrint(PSTR("SD errorCode: 0X"));
-  m_stdOut->print(m_card.errorCode(), HEX);
-  pstrPrint(PSTR(",0X"));
-  m_stdOut->println(m_card.errorData(), HEX);
-}
-//------------------------------------------------------------------------------
-/** %Print msg, any SD error code.
- *
- * \param[in] msg Message to print.
- */
-void SdFat::errorPrint(char const* msg) {
-  pstrPrint(PSTR("error: "));
-  m_stdOut->println(msg);
-  errorPrint();
-}
-//------------------------------------------------------------------------------
-/** %Print msg, any SD error code.
- *
- * \param[in] msg Message in program space (flash memory) to print.
- */
-void SdFat::errorPrint_P(PGM_P msg) {
-  pstrPrint(PSTR("error: "));
-  pstrPrintln(msg);
-  errorPrint();
-}
-//------------------------------------------------------------------------------
-/** %Print error details and halt after SdFat::init() fails. */
-void SdFat::initErrorHalt() {
-  initErrorPrint();
-  while (1) {}
-}
-//------------------------------------------------------------------------------
-/**Print message, error details, and halt after SdFat::init() fails.
- *
- * \param[in] msg Message to print.
- */
-void SdFat::initErrorHalt(char const *msg) {
-  m_stdOut->println(msg);
-  initErrorHalt();
-}
-//------------------------------------------------------------------------------
-/**Print message, error details, and halt after SdFat::init() fails.
- *
- * \param[in] msg Message in program space (flash memory) to print.
- */
-void SdFat::initErrorHalt_P(PGM_P msg) {
-  pstrPrintln(msg);
-  initErrorHalt();
-}
-//------------------------------------------------------------------------------
-/** Print error details after SdFat::init() fails. */
-void SdFat::initErrorPrint() {
-  if (m_card.errorCode()) {
-    pstrPrintln(PSTR("Can't access SD card. Do not reformat."));
-    if (m_card.errorCode() == SD_CARD_ERROR_CMD0) {
-      pstrPrintln(PSTR("No card, wrong chip select pin, or SPI problem?"));
-    }
-    errorPrint();
-  } else if (m_vol.fatType() == 0) {
-    pstrPrintln(PSTR("Invalid format, reformat SD."));
-  } else if (!m_vwd.isOpen()) {
-    pstrPrintln(PSTR("Can't open root directory."));
-  } else {
-    pstrPrintln(PSTR("No error found."));
-  }
-}
-//------------------------------------------------------------------------------
-/**Print message and error details and halt after SdFat::init() fails.
- *
- * \param[in] msg Message to print.
- */
-void SdFat::initErrorPrint(char const *msg) {
-  m_stdOut->println(msg);
-  initErrorPrint();
-}
-//------------------------------------------------------------------------------
-/**Print message and error details after SdFat::init() fails.
- *
- * \param[in] msg Message in program space (flash memory) to print.
- */
-void SdFat::initErrorPrint_P(PGM_P msg) {
-  pstrPrintln(msg);
-  initErrorHalt();
-}

+ 12 - 37
SdFat/SdFatUtil.cpp

@@ -18,59 +18,34 @@
  * <http://www.gnu.org/licenses/>.
  */
 #include <stdlib.h>
-#include <SdFat.h>
-#include <SdFatUtil.h>
+#include "SdFat.h"
+#include "SdFatUtil.h"
+//------------------------------------------------------------------------------
 #ifdef __arm__
-// should use uinstd.h to define sbrk but Due causes a conflict
 extern "C" char* sbrk(int incr);
-#else  // __ARM__
+int SdFatUtil::FreeRam() {
+  char top;
+  return &top - reinterpret_cast<char*>(sbrk(0));
+}
+#else  // __arm__
 extern char *__brkval;
 extern char __bss_end;
-#endif  // __arm__
-//------------------------------------------------------------------------------
 /** Amount of free RAM
  * \return The number of free bytes.
  */
 int SdFatUtil::FreeRam() {
   char top;
-#ifdef __arm__
-  return &top - reinterpret_cast<char*>(sbrk(0));
-#else  // __arm__
   return __brkval ? &top - __brkval : &top - &__bss_end;
-#endif  // __arm__
 }
+#endif  // __arm
 //------------------------------------------------------------------------------
-/** %Print a string in flash memory.
- *
- * \param[in] pr Print object for output.
- * \param[in] str Pointer to string stored in flash memory.
- */
 void SdFatUtil::print_P(Print* pr, PGM_P str) {
-  for (uint8_t c; (c = pgm_read_byte(str)); str++) pr->write(c);
+  for (uint8_t c; (c = pgm_read_byte(str)); str++) {
+    pr->write(c);
+  }
 }
 //------------------------------------------------------------------------------
-/** %Print a string in flash memory followed by a CR/LF.
- *
- * \param[in] pr Print object for output.
- * \param[in] str Pointer to string stored in flash memory.
- */
 void SdFatUtil::println_P(Print* pr, PGM_P str) {
   print_P(pr, str);
   pr->println();
 }
-//------------------------------------------------------------------------------
-/** %Print a string in flash memory to Serial.
- *
- * \param[in] str Pointer to string stored in flash memory.
- */
-void SdFatUtil::SerialPrint_P(PGM_P str) {
-  print_P(SdFat::stdOut(), str);
-}
-//------------------------------------------------------------------------------
-/** %Print a string in flash memory to Serial followed by a CR/LF.
- *
- * \param[in] str Pointer to string stored in flash memory.
- */
-void SdFatUtil::SerialPrintln_P(PGM_P str) {
-  println_P(SdFat::stdOut(), str);
-}

+ 32 - 5
SdFat/SdFatUtil.h

@@ -12,7 +12,7 @@
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
-
+ *
  * You should have received a copy of the GNU General Public License
  * along with the Arduino SdFat Library.  If not, see
  * <http://www.gnu.org/licenses/>.
@@ -23,18 +23,45 @@
  * \file
  * \brief Useful utility functions.
  */
-#include <SdFat.h>
+#include "SdFat.h"
 /** Store and print a string in flash memory.*/
 #define PgmPrint(x) SerialPrint_P(PSTR(x))
 /** Store and print a string in flash memory followed by a CR/LF.*/
 #define PgmPrintln(x) SerialPrintln_P(PSTR(x))
 
 namespace SdFatUtil {
+  /** Amount of free RAM
+   * \return The number of free bytes.
+   */
   int FreeRam();
+  /** %Print a string in flash memory.
+   *
+   * \param[in] pr Print object for output.
+   * \param[in] str Pointer to string stored in flash memory.
+   */
   void print_P(Print* pr, PGM_P str);
+  /** %Print a string in flash memory followed by a CR/LF.
+   *
+   * \param[in] pr Print object for output.
+   * \param[in] str Pointer to string stored in flash memory.
+   */
   void println_P(Print* pr, PGM_P str);
-  void SerialPrint_P(PGM_P str);
-  void SerialPrintln_P(PGM_P str);
-}
+  //----------------------------------------------------------------------------
+  /** %Print a string in flash memory to Serial.
+   *
+   * \param[in] str Pointer to string stored in flash memory.
+   */
+  inline void SerialPrint_P(PGM_P str) {
+    print_P(&Serial, str);
+  }
+  //----------------------------------------------------------------------------
+  /** %Print a string in flash memory to Serial followed by a CR/LF.
+   *
+   * \param[in] str Pointer to string stored in flash memory.
+   */
+  inline void SerialPrintln_P(PGM_P str) {
+    println_P(&Serial, str);
+  }
+}  // namespace SdFatUtil
 using namespace SdFatUtil;  // NOLINT
 #endif  // #define SdFatUtil_h

+ 173 - 45
SdFat/SdFatmainpage.h

@@ -1,13 +1,13 @@
 /* Arduino SdFat Library
  * Copyright (C) 2012 by William Greiman
- *  
+ *
  * This file is part of the Arduino SdFat Library
- *  
- * This Library is free software: you can redistribute it and/or modify 
- * it under the terms of the GNU General Public License as published by 
+ *
+ * This Library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation, either version 3 of the License, or
  * (at your option) any later version.
- * 
+ *
  * This Library is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
@@ -20,7 +20,7 @@
 
 /**
 \mainpage Arduino %SdFat Library
-<CENTER>Copyright &copy; 2012, 2013, 2014 by William Greiman
+<CENTER>Copyright &copy; 2012, 2013, 2014, 2015 by William Greiman
 </CENTER>
 
 \section Intro Introduction
@@ -31,13 +31,18 @@ cards are supported.
 Experimental support for FAT12 can be enabled by setting FAT12_SUPPORT
 nonzero in SdFatConfig.h.
 
-The %SdFat library only supports short 8.3 names.
+The %SdFat library supports Long %File Names or short 8.3 names.
+Edit the SdFatConfig.h file to select short or long file names.
 
-The main classes in %SdFat are SdFat, SdBaseFile, SdFile, File, StdioStream,
-\ref fstream, \ref ifstream, and \ref ofstream.
+The main classes in %SdFat are SdFat, SdFatSoftSpi, SdFatLibSpi,
+SdBaseFile, SdFile, File, StdioStream, \ref fstream, \ref ifstream,
+and \ref ofstream.
 
-The SdFat class maintains a FAT volume, a current working directory, 
-and simplifies initialization of other classes.
+The SdFat, SdFatLibSpi, and SdFatSoftSpi classes maintain a FAT volume,
+a current working directory, and simplifies initialization of other classes.
+The SdFat class uses a fast custom hardware SPI implementation. The
+SdFatLibSpi class uses the standard Arduino SPI library.  The SdFatSoftSpi
+class uses software SPI.
 
 The SdBaseFile class provides basic file access functions such as open(),
 binary read(), binary write(), close(), remove(), and sync(). SdBaseFile
@@ -47,11 +52,11 @@ The SdFile class has all the SdBaseFile class functions plus the Arduino
 Print class functions.
 
 The File class has all the SdBaseFile functions plus the functions in
-the Arduino SD.h File class. This provides compatibility with the 
+the Arduino SD.h File class. This provides compatibility with the
 Arduino SD.h library.
 
 The StdioStream class implements functions similar to Linux/Unix standard
-buffered input/output. 
+buffered input/output.
 
 The \ref fstream class implements C++ iostreams for both reading and writing
 text files.
@@ -60,6 +65,14 @@ The \ref ifstream class implements C++ iostreams for reading text files.
 
 The \ref ofstream class implements C++ iostreams for writing text files.
 
+The classes \ref ifstream, \ref ofstream, \ref istream, and \ref ostream
+follow the C++ \ref iostream standard when possible.
+
+There are many tutorials and much documentation about using C++ iostreams
+on the web.
+
+http://www.cplusplus.com/  is a good C++ site for learning iostreams.
+
 The classes \ref ibufstream and \ref obufstream format and parse character
  strings in memory buffers.
 
@@ -72,7 +85,7 @@ developed to test %SdFat and illustrate its use.
 \section Install Installation
 
 You must manually install SdFat by copying the SdFat folder from the download
-package to the Arduino libraries folder in you sketch book.
+package to the Arduino libraries folder in you sketch folder.
 
 See the Manual installation section of this guide.
 
@@ -81,31 +94,77 @@ http://arduino.cc/en/Guide/Libraries
 \section SDconfig SdFat Configuration
 
 Several configuration options may be changed by editing the SdFatConfig.h
-file in the SdFat folder.
+file in the %SdFat folder.
 
-Set SD_FILE_USES_STREAM nonzero to use Stream instead of Print for SdFile.
-Using Stream will use more flash.
+Set USE_LONG_FILE_NAMES nonzero to enable Long %File Names.  By default,
+Long %File Names are enabled. For the leanest fastest library disable
+Long %File Names.  Long %File names require extra flash but no extra RAM.
+Opening Long %File Names can be slower than opening Short %File Names.
+Data read and write performance is not changed by the type of %File Name.
 
-To enable SD card CRC checking set USE_SD_CRC nonzero.
+Set SD_SPI_CONFIGURATION to enable various SPI options.  The SdFatSoftSpi
+and SdFatLibSpi classes can be enabled. SdFatLibSpi uses the standard
+Arduino SPI library and SdFatSoftSpi uses software SPI.
 
-To use multiple SD cards set USE_MULTIPLE_CARDS nonzero.
+To enable SD card CRC checking set USE_SD_CRC nonzero.
 
 Set FAT12_SUPPORT nonzero to enable use of FAT12 volumes.
 FAT12 has not been well tested and requires additional flash.
 
-Set USE_ARDUINO_SPI_LIBRARY nonzero to force use of Arduino Standard
-SPI library. This will override native and software SPI for all boards.
-
-Use of software SPI can be enabled for selected boards by setting the symbols
-AVR_SOFT_SPI, DUE_SOFT_SPI, LEONARDO_SOFT_SPI, MEGA_SOFT_SPI,
-and TEENSY3_SOFT_SPI.
-
 Set ENABLE_SPI_TRANSACTION nonzero to enable the SPI transaction feature
 of the standard Arduino SPI library.  You must include SPI.h in your
-sketches when ENABLE_SPI_TRANSACTION is nonzero.
+programs when ENABLE_SPI_TRANSACTION is nonzero.
 
 Set ENABLE_SPI_YIELD nonzero to enable release of the SPI bus during
-SD card busy waits.  
+SD card busy waits.
+
+\section SDPath Paths and Working Directories
+
+Relative paths in SdFat are resolved in a manner similar to Windows.
+
+Each instance of SdFat has a current directory.  In SdFat this directory
+is called the volume working directory, vwd.  Initially this directory is
+the root directory for the volume.
+
+The volume working directory is changed by calling SdFat::chdir(path).
+
+The call sd.chdir("/2014") will change the volume working directory
+for sd to "/2014", assuming "/2014" exists.
+
+Relative paths for SdFat member functions are resolved by starting at
+the volume working directory.
+
+For example, the call sd.mkdir("April") will create the directory
+"/2014/April" assuming the volume working directory is "/2014".
+
+SdFat has a current working directory, cwd, that is used to resolve paths
+for file.open() calls.
+
+For a single SD card the current working directory is always the volume
+working directory for that card.
+
+For multiple SD cards the current working directory is set to the volume
+working directory of a card by calling the SdFat::chvol() member function.
+The chvol() call is like the Windows \<drive letter>: command.
+
+The call sd2.chvol() will set the current working directory to the volume
+working directory for sd2.
+
+If the volume working directory for sd2 is "/music" the call
+
+file.open("BigBand.wav", O_READ);
+
+will then open "/music/BigBand.wav" on sd2.
+
+The following functions are used to change or get current directories.
+See the html documentation for more information.
+@code
+bool SdFat::chdir(bool set_cwd = false);
+bool SdFat::chdir(const char* path, bool set_cwd = false);
+void SdFat::chvol();
+SdBaseFile* SdFat::vwd();
+static SdBaseFile* SdBaseFile::cwd();
+@endcode
 
 \section SDcard SD\SDHC Cards
 
@@ -126,7 +185,7 @@ limited RAM.
 \section Hardware Hardware Configuration
 
 %SdFat was developed using an
-<A HREF = "http://www.adafruit.com/"> Adafruit Industries</A> 
+<A HREF = "http://www.adafruit.com/"> Adafruit Industries</A>
 Data Logging Shield.
 
 The hardware interface to the SD card should not use a resistor based level
@@ -140,15 +199,78 @@ uses a 74AHC125N.  Gravitech sells SD and MicroSD Card Adapters based on the
 74LCX245.
 
 If you are using a resistor based level shifter and are having problems try
-setting the SPI bus frequency to 4 MHz.  This can be done by using 
+setting the SPI bus frequency to 4 MHz.  This can be done by using
 card.init(SPI_HALF_SPEED) to initialize the SD card.
 
+A feature to use software SPI is available.  Software SPI is slower
+than hardware SPI but allows any digital pins to be used.  See
+SdFatConfig.h for software SPI definitions.
+
 \section comment Bugs and Comments
 
-If you wish to report bugs or have comments, send email to fat16lib@sbcglobal.net.
+If you wish to report bugs or have comments, send email to
+fat16lib@sbcglobal.net.  If possible, include a simple program that illustrates
+the bug or problem.
+
+\section Trouble Troubleshooting
+
+The two example programs QuickStart, and SdInfo are useful for troubleshooting.
+
+A message like this from SdInfo with erorCode 0X1 indicates the SD card
+is not seen by SdFat.  This is often caused by a wiring error and reformatting
+the card will not solve the problem.
+<PRE>
+cardBegin failed
+SD errorCode: 0X1
+SD errorData: 0XFF
+</PRE>
+Here is a similar message from QuickStart:
+<PRE>
+SD initialization failed.
+Do not reformat the card!
+Is the card correctly inserted?
+Is chipSelect set to the correct value?
+Does another SPI device need to be disabled?
+Is there a wiring/soldering problem?
+
+errorCode: 0x1, errorData: 0xff
+</PRE> 
+Here is a message from QuickStart that indicates a formatting problem:
+<PRE>
+Card successfully initialized.
+Can't find a valid FAT16/FAT32 partition.
+Try reformatting the card.  For best results use
+the SdFormatter program in SdFat/examples or download
+and use SDFormatter from www.sdcard.org/downloads.
+</PRE>
+
+The best source of recent information and help is the Arduino forum.
+
+http://arduino.cc/forum/
+
+Also search the Adafruit forum.
+
+http://forums.adafruit.com/
+
+If you are using a Teensy try.
+
+http://forum.pjrc.com/forum.php
 
 \section SdFatClass SdFat Usage
 
+SdFat supports Long File Names.  Long names in SdFat are limited to 7-bit
+ASCII characters in the range 0X20 - 0XFE The following are reserved characters:
+    <ul>
+    <li>< (less than)
+    <li>> (greater than)
+    <li>: (colon)
+    <li>" (double quote)
+    <li>/ (forward slash)
+    <li>\ (backslash)
+    <li>| (vertical bar or pipe)
+    <li>? (question mark)
+    <li>* (asterisk)
+    </ul>
 %SdFat uses a slightly restricted form of short names.
 Short names are limited to 8 characters followed by an optional period (.)
 and extension of up to 3 characters.  The characters may be any combination
@@ -157,13 +279,15 @@ of letters and digits.  The following special characters are also allowed:
 $ % ' - _ @ ~ ` ! ( ) { } ^ # &
 
 Short names are always converted to upper case and their original case
-value is lost.
+value is lost.  Files that have a base-name where all characters have the
+same case and an extension where all characters have the same case will
+display properly.  Examples this type name are UPPER.low, lower.TXT,
+UPPER.TXT, and lower.txt. 
 
 An application which writes to a file using print(), println() or
-\link SdFile::write write() \endlink must call \link SdFile::sync() sync() \endlink
-at the appropriate time to force data and directory information to be written
-to the SD Card.  Data and directory information are also written to the SD card
-when \link SdFile::close() close() \endlink is called.
+\link SdFile::write write() \endlink must close the file or call
+\link SdFile::sync() sync() \endlink at the appropriate time to
+force data and directory information to be written to the SD Card.
 
 Applications must use care calling \link SdFile::sync() sync() \endlink
 since 2048 bytes of I/O is required to update file and
@@ -182,8 +306,8 @@ SDFormatter which can be downloaded from:
 
 http://www.sdcard.org/downloads
 
-A formatter sketch, SdFormatter.ino, is included in the
-%SdFat/examples/SdFormatter directory.  This sketch attempts to
+A formatter program, SdFormatter.ino, is included in the
+%SdFat/examples/SdFormatter directory.  This program attempts to
 emulate SD Association's SDFormatter.
 
 SDFormatter aligns flash erase boundaries with file
@@ -194,7 +318,7 @@ very small cards as FAT12.  Use the SdFat formatter to force FAT16
 formatting of small cards.
 
 Do not format the SD card with an OS utility, OS utilities do not format SD
-cards in conformance with the SD standard. 
+cards in conformance with the SD standard.
 
 You should use a freshly formatted SD card for best performance.  FAT
 file systems become slower if many files have been created and deleted.
@@ -209,7 +333,7 @@ A number of examples are provided in the SdFat/examples folder.
 See the html documentation for a list.
 
 To access these examples from the Arduino development environment
-go to:  %File -> Examples -> %SdFat -> \<Sketch Name\>
+go to:  %File -> Examples -> %SdFat -> \<program Name\>
 
 Compile, upload to your Arduino and click on Serial Monitor to run
 the example.
@@ -232,13 +356,15 @@ formating - Print a table with various formatting options.
 
 getline - Example of getline from section 27.7.1.3 of the C++ standard.
 
+LongFileName - Example use of openNext, printName, and open by index.
+
 LowLatencyLogger - A modifiable data logger for higher data rates.
 
 OpenNext - Open all files in the root dir and print their filename.
 
 PrintBenchmark - A simple benchmark for printing to a text file.
 
-QuickStart - A sketch to quickly test your SD card and SD shield/module.
+QuickStart - A program to quickly test your SD card and SD shield/module.
 
 RawWrite - A test of raw write functions for contiguous files.
 
@@ -248,17 +374,19 @@ ReadWriteSdFat - SdFat version of Arduino SD ReadWrite example.
 
 rename - A demo of SdFat::rename(old, new) and SdFile::rename(dirFile, newPath).
 
-SdFormatter - This sketch will format an SD or SDHC card.
+SdFormatter - This program will format an SD or SDHC card.
+
+SoftwareSpi - Simple demonstration of the SdFatSoftSpi template class.
 
 SdInfo - Initialize an SD card and analyze its structure for trouble shooting.
 
 StdioBench - Demo and test of stdio style stream.
 
-StreamParseInt - Simple demo of parseInt() Stream member function.
+StreamParseInt - Demo of the SD.h API and the File class parseInt() function.
 
-StressTest - Create and write files until the SD is full.
+ThreeCards - Demonstrate simultaneous use of SdFat, SdFatLibSpi, SdFatSoftSpi.
 
 Timestamp - Sets file create, modify, and access timestamps.
 
 TwoCards - Example using two SD cards.
- */  
+ */

+ 0 - 83
SdFat/SdFile.cpp

@@ -1,83 +0,0 @@
-/* Arduino SdFat Library
- * Copyright (C) 2012 by William Greiman
- *
- * This file is part of the Arduino SdFat Library
- *
- * This Library is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This Library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with the Arduino SdFat Library.  If not, see
- * <http://www.gnu.org/licenses/>.
- */
-#include <SdFile.h>
-/**  Create a file object and open it in the current working directory.
- *
- * \param[in] path A path with a valid 8.3 DOS name for a file to be opened.
- *
- * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive
- * OR of open flags. see SdBaseFile::open(SdBaseFile*, const char*, uint8_t).
- */
-SdFile::SdFile(const char* path, uint8_t oflag) : SdBaseFile(path, oflag) {
-}
-//------------------------------------------------------------------------------
-/** Write data to an open file.
- *
- * \note Data is moved to the cache but may not be written to the
- * storage device until sync() is called.
- *
- * \param[in] buf Pointer to the location of the data to be written.
- *
- * \param[in] nbyte Number of bytes to write.
- *
- * \return For success write() returns the number of bytes written, always
- * \a nbyte.  If an error occurs, write() returns -1.  Possible errors
- * include write() is called before a file has been opened, write is called
- * for a read-only file, device is full, a corrupt file system or an I/O error.
- *
- */
-int SdFile::write(const void* buf, size_t nbyte) {
-  return SdBaseFile::write(buf, nbyte);
-}
-//------------------------------------------------------------------------------
-/** Write a byte to a file. Required by the Arduino Print class.
- * \param[in] b the byte to be written.
- * Use getWriteError to check for errors.
- * \return 1 for success and 0 for failure.
- */
-size_t SdFile::write(uint8_t b) {
-  return SdBaseFile::write(&b, 1) == 1 ? 1 : 0;
-}
-//------------------------------------------------------------------------------
-/** Write a string to a file. Used by the Arduino Print class.
- * \param[in] str Pointer to the string.
- * Use getWriteError to check for errors.
- * \return count of characters written for success or -1 for failure.
- */
-int SdFile::write(const char* str) {
-  return SdBaseFile::write(str, strlen(str));
-}
-//------------------------------------------------------------------------------
-/** Write a PROGMEM string to a file.
- * \param[in] str Pointer to the PROGMEM string.
- * Use getWriteError to check for errors.
- */
-void SdFile::write_P(PGM_P str) {
-  for (uint8_t c; (c = pgm_read_byte(str)); str++) write(c);
-}
-//------------------------------------------------------------------------------
-/** Write a PROGMEM string followed by CR/LF to a file.
- * \param[in] str Pointer to the PROGMEM string.
- * Use getWriteError to check for errors.
- */
-void SdFile::writeln_P(PGM_P str) {
-  write_P(str);
-  write_P(PSTR("\r\n"));
-}

+ 0 - 257
SdFat/SdFile.h

@@ -1,257 +0,0 @@
-/* Arduino SdFat Library
- * Copyright (C) 2012 by William Greiman
- *
- * This file is part of the Arduino SdFat Library
- *
- * This Library is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This Library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with the Arduino SdFat Library.  If not, see
- * <http://www.gnu.org/licenses/>.
- */
-/**
- * \file
- * \brief SdFile class
- */
-#ifndef SdFile_h
-#define SdFile_h
-#include <limits.h>
-#include <SdBaseFile.h>
-//------------------------------------------------------------------------------
-/**
- * \class SdFile
- * \brief SdBaseFile with Arduino Stream.
- */
-#if SD_FILE_USES_STREAM
-class SdFile : public SdBaseFile, public Stream {
-#else  // SD_FILE_USES_STREAM
-class SdFile : public SdBaseFile, public Print {
-#endif  // SD_FILE_USES_STREAM
- public:
-  SdFile() {}
-  SdFile(const char* name, uint8_t oflag);
-#if DESTRUCTOR_CLOSES_FILE
-  ~SdFile() {}
-#endif  // DESTRUCTOR_CLOSES_FILE
-  /** \return number of bytes available from the current position to EOF
-   *   or INT_MAX if more than INT_MAX bytes are available.
-   */
-  int available() {
-    uint32_t n = SdBaseFile::available();
-    return n > INT_MAX ? INT_MAX : n;
-  }
-  /** Ensure that any bytes written to the file are saved to the SD card. */
-  void flush() {SdBaseFile::sync();}
-  /** Return the next available byte without consuming it.
-   *
-   * \return The byte if no error and not at eof else -1;
-   */  
-  int peek() {return SdBaseFile::peek();}
-  /** Read the next byte from a file.
-   *
-   * \return For success return the next byte in the file as an int.
-   * If an error occurs or end of file is reached return -1.
-   */  
-  int read() {return SdBaseFile::read();}
-  /** Read data from a file starting at the current position.
-   *
-   * \param[out] buf Pointer to the location that will receive the data.
-   *
-   * \param[in] nbyte Maximum number of bytes to read.
-   *
-   * \return For success read() returns the number of bytes read.
-   * A value less than \a nbyte, including zero, will be returned
-   * if end of file is reached.
-   * If an error occurs, read() returns -1.  Possible errors include
-   * read() called before a file has been opened, corrupt file system
-   * or an I/O error occurred.
-   */  
-  int read(void* buf, size_t nbyte) {return SdBaseFile::read(buf, nbyte);}
-  /** \return value of writeError */
-  bool getWriteError() {return SdBaseFile::getWriteError();}
-  /** Set writeError to zero */
-  void clearWriteError() {SdBaseFile::clearWriteError();}
-  size_t write(uint8_t b);
-
-  int write(const char* str);
-  /** Write data to an open file.
-   *
-   * \note Data is moved to the cache but may not be written to the
-   * storage device until sync() is called.
-   *
-   * \param[in] buf Pointer to the location of the data to be written.
-   *
-   * \param[in] nbyte Number of bytes to write.
-   *
-   * \return For success write() returns the number of bytes written, always
-   * \a nbyte.  If an error occurs, write() returns -1.  Possible errors
-   * include write() is called before a file has been opened, write is called
-   * for a read-only file, device is full, a corrupt file system or an 
-   * I/O error.
-   */  
-  int write(const void* buf, size_t nbyte);
-  /** Write data to an open file.  Form required by Print.
-   *
-   * \note Data is moved to the cache but may not be written to the
-   * storage device until sync() is called.
-   *
-   * \param[in] buf Pointer to the location of the data to be written.
-   *
-   * \param[in] size Number of bytes to write.
-   *
-   * \return For success write() returns the number of bytes written, always
-   * \a nbyte.  If an error occurs, write() returns -1.  Possible errors
-   * include write() is called before a file has been opened, write is called
-   * for a read-only file, device is full, a corrupt file system or an 
-   * I/O error.
-   */  
-  size_t write(const uint8_t *buf, size_t size) {
-    return SdBaseFile::write(buf, size);}
-  void write_P(PGM_P str);
-  void writeln_P(PGM_P str);
-};
-//------------------------------------------------------------------------------
-/** Arduino SD.h style flag for open for read. */
-#define FILE_READ O_READ
-/** Arduino SD.h style flag for open at EOF for read/write with create. */
-#define FILE_WRITE (O_RDWR | O_CREAT | O_AT_END)
-/**
- * \class File
- * \brief Arduino SD.h style File API
- */
-class File : public SdBaseFile, public Stream {
- public:
- /** The parenthesis operator.
-   *
-   * \return true if a file is open.
-   */
-  operator bool() {return isOpen();}
-  /** \return number of bytes available from the current position to EOF
-   *   or INT_MAX if more than INT_MAX bytes are available.
-   */
-  int available() {
-    uint32_t n = SdBaseFile::available();
-    return n > INT_MAX ? INT_MAX : n;
-  }
-  /** Set writeError to zero */
-  void clearWriteError() {SdBaseFile::clearWriteError();}
-  /** Ensure that any bytes written to the file are saved to the SD card. */
-  void flush() {sync();}
-  /** \return value of writeError */
-  bool getWriteError() {return SdBaseFile::getWriteError();}
-   /** This function reports if the current file is a directory or not.
-   * \return true if the file is a directory.
-   */  
-  bool isDirectory() {return isDir();}
-  /** \return a pointer to the file's name. */
-  char* name() {
-    m_name[0] = 0;
-    getFilename(m_name);
-    return m_name;
-  }
-  /** Return the next available byte without consuming it.
-   *
-   * \return The byte if no error and not at eof else -1;
-   */  
-  int peek() {return SdBaseFile::peek();}
-  /** \return the current file position. */
-  uint32_t position() {return curPosition();}
-  /** Opens the next file or folder in a directory.
-   *
-   * \param[in] mode open mode flags.
-   * \return a File object.
-   */
-  File openNextFile(uint8_t mode = O_READ) {
-    File tmpFile;
-    tmpFile.openNext(this, mode);
-    return tmpFile;
-  }
-  /** Read the next byte from a file.
-   *
-   * \return For success return the next byte in the file as an int.
-   * If an error occurs or end of file is reached return -1.
-   */  
-  int read() {return SdBaseFile::read();}
-  /** Read data from a file starting at the current position.
-   *
-   * \param[out] buf Pointer to the location that will receive the data.
-   *
-   * \param[in] nbyte Maximum number of bytes to read.
-   *
-   * \return For success read() returns the number of bytes read.
-   * A value less than \a nbyte, including zero, will be returned
-   * if end of file is reached.
-   * If an error occurs, read() returns -1.  Possible errors include
-   * read() called before a file has been opened, corrupt file system
-   * or an I/O error occurred.
-   */  
-  int read(void* buf, size_t nbyte) {return SdBaseFile::read(buf, nbyte);}
-  /** Rewind a file if it is a directory */
-  void rewindDirectory() {
-    if (isDir()) rewind();
-  }
-  /** 
-   * Seek to a new position in the file, which must be between
-   * 0 and the size of the file (inclusive).
-   *
-   * \param[in] pos the new file position.
-   * \return true for success else false.
-   */
-  bool seek(uint32_t pos) {return seekSet(pos);}
-  /** \return the file's size. */
-  uint32_t size() {return fileSize();}
-  /** Write a byte to a file. Required by the Arduino Print class.
-   * \param[in] b the byte to be written.
-   * Use getWriteError to check for errors.
-   * \return 1 for success and 0 for failure.
-   */
-  size_t write(uint8_t b) {
-    return SdBaseFile::write(&b, 1) == 1 ? 1 : 0;
-  }
-  /** Write data to an open file.
-   *
-   * \note Data is moved to the cache but may not be written to the
-   * storage device until sync() is called.
-   *
-   * \param[in] buf Pointer to the location of the data to be written.
-   *
-   * \param[in] nbyte Number of bytes to write.
-   *
-   * \return For success write() returns the number of bytes written, always
-   * \a nbyte.  If an error occurs, write() returns -1.  Possible errors
-   * include write() is called before a file has been opened, write is called
-   * for a read-only file, device is full, a corrupt file system or an 
-   * I/O error.
-   */  
-  int write(const void* buf, size_t nbyte);
-  /** Write data to an open file.  Form required by Print.
-   *
-   * \note Data is moved to the cache but may not be written to the
-   * storage device until sync() is called.
-   *
-   * \param[in] buf Pointer to the location of the data to be written.
-   *
-   * \param[in] size Number of bytes to write.
-   *
-   * \return For success write() returns the number of bytes written, always
-   * \a nbyte.  If an error occurs, write() returns -1.  Possible errors
-   * include write() is called before a file has been opened, write is called
-   * for a read-only file, device is full, a corrupt file system or an 
-   * I/O error.
-   */  
-  size_t write(const uint8_t *buf, size_t size) {
-    return SdBaseFile::write(buf, size);
-  }
-
- private:
-  char m_name[13];
-};
-#endif  // SdFile_h

+ 97 - 19
SdFat/SdInfo.h

@@ -1,7 +1,7 @@
-/* Arduino Sd2Card Library
+/* Arduino SdSpiCard Library
  * Copyright (C) 2012 by William Greiman
  *
- * This file is part of the Arduino Sd2Card Library
+ * This file is part of the Arduino SdSpiCard Library
  *
  * This Library is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -14,7 +14,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with the Arduino Sd2Card Library.  If not, see
+ * along with the Arduino SdSpiCard Library.  If not, see
  * <http://www.gnu.org/licenses/>.
  */
 #ifndef SdInfo_h
@@ -31,6 +31,72 @@
 //
 // http://www.sdcard.org/developers/tech/sdcard/pls/simplified_specs
 //------------------------------------------------------------------------------
+// SD card errors
+/** timeout error for command CMD0 (initialize card in SPI mode) */
+uint8_t const SD_CARD_ERROR_CMD0 = 0X1;
+/** CMD8 was not accepted - not a valid SD card*/
+uint8_t const SD_CARD_ERROR_CMD8 = 0X2;
+/** card returned an error response for CMD12 (stop multiblock read) */
+uint8_t const SD_CARD_ERROR_CMD12 = 0X3;
+/** card returned an error response for CMD17 (read block) */
+uint8_t const SD_CARD_ERROR_CMD17 = 0X4;
+/** card returned an error response for CMD18 (read multiple block) */
+uint8_t const SD_CARD_ERROR_CMD18 = 0X5;
+/** card returned an error response for CMD24 (write block) */
+uint8_t const SD_CARD_ERROR_CMD24 = 0X6;
+/**  WRITE_MULTIPLE_BLOCKS command failed */
+uint8_t const SD_CARD_ERROR_CMD25 = 0X7;
+/** card returned an error response for CMD58 (read OCR) */
+uint8_t const SD_CARD_ERROR_CMD58 = 0X8;
+/** SET_WR_BLK_ERASE_COUNT failed */
+uint8_t const SD_CARD_ERROR_ACMD23 = 0X9;
+/** ACMD41 initialization process timeout */
+uint8_t const SD_CARD_ERROR_ACMD41 = 0XA;
+/** card returned a bad CSR version field */
+uint8_t const SD_CARD_ERROR_BAD_CSD = 0XB;
+/** erase block group command failed */
+uint8_t const SD_CARD_ERROR_ERASE = 0XC;
+/** card not capable of single block erase */
+uint8_t const SD_CARD_ERROR_ERASE_SINGLE_BLOCK = 0XD;
+/** Erase sequence timed out */
+uint8_t const SD_CARD_ERROR_ERASE_TIMEOUT = 0XE;
+/** card returned an error token instead of read data */
+uint8_t const SD_CARD_ERROR_READ = 0XF;
+/** read CID or CSD failed */
+uint8_t const SD_CARD_ERROR_READ_REG = 0X10;
+/** timeout while waiting for start of read data */
+uint8_t const SD_CARD_ERROR_READ_TIMEOUT = 0X11;
+/** card did not accept STOP_TRAN_TOKEN */
+uint8_t const SD_CARD_ERROR_STOP_TRAN = 0X12;
+/** card returned an error token as a response to a write operation */
+uint8_t const SD_CARD_ERROR_WRITE = 0X13;
+/** attempt to write protected block zero */
+uint8_t const SD_CARD_ERROR_WRITE_BLOCK_ZERO = 0X14;  // REMOVE - not used
+/** card did not go ready for a multiple block write */
+uint8_t const SD_CARD_ERROR_WRITE_MULTIPLE = 0X15;
+/** card returned an error to a CMD13 status check after a write */
+uint8_t const SD_CARD_ERROR_WRITE_PROGRAMMING = 0X16;
+/** timeout occurred during write programming */
+uint8_t const SD_CARD_ERROR_WRITE_TIMEOUT = 0X17;
+/** incorrect rate selected */
+uint8_t const SD_CARD_ERROR_SCK_RATE = 0X18;
+/** init() not called */
+uint8_t const SD_CARD_ERROR_INIT_NOT_CALLED = 0X19;
+/** card returned an error for CMD59 (CRC_ON_OFF) */
+uint8_t const SD_CARD_ERROR_CMD59 = 0X1A;
+/** invalid read CRC */
+uint8_t const SD_CARD_ERROR_READ_CRC = 0X1B;
+/** SPI DMA error */
+uint8_t const SD_CARD_ERROR_SPI_DMA = 0X1C;
+//------------------------------------------------------------------------------
+// card types
+/** Standard capacity V1 SD card */
+uint8_t const SD_CARD_TYPE_SD1  = 1;
+/** Standard capacity V2 SD card */
+uint8_t const SD_CARD_TYPE_SD2  = 2;
+/** High Capacity SD card */
+uint8_t const SD_CARD_TYPE_SDHC = 3;
+//------------------------------------------------------------------------------
 // SPI divisor constants
 /** Set SCK to max rate of F_CPU/2. */
 uint8_t const SPI_FULL_SPEED = 2;
@@ -97,7 +163,7 @@ uint8_t const ACMD23 = 0X17;
 /** SD_SEND_OP_COMD - Sends host capacity support information and
     activates the card's initialization process */
 uint8_t const ACMD41 = 0X29;
-//------------------------------------------------------------------------------
+//==============================================================================
 /** status for card in the ready state */
 uint8_t const R1_READY_STATE = 0X00;
 /** status for card in the idle state */
@@ -114,8 +180,11 @@ uint8_t const WRITE_MULTIPLE_TOKEN = 0XFC;
 uint8_t const DATA_RES_MASK = 0X1F;
 /** write data accepted token */
 uint8_t const DATA_RES_ACCEPTED = 0X05;
-//------------------------------------------------------------------------------
-/** Card IDentification (CID) register */
+//==============================================================================
+/**
+ * \class CID
+ * \brief Card IDentification (CID) register.
+ */
 typedef struct CID {
   // byte 0
   /** Manufacturer ID */
@@ -143,15 +212,18 @@ typedef struct CID {
   /** Manufacturing date month */
   unsigned char mdt_month : 4;
   /** Manufacturing date year low digit */
-  unsigned char mdt_year_low :4;
+  unsigned char mdt_year_low : 4;
   // byte 15
   /** not used always 1 */
   unsigned char always1 : 1;
   /** CRC7 checksum */
   unsigned char crc : 7;
-}__attribute__((packed)) cid_t;
-//------------------------------------------------------------------------------
-/** CSD for version 1.00 cards */
+} __attribute__((packed)) cid_t;
+//==============================================================================
+/**
+ * \class CSDV1
+ * \brief CSD register for version 1.00 cards .
+ */
 typedef struct CSDV1 {
   // byte 0
   unsigned char reserved1 : 6;
@@ -171,7 +243,7 @@ typedef struct CSDV1 {
   unsigned char c_size_high : 2;
   unsigned char reserved2 : 2;
   unsigned char dsr_imp : 1;
-  unsigned char read_blk_misalign :1;
+  unsigned char read_blk_misalign : 1;
   unsigned char write_blk_misalign : 1;
   unsigned char read_bl_partial : 1;
   // byte 7
@@ -179,7 +251,7 @@ typedef struct CSDV1 {
   // byte 8
   unsigned char vdd_r_curr_max : 3;
   unsigned char vdd_r_curr_min : 3;
-  unsigned char c_size_low :2;
+  unsigned char c_size_low : 2;
   // byte 9
   unsigned char c_size_mult_high : 2;
   unsigned char vdd_w_cur_max : 3;
@@ -211,9 +283,12 @@ typedef struct CSDV1 {
   // byte 15
   unsigned char always1 : 1;
   unsigned char crc : 7;
-}__attribute__((packed)) csd1_t;
-//------------------------------------------------------------------------------
-/** CSD for version 2.00 cards */
+} __attribute__((packed)) csd1_t;
+//==============================================================================
+/**
+ * \class CSDV2
+ * \brief CSD register for version 2.00 cards.
+ */
 typedef struct CSDV2 {
   // byte 0
   unsigned char reserved1 : 6;
@@ -237,7 +312,7 @@ typedef struct CSDV2 {
   unsigned char reserved2 : 4;
   unsigned char dsr_imp : 1;
   /** fixed to 0 */
-  unsigned char read_blk_misalign :1;
+  unsigned char read_blk_misalign : 1;
   /** fixed to 0 */
   unsigned char write_blk_misalign : 1;
   /** fixed to 0 - no partial read */
@@ -293,9 +368,12 @@ typedef struct CSDV2 {
   unsigned char always1 : 1;
   /** checksum */
   unsigned char crc : 7;
-}__attribute__((packed)) csd2_t;
-//------------------------------------------------------------------------------
-/** union of old and new style CSD register */
+} __attribute__((packed)) csd2_t;
+//==============================================================================
+/**
+ * \class csd_t
+ * \brief Union of old and new style CSD register.
+ */
 union csd_t {
   csd1_t v1;
   csd2_t v2;

+ 280 - 83
SdFat/SdSpi.h

@@ -17,127 +17,319 @@
  * along with the Arduino SdSpi Library.  If not, see
  * <http://www.gnu.org/licenses/>.
  */
- /**
- * \file
- * \brief SdSpi class for V2 SD/SDHC cards
- */
+/**
+* \file
+* \brief SdSpi class for V2 SD/SDHC cards
+*/
 #ifndef SdSpi_h
 #define SdSpi_h
 #include <Arduino.h>
-#include <SdFatConfig.h>
-
-#if !USE_ARDUINO_SPI_LIBRARY
-// AVR Arduinos
-#ifdef __AVR__
-#if AVR_SOFT_SPI
-#define USE_SOFTWARE_SPI 1
-#elif LEONARDO_SOFT_SPI && defined(__AVR_ATmega32U4__) && !defined(CORE_TEENSY)
-#define USE_SOFTWARE_SPI 1
-#elif MEGA_SOFT_SPI && (defined(__AVR_ATmega1280__)\
-      ||defined(__AVR_ATmega2560__))
-#define USE_SOFTWARE_SPI 1
-#else  // USE_SOFTWARE_SPI
-#define USE_NATIVE_AVR_SPI 1
-#endif  // USE_SOFTWARE_SPI
-#endif  // __AVR__
-// Due
-#if defined(__arm__) && !defined(CORE_TEENSY)
-#if DUE_SOFT_SPI
-#define USE_SOFTWARE_SPI 1
-#else  // DUE_SOFT_SPI
-/** Nonzero - use native SAM3X SPI */
-#define USE_NATIVE_SAM3X_SPI 1
-#endif  // DUE_SOFT_SPI
-#endif  // defined(__arm__) && !defined(CORE_TEENSY)
-// Teensy 3.0
-#if defined(__arm__) && defined(CORE_TEENSY)
-#if TEENSY3_SOFT_SPI
-#define USE_SOFTWARE_SPI 1
-#else  // TEENSY3_SOFT_SPI
-/** Nonzero - use native MK20DX128 SPI */
-#define USE_NATIVE_TEENSY3_SPI 1
-#endif  // TEENSY3_SOFT_SPI
-#endif  // defined(__arm__) && defined(CORE_TEENSY)
-#endif  // !USE_ARDUINO_SPI_LIBRARY
-
-#ifndef USE_SOFTWARE_SPI
-#define USE_SOFTWARE_SPI 0
-#endif  //  USE_SOFTWARE_SPI
-
-#ifndef USE_NATIVE_AVR_SPI
-#define USE_NATIVE_AVR_SPI 0
-#endif
+#include "SdFatConfig.h"
 
-#ifndef USE_NATIVE_SAM3X_SPI
-#define USE_NATIVE_SAM3X_SPI 0
-#endif  // USE_NATIVE_SAM3X_SPI
-
-#ifndef USE_NATIVE_TEENSY3_SPI
-#define USE_NATIVE_TEENSY3_SPI 0
-#endif  // USE_NATIVE_TEENSY3_SPI
 //------------------------------------------------------------------------------
-// define default chip select pin
-//
-#if !USE_SOFTWARE_SPI
-/** The default chip select pin for the SD card is SS. */
-uint8_t const  SD_CHIP_SELECT_PIN = SS;
-#else  // USE_AVR_SOFTWARE_SPI
-/** SPI chip select pin for software SPI. */
-uint8_t const SD_CHIP_SELECT_PIN = SOFT_SPI_CS_PIN;
-#endif  // USE_AVR_SOFTWARE_SPI
-
+/**
+ * \class SdSpiBase
+ * \brief Virtual SPI class for access to SD and SDHC flash memory cards.
+ */
+class SdSpiBase {
+ public:
+  /** Initialize the SPI bus */
+  virtual void begin() = 0;
+  /** Set SPI options for access to SD/SDHC cards.
+   *
+   * \param[in] divisor SCK clock divider relative to the system clock.
+   */
+  virtual void init(uint8_t divisor);
+  /** Receive a byte.
+   *
+   * \return The byte.
+   */
+  virtual uint8_t receive() = 0;
+  /** Receive multiple bytes.
+   *
+   * \param[out] buf Buffer to receive the data.
+   * \param[in] n Number of bytes to receive.
+   *
+   * \return Zero for no error or nonzero error code.
+   */
+  virtual uint8_t receive(uint8_t* buf, size_t n) = 0;
+  /** Send a byte.
+   *
+   * \param[in] data Byte to send
+   */
+  virtual void send(uint8_t data) = 0;
+  /** Send multiple bytes.
+  *
+  * \param[in] buf Buffer for data to be sent.
+  * \param[in] n Number of bytes to send.
+  */
+  virtual void send(const uint8_t* buf, size_t n) = 0;
+  /** \return true if hardware SPI else false */
+  virtual bool useSpiTransactions() = 0;
+};
 //------------------------------------------------------------------------------
 /**
  * \class SdSpi
  * \brief SPI class for access to SD and SDHC flash memory cards.
  */
+#if SD_SPI_CONFIGURATION >= 3
+class SdSpi : public SdSpiBase {
+#else  // SD_SPI_CONFIGURATION >= 3
 class SdSpi {
+#endif  // SD_SPI_CONFIGURATION >= 3
  public:
   /** Initialize the SPI bus */
   void begin();
   /** Set SPI options for access to SD/SDHC cards.
-   * 
-   * \param[in] spiDivisor SCK clock divider relative to the system clock.
+   *
+   * \param[in] divisor SCK clock divider relative to the system clock.
    */
-  void init(uint8_t spiDivisor);
-  /** Receive a byte. 
+  void init(uint8_t divisor);
+  /** Receive a byte.
    *
    * \return The byte.
    */
   uint8_t receive();
   /** Receive multiple bytes.
    *
-   * \param[out] buf Buffer to receive the data.   
+   * \param[out] buf Buffer to receive the data.
    * \param[in] n Number of bytes to receive.
    *
    * \return Zero for no error or nonzero error code.
-   */   
+   */
   uint8_t receive(uint8_t* buf, size_t n);
   /** Send a byte.
    *
    * \param[in] data Byte to send
    */
   void send(uint8_t data);
-   /** Send multiple bytes.
+  /** Send multiple bytes.
    *
-   * \param[in] buf Buffer for data to be sent.   
+   * \param[in] buf Buffer for data to be sent.
    * \param[in] n Number of bytes to send.
-   */   
+   */
   void send(const uint8_t* buf, size_t n);
+  /** \return true - uses SPI transactions */
+  bool useSpiTransactions() {
+    return true;
+  }
+};
+//------------------------------------------------------------------------------
+/**
+ * \class SdSpiLib
+ * \brief Arduino SPI library class for access to SD and SDHC flash
+ *        memory cards.
+ */
+#if SD_SPI_CONFIGURATION >= 3 || SD_SPI_CONFIGURATION == 1 || defined(DOXYGEN)
+#include <SPI.h>
+#if SD_SPI_CONFIGURATION >= 3
+class SdSpiLib : public SdSpiBase {
+#else  // SD_SPI_CONFIGURATION >= 3
+class SdSpiLib {
+#endif  // SD_SPI_CONFIGURATION >= 3
+ public:
+  /**
+   * Initialize SPI pins.
+   */
+  void begin() {
+    SPI.begin();
+  }
+  /** Set SPI options for access to SD/SDHC cards.
+   *
+   * \param[in] divisor SCK clock divider relative to the system clock.
+   */
+  void init(uint8_t divisor) {
+    SPI.setBitOrder(MSBFIRST);
+    SPI.setDataMode(SPI_MODE0);
+#ifndef SPI_CLOCK_DIV128
+    SPI.setClockDivider(divisor);
+#else  // SPI_CLOCK_DIV128
+    int v;
+    if (divisor <= 2) {
+      v = SPI_CLOCK_DIV2;
+    } else  if (divisor <= 4) {
+      v = SPI_CLOCK_DIV4;
+    } else  if (divisor <= 8) {
+      v = SPI_CLOCK_DIV8;
+    } else  if (divisor <= 16) {
+      v = SPI_CLOCK_DIV16;
+    } else  if (divisor <= 32) {
+      v = SPI_CLOCK_DIV32;
+    } else  if (divisor <= 64) {
+      v = SPI_CLOCK_DIV64;
+    } else {
+      v = SPI_CLOCK_DIV128;
+    }
+    SPI.setClockDivider(v);
+#endif  // SPI_CLOCK_DIV128
+  }
+  /** Receive a byte.
+   *
+   * \return The byte.
+   */
+  uint8_t receive() {
+    return SPI.transfer(0XFF);
+  }
+  /** Receive multiple bytes.
+   *
+   * \param[out] buf Buffer to receive the data.
+   * \param[in] n Number of bytes to receive.
+   *
+   * \return Zero for no error or nonzero error code.
+   */
+  uint8_t receive(uint8_t* buf, size_t n) {
+    for (size_t i = 0; i < n; i++) {
+      buf[i] = SPI.transfer(0XFF);
+    }
+    return 0;
+  }
+  /** Send a byte.
+   *
+   * \param[in] b Byte to send
+   */
+  void send(uint8_t b) {
+    SPI.transfer(b);
+  }
+  /** Send multiple bytes.
+   *
+   * \param[in] buf Buffer for data to be sent.
+   * \param[in] n Number of bytes to send.
+   */
+  void send(const uint8_t* buf , size_t n) {
+    for (size_t i = 0; i < n; i++) {
+      SPI.transfer(buf[i]);
+    }
+  }
+  /** \return true - uses SPI transactions */
+  bool useSpiTransactions() {
+    return true;
+  }
+};
+#endif  // SD_SPI_CONFIGURATION >= 3 || SD_SPI_CONFIGURATION == 1
+//------------------------------------------------------------------------------
+#if SD_SPI_CONFIGURATION > 1 || defined(DOXYGEN)
+#include "utility/SoftSPI.h"
+/**
+ * \class SdSpiSoft
+ * \brief Software SPI class for access to SD and SDHC flash memory cards.
+ */
+template<uint8_t MisoPin, uint8_t MosiPin, uint8_t SckPin>
+class SdSpiSoft : public SdSpiBase {
+ public:
+  /**
+   * initialize SPI pins
+   */
+  void begin() {
+    m_spi.begin();
+  }
+  /**
+   * Initialize hardware SPI - dummy for soft SPI
+   * \param[in] divisor SCK divisor - ignored.
+   */
+  void init(uint8_t divisor) {}
+  /** Receive a byte.
+   *
+   * \return The byte.
+   */
+  uint8_t receive() {
+    return m_spi.receive();
+  }
+  /** Receive multiple bytes.
+  *
+  * \param[out] buf Buffer to receive the data.
+  * \param[in] n Number of bytes to receive.
+  *
+  * \return Zero for no error or nonzero error code.
+  */
+  uint8_t receive(uint8_t* buf, size_t n) {
+    for (size_t i = 0; i < n; i++) {
+      buf[i] = receive();
+    }
+    return 0;
+  }
+  /** Send a byte.
+   *
+   * \param[in] data Byte to send
+   */
+  void send(uint8_t data) {
+    m_spi.send(data);
+  }
+  /** Send multiple bytes.
+   *
+   * \param[in] buf Buffer for data to be sent.
+   * \param[in] n Number of bytes to send.
+   */
+  void send(const uint8_t* buf , size_t n) {
+    for (size_t i = 0; i < n; i++) {
+      send(buf[i]);
+    }
+  }
+  /** \return false - no SPI transactions */
+  bool useSpiTransactions() {
+    return false;
+  }
+
+ private:
+  SoftSPI<MisoPin, MosiPin, SckPin, 0> m_spi;
 };
+#endif  // SD_SPI_CONFIGURATION > 1 || defined(DOXYGEN)
+//------------------------------------------------------------------------------
+#if SD_SPI_CONFIGURATION == 0 || SD_SPI_CONFIGURATION >= 3
+/** Default is custom fast SPI. */
+typedef SdSpi SpiDefault_t;
+#elif SD_SPI_CONFIGURATION == 1
+/** Default is Arduino library SPI. */
+typedef SdSpiLib SpiDefault_t;
+#elif SD_SPI_CONFIGURATION == 2
+/** Default is software SPI. */
+typedef SdSpiSoft<SOFT_SPI_MISO_PIN, SOFT_SPI_MOSI_PIN, SOFT_SPI_SCK_PIN>
+SpiDefault_t;
+#else  // SD_SPI_CONFIGURATION == 0 || SD_SPI_CONFIGURATION >= 3
+#error bad SD_SPI_CONFIGURATION
+#endif  // SD_SPI_CONFIGURATION == 0 || SD_SPI_CONFIGURATION >= 3
+//------------------------------------------------------------------------------
+// Use of in-line for AVR to save flash.
+#ifdef __AVR__
+//------------------------------------------------------------------------------
+inline void SdSpi::begin() {
+#ifdef __AVR_ATmega328P__
+  // Save a few bytes for 328 CPU - gcc optimizes single bit '|' to sbi.
+  PORTB |= 1 << 2;  // SS high
+  DDRB  |= 1 << 2;  // SS output mode
+  DDRB  |= 1 << 3;  // MOSI output mode
+  DDRB  |= 1 << 5;  // SCK output mode
+#else  // __AVR_ATmega328P__
+
+  // set SS high - may be chip select for another SPI device
+  digitalWrite(SS, HIGH);
+
+  // SS must be in output mode even it is not chip select
+  pinMode(SS, OUTPUT);
+  pinMode(MOSI, OUTPUT);
+  pinMode(SCK, OUTPUT);
+#endif  // __AVR_ATmega328P__
+}
+//------------------------------------------------------------------------------
+inline void SdSpi::init(uint8_t divisor) {
+  uint8_t b = 2;
+  uint8_t r = 0;
+
+  // See AVR processor documentation.
+  for (; divisor > b && r < 7; b <<= 1, r += r < 5 ? 1 : 2) {}
+  SPCR = (1 << SPE) | (1 << MSTR) | (r >> 1);
+  SPSR = r & 1 ? 0 : 1 << SPI2X;
+}
 //------------------------------------------------------------------------------
-// Use of inline for AVR results in up to 10% better write performance.
-// Inline also save a little flash memory.
-/** inline avr native functions if nonzero. */
-#define USE_AVR_NATIVE_SPI_INLINE 1
-#if USE_NATIVE_AVR_SPI && USE_AVR_NATIVE_SPI_INLINE
 inline uint8_t SdSpi::receive() {
   SPDR = 0XFF;
   while (!(SPSR & (1 << SPIF))) {}
   return SPDR;
 }
+//------------------------------------------------------------------------------
 inline uint8_t SdSpi::receive(uint8_t* buf, size_t n) {
-  if (n-- == 0) return 0;
+  if (n-- == 0) {
+    return 0;
+  }
   SPDR = 0XFF;
   for (size_t i = 0; i < n; i++) {
     while (!(SPSR & (1 << SPIF))) {}
@@ -149,12 +341,16 @@ inline uint8_t SdSpi::receive(uint8_t* buf, size_t n) {
   buf[n] = SPDR;
   return 0;
 }
+//------------------------------------------------------------------------------
 inline void SdSpi::send(uint8_t data) {
   SPDR = data;
   while (!(SPSR & (1 << SPIF))) {}
 }
+//------------------------------------------------------------------------------
 inline void SdSpi::send(const uint8_t* buf , size_t n) {
-  if (n == 0) return;
+  if (n == 0) {
+    return;
+  }
   SPDR = buf[0];
   if (n > 1) {
     uint8_t b = buf[1];
@@ -162,12 +358,13 @@ inline void SdSpi::send(const uint8_t* buf , size_t n) {
     while (1) {
       while (!(SPSR & (1 << SPIF))) {}
       SPDR = b;
-      if (i == n) break;
+      if (i == n) {
+        break;
+      }
       b = buf[i++];
     }
   }
   while (!(SPSR & (1 << SPIF))) {}
 }
-#endif  // USE_NATIVE_AVR_SPI && USE_AVR_NATIVE_SPI_INLINE
+#endif  // __AVR__
 #endif  // SdSpi_h
-

+ 0 - 86
SdFat/SdSpiAVR.cpp

@@ -1,86 +0,0 @@
-/* Arduino SdSpi Library
- * Copyright (C) 2013 by William Greiman
- *
- * This file is part of the Arduino SdSpi Library
- *
- * This Library is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This Library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with the Arduino SdSpi Library.  If not, see
- * <http://www.gnu.org/licenses/>.
- */
-#include <SdSpi.h>
-#if USE_NATIVE_AVR_SPI
-//------------------------------------------------------------------------------
-void SdSpi::begin() {
-  // set SS high - may be chip select for another SPI device
-  digitalWrite(SS, HIGH);
-
-  // SS must be in output mode even it is not chip select
-  pinMode(SS, OUTPUT);
-  pinMode(MISO, INPUT);
-  pinMode(MOSI, OUTPUT);
-  pinMode(SCK, OUTPUT);
-}
-//------------------------------------------------------------------------------
-void SdSpi::init(uint8_t sckDivisor) {
-  uint8_t r = 0;
-
-  for (uint8_t b = 2; sckDivisor > b && r < 6; b <<= 1, r++) {}
-  // See avr processor documentation
-  SPCR = (1 << SPE) | (1 << MSTR) | (r >> 1);
-  SPSR = r & 1 || r == 6 ? 0 : 1 << SPI2X;
-}
-#if !USE_AVR_NATIVE_SPI_INLINE
-//------------------------------------------------------------------------------
-uint8_t SdSpi::receive() {
-  SPDR = 0XFF;
-  while (!(SPSR & (1 << SPIF))) {}
-  return SPDR;
-}
-//------------------------------------------------------------------------------
-uint8_t SdSpi::receive(uint8_t* buf, size_t n) {
-  if (n-- == 0) return 0;
-  SPDR = 0XFF;
-  for (size_t i = 0; i < n; i++) {
-    while (!(SPSR & (1 << SPIF))) {}
-    uint8_t b = SPDR;
-    SPDR = 0XFF;
-    buf[i] = b;
-  }
-  while (!(SPSR & (1 << SPIF))) {}
-  buf[n] = SPDR;
-  return 0;
-}
-//------------------------------------------------------------------------------
-void SdSpi::send(uint8_t data) {
-  SPDR = data;
-  while (!(SPSR & (1 << SPIF))) {}
-}
-//------------------------------------------------------------------------------
-void SdSpi::send(const uint8_t* buf , size_t n) {
-  if (n == 0) return;
-  SPDR = buf[0];
-  if (n > 1) {
-    uint8_t b = buf[1];
-    size_t i = 2;
-    while (1) {
-      while (!(SPSR & (1 << SPIF))) {}
-      SPDR = b;
-      if (i == n) break;
-      b = buf[i++];
-    }
-  }
-  while (!(SPSR & (1 << SPIF))) {}
-}
-#endif  // !USE_AVR_NATIVE_SPI_INLINE
-#endif  // USE_NATIVE_AVR_SPI
-

+ 0 - 70
SdFat/SdSpiArduino.cpp

@@ -1,70 +0,0 @@
-/* Arduino SdSpi Library
- * Copyright (C) 2013 by William Greiman
- *
- * This file is part of the Arduino SdSpi Library
- *
- * This Library is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This Library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with the Arduino SdSpi Library.  If not, see
- * <http://www.gnu.org/licenses/>.
- */
-#include <SdSpi.h>
-#if USE_ARDUINO_SPI_LIBRARY
-#include <SPI.h>
-//------------------------------------------------------------------------------
-void SdSpi::begin() {
-  SPI.begin();
-}
-//------------------------------------------------------------------------------
-void SdSpi::init(uint8_t sckDivisor) {
-  SPI.setBitOrder(MSBFIRST);
-  SPI.setDataMode(SPI_MODE0);
-#ifndef SPI_CLOCK_DIV128
-  SPI.setClockDivider(sckDivisor);
-#else  // SPI_CLOCK_DIV128
-  int v;
-  if (sckDivisor <= 2) v = SPI_CLOCK_DIV2;
-  else  if (sckDivisor <= 4) v = SPI_CLOCK_DIV4;
-  else  if (sckDivisor <= 8) v = SPI_CLOCK_DIV8;
-  else  if (sckDivisor <= 16) v = SPI_CLOCK_DIV16;
-  else  if (sckDivisor <= 32) v = SPI_CLOCK_DIV32;
-  else  if (sckDivisor <= 64) v = SPI_CLOCK_DIV64;
-  else  v = SPI_CLOCK_DIV128;
-  SPI.setClockDivider(v);
-#endif  // SPI_CLOCK_DIV128
-}
-//------------------------------------------------------------------------------
-/** SPI receive a byte */
-uint8_t SdSpi::receive() {
-  return SPI.transfer(0XFF);
-}
-//------------------------------------------------------------------------------
-/** SPI receive multiple bytes */
-uint8_t SdSpi::receive(uint8_t* buf, size_t n) {
-  for (size_t i = 0; i < n; i++) {
-    buf[i] = SPI.transfer(0XFF);
-  }
-  return 0;
-}
-//------------------------------------------------------------------------------
-/** SPI send a byte */
-void SdSpi::send(uint8_t b) {
-  SPI.transfer(b);
-}
-//------------------------------------------------------------------------------
-/** SPI send multiple bytes */
-void SdSpi::send(const uint8_t* buf , size_t n) {
-  for (size_t i = 0; i < n; i++) {
-    SPI.transfer(buf[i]);
-  }
-}
-#endif  // USE_ARDUINO_SPI_LIBRARY

+ 172 - 206
SdFat/Sd2Card.cpp → SdFat/SdSpiCard.cpp

@@ -1,7 +1,7 @@
-/* Arduino Sd2Card Library
+/* Arduino SdSpiCard Library
  * Copyright (C) 2012 by William Greiman
  *
- * This file is part of the Arduino Sd2Card Library
+ * This file is part of the Arduino SdSpiCard Library
  *
  * This Library is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -14,19 +14,17 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with the Arduino Sd2Card Library.  If not, see
+ * along with the Arduino SdSpiCard Library.  If not, see
  * <http://www.gnu.org/licenses/>.
  */
-#include <Sd2Card.h>
-#include <SdSpi.h>
-#if !USE_SOFTWARE_SPI && ENABLE_SPI_TRANSACTION
+#include "SdSpiCard.h"
+#include "SdSpi.h"
+#if ENABLE_SPI_TRANSACTION
 #include <SPI.h>
-#endif  // !USE_SOFTWARE_SPI && defined(SPI_HAS_TRANSACTION)
+#endif  // ENABLE_SPI_TRANSACTION
 // debug trace macro
 #define SD_TRACE(m, b)
 // #define SD_TRACE(m, b) Serial.print(m);Serial.println(b);
-//------------------------------------------------------------------------------
-SdSpi Sd2Card::m_spi;
 //==============================================================================
 #if USE_SD_CRC
 // CRC functions
@@ -37,7 +35,9 @@ static uint8_t CRC7(const uint8_t* data, uint8_t n) {
     uint8_t d = data[i];
     for (uint8_t j = 0; j < 8; j++) {
       crc <<= 1;
-      if ((d & 0x80) ^ (crc & 0x80)) crc ^= 0x09;
+      if ((d & 0x80) ^ (crc & 0x80)) {
+        crc ^= 0x09;
+      }
       d <<= 1;
     }
   }
@@ -114,20 +114,11 @@ static uint16_t CRC_CCITT(const uint8_t* data, size_t n) {
 #endif  // CRC_CCITT
 #endif  // USE_SD_CRC
 //==============================================================================
-// Sd2Card member functions
+// SdSpiCard member functions
 //------------------------------------------------------------------------------
-/**
- * Initialize an SD flash memory card.
- *
- * \param[in] chipSelectPin SD chip select pin number.
- * \param[in] sckDivisor SPI SCK clock rate divisor.
- *
- * \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.  The reason for failure
- * can be determined by calling errorCode() and errorData().
- */
-bool Sd2Card::begin(uint8_t chipSelectPin, uint8_t sckDivisor) {
+bool SdSpiCard::begin(m_spi_t* spi, uint8_t chipSelectPin, uint8_t sckDivisor) {
   m_errorCode = m_type = 0;
+  m_spi = spi;
   m_chipSelectPin = chipSelectPin;
   // 16-bit init start time allows over a minute
   uint16_t t0 = (uint16_t)millis();
@@ -135,14 +126,16 @@ bool Sd2Card::begin(uint8_t chipSelectPin, uint8_t sckDivisor) {
 
   pinMode(m_chipSelectPin, OUTPUT);
   digitalWrite(m_chipSelectPin, HIGH);
-  m_spi.begin();
+  spiBegin();
 
   // set SCK rate for initialization commands
   m_sckDivisor = SPI_SCK_INIT_DIVISOR;
-  m_spi.init(m_sckDivisor);
+  spiInit(m_sckDivisor);
 
   // must supply min of 74 clock cycles with CS high.
-  for (uint8_t i = 0; i < 10; i++) m_spi.send(0XFF);
+  for (uint8_t i = 0; i < 10; i++) {
+    spiSend(0XFF);
+  }
 
   // command to go idle in SPI mode
   while (cardCommand(CMD0, 0) != R1_IDLE_STATE) {
@@ -163,7 +156,9 @@ bool Sd2Card::begin(uint8_t chipSelectPin, uint8_t sckDivisor) {
       type(SD_CARD_TYPE_SD1);
       break;
     }
-    for (uint8_t i = 0; i < 4; i++) m_status = m_spi.receive();
+    for (uint8_t i = 0; i < 4; i++) {
+      m_status = spiReceive();
+    }
     if (m_status == 0XAA) {
       type(SD_CARD_TYPE_SD2);
       break;
@@ -189,21 +184,25 @@ bool Sd2Card::begin(uint8_t chipSelectPin, uint8_t sckDivisor) {
       error(SD_CARD_ERROR_CMD58);
       goto fail;
     }
-    if ((m_spi.receive() & 0XC0) == 0XC0) type(SD_CARD_TYPE_SDHC);
+    if ((spiReceive() & 0XC0) == 0XC0) {
+      type(SD_CARD_TYPE_SDHC);
+    }
     // Discard rest of ocr - contains allowed voltage range.
-    for (uint8_t i = 0; i < 3; i++) m_spi.receive();
+    for (uint8_t i = 0; i < 3; i++) {
+      spiReceive();
+    }
   }
   chipSelectHigh();
   m_sckDivisor = sckDivisor;
   return true;
 
- fail:
+fail:
   chipSelectHigh();
   return false;
 }
 //------------------------------------------------------------------------------
 // send command and return error code.  Return zero for OK
-uint8_t Sd2Card::cardCommand(uint8_t cmd, uint32_t arg) {
+uint8_t SdSpiCard::cardCommand(uint8_t cmd, uint32_t arg) {
   // select card
   chipSelectLow();
 
@@ -220,36 +219,38 @@ uint8_t Sd2Card::cardCommand(uint8_t cmd, uint32_t arg) {
   d[5] = CRC7(d, 5);
 
   // send message
-  for (uint8_t k = 0; k < 6; k++) m_spi.send(d[k]);
+  for (uint8_t k = 0; k < 6; k++) {
+    spiSend(d[k]);
+  }
 #else  // USE_SD_CRC
   // send command
-  m_spi.send(cmd | 0x40);
+  spiSend(cmd | 0x40);
 
   // send argument
-  for (int8_t i = 3; i >= 0; i--) m_spi.send(pa[i]);
+  for (int8_t i = 3; i >= 0; i--) {
+    spiSend(pa[i]);
+  }
 
   // send CRC - correct for CMD0 with arg zero or CMD8 with arg 0X1AA
-  m_spi.send(cmd == CMD0 ? 0X95 : 0X87);
+  spiSend(cmd == CMD0 ? 0X95 : 0X87);
 #endif  // USE_SD_CRC
 
   // skip stuff byte for stop read
-  if (cmd == CMD12) m_spi.receive();
+  if (cmd == CMD12) {
+    spiReceive();
+  }
 
   // wait for response
-  for (uint8_t i = 0; ((m_status = m_spi.receive()) & 0X80) && i != 0XFF; i++) {
+  for (uint8_t i = 0; ((m_status = spiReceive()) & 0X80) && i != 0XFF; i++) {
   }
   return m_status;
 }
 //------------------------------------------------------------------------------
-/**
- * Determine the size of an SD flash memory card.
- *
- * \return The number of 512 byte data blocks in the card
- *         or zero if an error occurs.
- */
-uint32_t Sd2Card::cardSize() {
+uint32_t SdSpiCard::cardSize() {
   csd_t csd;
-  if (!readCSD(&csd)) return 0;
+  if (!readCSD(&csd)) {
+    return 0;
+  }
   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)
@@ -267,46 +268,39 @@ uint32_t Sd2Card::cardSize() {
   }
 }
 //------------------------------------------------------------------------------
-void Sd2Card::spiYield() {
-#if ENABLE_SPI_YIELD && !USE_SOFTWARE_SPI && defined(SPI_HAS_TRANSACTION)
+void SdSpiCard::spiYield() {
+#if ENABLE_SPI_TRANSACTION && ENABLE_SPI_YIELD && defined(SPI_HAS_TRANSACTION)
   chipSelectHigh();
   chipSelectLow();
-#endif  // ENABLE_SPI_YIELD && !USE_SOFTWARE_SPI && defined(SPI_HAS_TRANSACTION)
+#endif  // ENABLE_SPI_TRANSACTION && ENABLE_SPI_YIELD && SPI_HAS_TRANSACTION
 }
 //------------------------------------------------------------------------------
-void Sd2Card::chipSelectHigh() {
+void SdSpiCard::chipSelectHigh() {
   digitalWrite(m_chipSelectPin, HIGH);
   // insure MISO goes high impedance
-  m_spi.send(0XFF);
-#if !USE_SOFTWARE_SPI && defined(SPI_HAS_TRANSACTION)
-  SPI.endTransaction();
-#endif  // !USE_SOFTWARE_SPI && defined(SPI_HAS_TRANSACTION)
+  spiSend(0XFF);
+#if ENABLE_SPI_TRANSACTION && defined(SPI_HAS_TRANSACTION)
+  if (useSpiTransactions()) {
+    SPI.endTransaction();
+  }
+#endif  // ENABLE_SPI_TRANSACTION && defined(SPI_HAS_TRANSACTION)
 }
 //------------------------------------------------------------------------------
-void Sd2Card::chipSelectLow() {
-#if !USE_SOFTWARE_SPI && defined(SPI_HAS_TRANSACTION)
-  SPI.beginTransaction(SPISettings());
-#endif  // !USE_SOFTWARE_SPI && defined(SPI_HAS_TRANSACTION)
-  m_spi.init(m_sckDivisor);
+void SdSpiCard::chipSelectLow() {
+#if ENABLE_SPI_TRANSACTION && defined(SPI_HAS_TRANSACTION)
+  if (useSpiTransactions()) {
+    SPI.beginTransaction(SPISettings());
+  }
+#endif  // ENABLE_SPI_TRANSACTION && defined(SPI_HAS_TRANSACTION)
+  spiInit(m_sckDivisor);
   digitalWrite(m_chipSelectPin, LOW);
 }
 //------------------------------------------------------------------------------
-/** Erase a range of blocks.
- *
- * \param[in] firstBlock The address of the first block in the range.
- * \param[in] lastBlock The address of the last block in the range.
- *
- * \note This function requests the SD card to do a flash erase for a
- * range of blocks.  The data on the card after an erase operation is
- * either 0 or 1, depends on the card vendor.  The card must support
- * single block erase.
- *
- * \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.
- */
-bool Sd2Card::erase(uint32_t firstBlock, uint32_t lastBlock) {
+bool SdSpiCard::erase(uint32_t firstBlock, uint32_t lastBlock) {
   csd_t csd;
-  if (!readCSD(&csd)) goto fail;
+  if (!readCSD(&csd)) {
+    goto fail;
+  }
   // check for single block erase
   if (!csd.v1.erase_blk_en) {
     // erase size mask
@@ -322,10 +316,10 @@ bool Sd2Card::erase(uint32_t firstBlock, uint32_t lastBlock) {
     lastBlock <<= 9;
   }
   if (cardCommand(CMD32, firstBlock)
-    || cardCommand(CMD33, lastBlock)
-    || cardCommand(CMD38, 0)) {
-      error(SD_CARD_ERROR_ERASE);
-      goto fail;
+      || cardCommand(CMD33, lastBlock)
+      || cardCommand(CMD38, 0)) {
+    error(SD_CARD_ERROR_ERASE);
+    goto fail;
   }
   if (!waitNotBusy(SD_ERASE_TIMEOUT)) {
     error(SD_CARD_ERROR_ERASE_TIMEOUT);
@@ -334,139 +328,125 @@ bool Sd2Card::erase(uint32_t firstBlock, uint32_t lastBlock) {
   chipSelectHigh();
   return true;
 
- fail:
+fail:
   chipSelectHigh();
   return false;
 }
 //------------------------------------------------------------------------------
-/** Determine if card supports single block erase.
- *
- * \return The value one, true, is returned if single block erase is supported.
- * The value zero, false, is returned if single block erase is not supported.
- */
-bool Sd2Card::eraseSingleBlockEnable() {
+bool SdSpiCard::eraseSingleBlockEnable() {
   csd_t csd;
   return readCSD(&csd) ? csd.v1.erase_blk_en : false;
 }
 //------------------------------------------------------------------------------
-/**
- * Check for busy.  MISO low indicates the card is busy.
- * 
- * \return true if busy else false.
- */
-bool Sd2Card::isBusy() {
+bool SdSpiCard::isBusy() {
   bool rtn;
   chipSelectLow();
   for (uint8_t i = 0; i < 8; i++) {
-    rtn = m_spi.receive() != 0XFF;
-    if (!rtn) break;
+    rtn = spiReceive() != 0XFF;
+    if (!rtn) {
+      break;
+    }
   }
   chipSelectHigh();
   return rtn;
 }
 //------------------------------------------------------------------------------
-/**
- * Read a 512 byte block from an SD card.
- *
- * \param[in] blockNumber Logical block to be read.
- * \param[out] dst Pointer to the location that will receive the data.
-
- * \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.
- */
-bool Sd2Card::readBlock(uint32_t blockNumber, uint8_t* dst) {
+bool SdSpiCard::readBlock(uint32_t blockNumber, uint8_t* dst) {
   SD_TRACE("RB", blockNumber);
   // use address if not SDHC card
-  if (type()!= SD_CARD_TYPE_SDHC) blockNumber <<= 9;
+  if (type() != SD_CARD_TYPE_SDHC) {
+    blockNumber <<= 9;
+  }
   if (cardCommand(CMD17, blockNumber)) {
     error(SD_CARD_ERROR_CMD17);
     goto fail;
   }
   return readData(dst, 512);
 
- fail:
+fail:
   chipSelectHigh();
   return false;
 }
 //------------------------------------------------------------------------------
-/** Read one data block in a multiple block read sequence
- *
- * \param[in] dst Pointer to the location for the data to be read.
- *
- * \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.
- */
-bool Sd2Card::readData(uint8_t *dst) {
+bool SdSpiCard::readBlocks(uint32_t block, uint8_t* dst, size_t count) {
+  if (!readStart(block)) {
+    return false;
+  }
+  for (uint16_t b = 0; b < count; b++, dst += 512) {
+    if (!readData(dst)) {
+      return false;
+    }
+  }
+  return readStop();
+}
+//------------------------------------------------------------------------------
+bool SdSpiCard::readData(uint8_t *dst) {
   chipSelectLow();
   return readData(dst, 512);
 }
 //------------------------------------------------------------------------------
-bool Sd2Card::readData(uint8_t* dst, size_t count) {
+bool SdSpiCard::readData(uint8_t* dst, size_t count) {
 #if USE_SD_CRC
   uint16_t crc;
 #endif  // USE_SD_CRC
   // wait for start block token
   uint16_t t0 = millis();
-  while ((m_status = m_spi.receive()) == 0XFF) {
+  while ((m_status = spiReceive()) == 0XFF) {
     if (((uint16_t)millis() - t0) > SD_READ_TIMEOUT) {
       error(SD_CARD_ERROR_READ_TIMEOUT);
       goto fail;
     }
-    spiYield();
   }
   if (m_status != DATA_START_BLOCK) {
     error(SD_CARD_ERROR_READ);
     goto fail;
   }
   // transfer data
-  if ((m_status = m_spi.receive(dst, count))) {
+  if ((m_status = spiReceive(dst, count))) {
     error(SD_CARD_ERROR_SPI_DMA);
     goto fail;
   }
 
 #if USE_SD_CRC
   // get crc
-  crc = (m_spi.receive() << 8) | m_spi.receive();
+  crc = (spiReceive() << 8) | spiReceive();
   if (crc != CRC_CCITT(dst, count)) {
     error(SD_CARD_ERROR_READ_CRC);
     goto fail;
   }
 #else
   // discard crc
-  m_spi.receive();
-  m_spi.receive();
+  spiReceive();
+  spiReceive();
 #endif  // USE_SD_CRC
   chipSelectHigh();
   return true;
 
- fail:
+fail:
   chipSelectHigh();
   return false;
 }
 //------------------------------------------------------------------------------
-/** Read OCR register.
- *
- * \param[out] ocr Value of OCR register.
- * \return true for success else false.
- */
-bool Sd2Card::readOCR(uint32_t* ocr) {
+bool SdSpiCard::readOCR(uint32_t* ocr) {
   uint8_t *p = reinterpret_cast<uint8_t*>(ocr);
   if (cardCommand(CMD58, 0)) {
     error(SD_CARD_ERROR_CMD58);
     goto fail;
   }
-  for (uint8_t i = 0; i < 4; i++) p[3-i] = m_spi.receive();
+  for (uint8_t i = 0; i < 4; i++) {
+    p[3 - i] = spiReceive();
+  }
 
   chipSelectHigh();
   return true;
 
- fail:
+fail:
   chipSelectHigh();
   return false;
 }
 //------------------------------------------------------------------------------
 /** read CID or CSR register */
-bool Sd2Card::readRegister(uint8_t cmd, void* buf) {
+bool SdSpiCard::readRegister(uint8_t cmd, void* buf) {
   uint8_t* dst = reinterpret_cast<uint8_t*>(buf);
   if (cardCommand(cmd, 0)) {
     error(SD_CARD_ERROR_READ_REG);
@@ -474,24 +454,16 @@ bool Sd2Card::readRegister(uint8_t cmd, void* buf) {
   }
   return readData(dst, 16);
 
- fail:
+fail:
   chipSelectHigh();
   return false;
 }
 //------------------------------------------------------------------------------
-/** Start a read multiple blocks sequence.
- *
- * \param[in] blockNumber Address of first block in sequence.
- *
- * \note This function is used with readData() and readStop() for optimized
- * multiple block reads.  SPI chipSelect must be low for the entire sequence.
- *
- * \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.
- */
-bool Sd2Card::readStart(uint32_t blockNumber) {
+bool SdSpiCard::readStart(uint32_t blockNumber) {
   SD_TRACE("RS", blockNumber);
-  if (type()!= SD_CARD_TYPE_SDHC) blockNumber <<= 9;
+  if (type() != SD_CARD_TYPE_SDHC) {
+    blockNumber <<= 9;
+  }
   if (cardCommand(CMD18, blockNumber)) {
     error(SD_CARD_ERROR_CMD18);
     goto fail;
@@ -499,17 +471,12 @@ bool Sd2Card::readStart(uint32_t blockNumber) {
   chipSelectHigh();
   return true;
 
- fail:
+fail:
   chipSelectHigh();
   return false;
 }
 //------------------------------------------------------------------------------
-/** End a read multiple blocks sequence.
- *
-* \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.
- */
-bool Sd2Card::readStop() {
+bool SdSpiCard::readStop() {
   if (cardCommand(CMD12, 0)) {
     error(SD_CARD_ERROR_CMD12);
     goto fail;
@@ -517,41 +484,39 @@ bool Sd2Card::readStop() {
   chipSelectHigh();
   return true;
 
- fail:
+fail:
   chipSelectHigh();
   return false;
 }
 //------------------------------------------------------------------------------
 // wait for card to go not busy
-bool Sd2Card::waitNotBusy(uint16_t timeoutMillis) {
+bool SdSpiCard::waitNotBusy(uint16_t timeoutMillis) {
   uint16_t t0 = millis();
-  while (m_spi.receive() != 0XFF) {
-    if (((uint16_t)millis() - t0) >= timeoutMillis) goto fail;
+  while (spiReceive() != 0XFF) {
+    if (((uint16_t)millis() - t0) >= timeoutMillis) {
+      goto fail;
+    }
     spiYield();
   }
   return true;
 
- fail:
+fail:
   return false;
 }
 //------------------------------------------------------------------------------
-/**
- * Writes a 512 byte block to an SD card.
- *
- * \param[in] blockNumber Logical block to be written.
- * \param[in] src Pointer to the location of the data to be written.
- * \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.
- */
-bool Sd2Card::writeBlock(uint32_t blockNumber, const uint8_t* src) {
+bool SdSpiCard::writeBlock(uint32_t blockNumber, const uint8_t* src) {
   SD_TRACE("WB", blockNumber);
   // use address if not SDHC card
-  if (type() != SD_CARD_TYPE_SDHC) blockNumber <<= 9;
+  if (type() != SD_CARD_TYPE_SDHC) {
+    blockNumber <<= 9;
+  }
   if (cardCommand(CMD24, blockNumber)) {
     error(SD_CARD_ERROR_CMD24);
     goto fail;
   }
-  if (!writeData(DATA_START_BLOCK, src)) goto fail;
+  if (!writeData(DATA_START_BLOCK, src)) {
+    goto fail;
+  }
 
 #define CHECK_PROGRAMMING 0
 #if CHECK_PROGRAMMING
@@ -561,7 +526,7 @@ bool Sd2Card::writeBlock(uint32_t blockNumber, const uint8_t* src) {
     goto fail;
   }
   // response is r2 so get and check two bytes for nonzero
-  if (cardCommand(CMD13, 0) || m_spi.receive()) {
+  if (cardCommand(CMD13, 0) || spiReceive()) {
     error(SD_CARD_ERROR_WRITE_PROGRAMMING);
     goto fail;
   }
@@ -570,66 +535,66 @@ bool Sd2Card::writeBlock(uint32_t blockNumber, const uint8_t* src) {
   chipSelectHigh();
   return true;
 
- fail:
+fail:
   chipSelectHigh();
   return false;
 }
 //------------------------------------------------------------------------------
-/** Write one data block in a multiple block write sequence
- * \param[in] src Pointer to the location of the data to be written.
- * \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.
- */
-bool Sd2Card::writeData(const uint8_t* src) {
+bool SdSpiCard::writeBlocks(uint32_t block, const uint8_t* src, size_t count) {
+  if (!writeStart(block, count)) {
+    return false;
+  }
+  for (size_t b = 0; b < count; b++, src += 512) {
+    if (!writeData(src)) {
+      return false;
+    }
+  }
+  return writeStop();
+}
+//------------------------------------------------------------------------------
+bool SdSpiCard::writeData(const uint8_t* src) {
   chipSelectLow();
   // wait for previous write to finish
-  if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto fail;
-  if (!writeData(WRITE_MULTIPLE_TOKEN, src)) goto fail;
+  if (!waitNotBusy(SD_WRITE_TIMEOUT)) {
+    goto fail;
+  }
+  if (!writeData(WRITE_MULTIPLE_TOKEN, src)) {
+    goto fail;
+  }
   chipSelectHigh();
   return true;
 
- fail:
+fail:
   error(SD_CARD_ERROR_WRITE_MULTIPLE);
   chipSelectHigh();
   return false;
 }
 //------------------------------------------------------------------------------
 // send one block of data for write block or write multiple blocks
-bool Sd2Card::writeData(uint8_t token, const uint8_t* src) {
+bool SdSpiCard::writeData(uint8_t token, const uint8_t* src) {
 #if USE_SD_CRC
   uint16_t crc = CRC_CCITT(src, 512);
 #else  // USE_SD_CRC
   uint16_t crc = 0XFFFF;
 #endif  // USE_SD_CRC
-  m_spi.send(token);
-  m_spi.send(src, 512);
-  m_spi.send(crc >> 8);
-  m_spi.send(crc & 0XFF);
+  spiSend(token);
+  spiSend(src, 512);
+  spiSend(crc >> 8);
+  spiSend(crc & 0XFF);
 
-  m_status = m_spi.receive();
+  m_status = spiReceive();
   if ((m_status & DATA_RES_MASK) != DATA_RES_ACCEPTED) {
     error(SD_CARD_ERROR_WRITE);
     goto fail;
   }
   return true;
 
- fail:
+fail:
   chipSelectHigh();
   return false;
 }
 //------------------------------------------------------------------------------
-/** Start a write multiple blocks sequence.
- *
- * \param[in] blockNumber Address of first block in sequence.
- * \param[in] eraseCount The number of blocks to be pre-erased.
- *
- * \note This function is used with writeData() and writeStop()
- * for optimized multiple block writes.
- *
- * \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.
- */
-bool Sd2Card::writeStart(uint32_t blockNumber, uint32_t eraseCount) {
+bool SdSpiCard::writeStart(uint32_t blockNumber, uint32_t eraseCount) {
   SD_TRACE("WS", blockNumber);
   // send pre-erase count
   if (cardAcmd(ACMD23, eraseCount)) {
@@ -637,7 +602,9 @@ bool Sd2Card::writeStart(uint32_t blockNumber, uint32_t eraseCount) {
     goto fail;
   }
   // use address if not SDHC card
-  if (type() != SD_CARD_TYPE_SDHC) blockNumber <<= 9;
+  if (type() != SD_CARD_TYPE_SDHC) {
+    blockNumber <<= 9;
+  }
   if (cardCommand(CMD25, blockNumber)) {
     error(SD_CARD_ERROR_CMD25);
     goto fail;
@@ -645,25 +612,24 @@ bool Sd2Card::writeStart(uint32_t blockNumber, uint32_t eraseCount) {
   chipSelectHigh();
   return true;
 
- fail:
+fail:
   chipSelectHigh();
   return false;
 }
 //------------------------------------------------------------------------------
-/** End a write multiple blocks sequence.
- *
-* \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.
- */
-bool Sd2Card::writeStop() {
+bool SdSpiCard::writeStop() {
   chipSelectLow();
-  if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto fail;
-  m_spi.send(STOP_TRAN_TOKEN);
-  if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto fail;
+  if (!waitNotBusy(SD_WRITE_TIMEOUT)) {
+    goto fail;
+  }
+  spiSend(STOP_TRAN_TOKEN);
+  if (!waitNotBusy(SD_WRITE_TIMEOUT)) {
+    goto fail;
+  }
   chipSelectHigh();
   return true;
 
- fail:
+fail:
   error(SD_CARD_ERROR_STOP_TRAN);
   chipSelectHigh();
   return false;

+ 308 - 0
SdFat/SdSpiCard.h

@@ -0,0 +1,308 @@
+/* Arduino SdSpiCard Library
+ * Copyright (C) 2012 by William Greiman
+ *
+ * This file is part of the Arduino SdSpiCard Library
+ *
+ * This Library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the Arduino SdSpiCard Library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+#ifndef SpiCard_h
+#define SpiCard_h
+/**
+ * \file
+ * \brief SdSpiCard class for V2 SD/SDHC cards
+ */
+#include <Arduino.h>
+#include <SdFatConfig.h>
+#include <SdInfo.h>
+#include <SdSpi.h>
+//==============================================================================
+/**
+ * \class SdSpiCard
+ * \brief Raw access to SD and SDHC flash memory cards via SPI protocol.
+ */
+class SdSpiCard {
+ public:
+  /** typedef for SPI class.  */
+#if SD_SPI_CONFIGURATION < 3
+  typedef SpiDefault_t m_spi_t;
+#else  // SD_SPI_CONFIGURATION < 3
+  typedef SdSpiBase m_spi_t;
+#endif  // SD_SPI_CONFIGURATION < 3
+  /** Construct an instance of SdSpiCard. */
+  SdSpiCard() : m_errorCode(SD_CARD_ERROR_INIT_NOT_CALLED), m_type(0) {}
+  /** Initialize the SD card.
+   * \param[in] spi SPI object.
+   * \param[in] chipSelectPin SD chip select pin.
+   * \param[in] sckDivisor SPI clock divisor.
+   * \return true for success else false.
+   */
+  bool begin(m_spi_t* spi, uint8_t chipSelectPin = SS,
+             uint8_t sckDivisor = SPI_FULL_SPEED);
+  /**
+   * Determine the size of an SD flash memory card.
+   *
+   * \return The number of 512 byte data blocks in the card
+   *         or zero if an error occurs.
+   */
+  uint32_t cardSize();
+  /** Erase a range of blocks.
+   *
+   * \param[in] firstBlock The address of the first block in the range.
+   * \param[in] lastBlock The address of the last block in the range.
+   *
+   * \note This function requests the SD card to do a flash erase for a
+   * range of blocks.  The data on the card after an erase operation is
+   * either 0 or 1, depends on the card vendor.  The card must support
+   * single block erase.
+   *
+   * \return The value true is returned for success and
+   * the value false is returned for failure.
+   */
+  bool erase(uint32_t firstBlock, uint32_t lastBlock);
+  /** Determine if card supports single block erase.
+   *
+   * \return true is returned if single block erase is supported.
+   * false is returned if single block erase is not supported.
+   */
+  bool eraseSingleBlockEnable();
+  /**
+   *  Set SD error code.
+   *  \param[in] code value for error code.
+   */
+  void error(uint8_t code) {
+    m_errorCode = code;
+  }
+  /**
+   * \return code for the last error. See SdSpiCard.h for a list of error codes.
+   */
+  int errorCode() const {
+    return m_errorCode;
+  }
+  /** \return error data for last error. */
+  int errorData() const {
+    return m_status;
+  }
+  /**
+   * Check for busy.  MISO low indicates the card is busy.
+   *
+   * \return true if busy else false.
+   */
+  bool isBusy();
+  /**
+   * Read a 512 byte block from an SD card.
+   *
+   * \param[in] block Logical block to be read.
+   * \param[out] dst Pointer to the location that will receive the data.
+   * \return The value true is returned for success and
+   * the value false is returned for failure.
+   */
+  bool readBlock(uint32_t block, uint8_t* dst);
+  /**
+   * Read multiple 512 byte blocks from an SD card.
+   *
+   * \param[in] block Logical block to be read.
+   * \param[in] count Number of blocks to be read.
+   * \param[out] dst Pointer to the location that will receive the data.
+   * \return The value true is returned for success and
+   * the value false is returned for failure.
+   */
+  bool readBlocks(uint32_t block, uint8_t* dst, size_t count);
+  /**
+   * Read a card's CID register. The CID contains card identification
+   * information such as Manufacturer ID, Product name, Product serial
+   * number and Manufacturing date.
+   *
+   * \param[out] cid pointer to area for returned data.
+   *
+   * \return true for success or false for failure.
+   */
+  bool readCID(cid_t* cid) {
+    return readRegister(CMD10, cid);
+  }
+  /**
+   * Read a card's CSD register. The CSD contains Card-Specific Data that
+   * provides information regarding access to the card's contents.
+   *
+   * \param[out] csd pointer to area for returned data.
+   *
+   * \return true for success or false for failure.
+   */
+  bool readCSD(csd_t* csd) {
+    return readRegister(CMD9, csd);
+  }
+  /** Read one data block in a multiple block read sequence
+   *
+   * \param[out] dst Pointer to the location for the data to be read.
+   *
+   * \return The value true is returned for success and
+   * the value false is returned for failure.
+   */
+  bool readData(uint8_t *dst);
+  /** Read OCR register.
+   *
+   * \param[out] ocr Value of OCR register.
+   * \return true for success else false.
+   */
+  bool readOCR(uint32_t* ocr);
+  /** Start a read multiple blocks sequence.
+   *
+   * \param[in] blockNumber Address of first block in sequence.
+   *
+   * \note This function is used with readData() and readStop() for optimized
+   * multiple block reads.  SPI chipSelect must be low for the entire sequence.
+   *
+   * \return The value true is returned for success and
+   * the value false is returned for failure.
+   */
+  bool readStart(uint32_t blockNumber);
+  /** End a read multiple blocks sequence.
+   *
+   * \return The value true is returned for success and
+   * the value false is returned for failure.
+   */
+  bool readStop();
+  /** Return SCK divisor.
+   *
+   * \return Requested SCK divisor.
+   */
+  uint8_t sckDivisor() {
+    return m_sckDivisor;
+  }
+  /** Return the card type: SD V1, SD V2 or SDHC
+   * \return 0 - SD V1, 1 - SD V2, or 3 - SDHC.
+   */
+  int type() const {
+    return m_type;
+  }
+  /**
+   * Writes a 512 byte block to an SD card.
+   *
+   * \param[in] blockNumber Logical block to be written.
+   * \param[in] src Pointer to the location of the data to be written.
+   * \return The value true is returned for success and
+   * the value false is returned for failure.
+   */
+  bool writeBlock(uint32_t blockNumber, const uint8_t* src);
+  /**
+   * Write multiple 512 byte blocks to an SD card.
+   *
+   * \param[in] block Logical block to be written.
+   * \param[in] count Number of blocks to be written.
+   * \param[in] src Pointer to the location of the data to be written.
+   * \return The value true is returned for success and
+   * the value false is returned for failure.
+   */
+  bool writeBlocks(uint32_t block, const uint8_t* src, size_t count);
+  /** Write one data block in a multiple block write sequence
+   * \param[in] src Pointer to the location of the data to be written.
+   * \return The value true is returned for success and
+   * the value false is returned for failure.
+   */
+  bool writeData(const uint8_t* src);
+  /** Start a write multiple blocks sequence.
+   *
+   * \param[in] blockNumber Address of first block in sequence.
+   * \param[in] eraseCount The number of blocks to be pre-erased.
+   *
+   * \note This function is used with writeData() and writeStop()
+   * for optimized multiple block writes.
+   *
+   * \return The value true is returned for success and
+   * the value false is returned for failure.
+   */
+  bool writeStart(uint32_t blockNumber, uint32_t eraseCount);
+  /** End a write multiple blocks sequence.
+   *
+   * \return The value true is returned for success and
+   * the value false is returned for failure.
+   */
+  bool writeStop();
+
+ private:
+  // private functions
+  uint8_t cardAcmd(uint8_t cmd, uint32_t arg) {
+    cardCommand(CMD55, 0);
+    return cardCommand(cmd, arg);
+  }
+  uint8_t cardCommand(uint8_t cmd, uint32_t arg);
+  bool readData(uint8_t* dst, size_t count);
+  bool readRegister(uint8_t cmd, void* buf);
+  void chipSelectHigh();
+  void chipSelectLow();
+  void spiYield();
+  void type(uint8_t value) {
+    m_type = value;
+  }
+  bool waitNotBusy(uint16_t timeoutMillis);
+  bool writeData(uint8_t token, const uint8_t* src);
+  void spiBegin() {
+    m_spi->begin();
+  }
+  void spiInit(uint8_t spiDivisor) {
+    m_spi->init(spiDivisor);
+  }
+  uint8_t spiReceive() {
+    return m_spi->receive();
+  }
+  uint8_t spiReceive(uint8_t* buf, size_t n) {
+    return m_spi->receive(buf, n);
+  }
+  void spiSend(uint8_t data) {
+    m_spi->send(data);
+  }
+  void spiSend(const uint8_t* buf, size_t n) {
+    m_spi->send(buf, n);
+  }
+  bool useSpiTransactions() {
+    return m_spi->useSpiTransactions();
+  }
+  m_spi_t* m_spi;
+  uint8_t m_chipSelectPin;
+  uint8_t m_errorCode;
+  uint8_t m_sckDivisor;
+  uint8_t m_status;
+  uint8_t m_type;
+};
+//==============================================================================
+/**
+ * \class Sd2Card
+ * \brief Raw access to SD and SDHC card using default SPI library.
+ */
+class Sd2Card : public SdSpiCard {
+ public:
+  /** Initialize the SD card.
+   * \param[in] chipSelectPin SD chip select pin.
+   * \param[in] sckDivisor SPI clock divisor.
+   * \return true for success else false.
+   */
+  bool begin(uint8_t chipSelectPin = SS, uint8_t sckDivisor = 2) {
+    return SdSpiCard::begin(&m_spi, chipSelectPin, sckDivisor);
+  }
+  /** Initialize the SD card. Obsolete form.
+   * \param[in] chipSelectPin SD chip select pin.
+   * \param[in] sckDivisor SPI clock divisor.
+   * \return true for success else false.
+   */
+  bool init(uint8_t sckDivisor = 2, uint8_t chipSelectPin = SS) {
+    return begin(chipSelectPin, sckDivisor);
+  }
+ private:
+  bool begin(m_spi_t* spi, uint8_t chipSelectPin = SS,
+             uint8_t sckDivisor = SPI_FULL_SPEED) {
+    return false;
+  }
+  SpiDefault_t m_spi;
+};
+#endif  // SpiCard_h

+ 25 - 23
SdFat/SdSpiSAM3X.cpp

@@ -17,8 +17,8 @@
  * along with the Arduino SdSpi Library.  If not, see
  * <http://www.gnu.org/licenses/>.
  */
-#include <SdSpi.h>
-#if USE_NATIVE_SAM3X_SPI
+#include "SdSpi.h"
+#if defined(__SAM3X8E__) || defined(__SAM3X8H__)
 /** Use SAM3X DMAC if nonzero */
 #define USE_SAM3X_DMAC 1
 /** Use extra Bus Matrix arbitration fix if nonzero */
@@ -59,20 +59,20 @@ static bool dmac_channel_transfer_done(uint32_t ul_num) {
 //------------------------------------------------------------------------------
 void SdSpi::begin() {
   PIO_Configure(
-      g_APinDescription[PIN_SPI_MOSI].pPort,
-      g_APinDescription[PIN_SPI_MOSI].ulPinType,
-      g_APinDescription[PIN_SPI_MOSI].ulPin,
-      g_APinDescription[PIN_SPI_MOSI].ulPinConfiguration);
+    g_APinDescription[PIN_SPI_MOSI].pPort,
+    g_APinDescription[PIN_SPI_MOSI].ulPinType,
+    g_APinDescription[PIN_SPI_MOSI].ulPin,
+    g_APinDescription[PIN_SPI_MOSI].ulPinConfiguration);
   PIO_Configure(
-      g_APinDescription[PIN_SPI_MISO].pPort,
-      g_APinDescription[PIN_SPI_MISO].ulPinType,
-      g_APinDescription[PIN_SPI_MISO].ulPin,
-      g_APinDescription[PIN_SPI_MISO].ulPinConfiguration);
+    g_APinDescription[PIN_SPI_MISO].pPort,
+    g_APinDescription[PIN_SPI_MISO].ulPinType,
+    g_APinDescription[PIN_SPI_MISO].ulPin,
+    g_APinDescription[PIN_SPI_MISO].ulPinConfiguration);
   PIO_Configure(
-      g_APinDescription[PIN_SPI_SCK].pPort,
-      g_APinDescription[PIN_SPI_SCK].ulPinType,
-      g_APinDescription[PIN_SPI_SCK].ulPin,
-      g_APinDescription[PIN_SPI_SCK].ulPinConfiguration);
+    g_APinDescription[PIN_SPI_SCK].pPort,
+    g_APinDescription[PIN_SPI_SCK].ulPinType,
+    g_APinDescription[PIN_SPI_SCK].ulPin,
+    g_APinDescription[PIN_SPI_SCK].ulPinConfiguration);
   pmc_enable_periph_clk(ID_SPI0);
 #if USE_SAM3X_DMAC
   pmc_enable_periph_clk(ID_DMAC);
@@ -97,12 +97,12 @@ static void spiDmaRX(uint8_t* dst, uint16_t count) {
   DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_DADDR = (uint32_t)dst;
   DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_DSCR =  0;
   DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_CTRLA = count |
-    DMAC_CTRLA_SRC_WIDTH_BYTE | DMAC_CTRLA_DST_WIDTH_BYTE;
+      DMAC_CTRLA_SRC_WIDTH_BYTE | DMAC_CTRLA_DST_WIDTH_BYTE;
   DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_CTRLB = DMAC_CTRLB_SRC_DSCR |
-    DMAC_CTRLB_DST_DSCR | DMAC_CTRLB_FC_PER2MEM_DMA_FC |
-    DMAC_CTRLB_SRC_INCR_FIXED | DMAC_CTRLB_DST_INCR_INCREMENTING;
+      DMAC_CTRLB_DST_DSCR | DMAC_CTRLB_FC_PER2MEM_DMA_FC |
+      DMAC_CTRLB_SRC_INCR_FIXED | DMAC_CTRLB_DST_INCR_INCREMENTING;
   DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_CFG = DMAC_CFG_SRC_PER(SPI_RX_IDX) |
-    DMAC_CFG_SRC_H2SEL | DMAC_CFG_SOD | DMAC_CFG_FIFOCFG_ASAP_CFG;
+      DMAC_CFG_SRC_H2SEL | DMAC_CFG_SOD | DMAC_CFG_FIFOCFG_ASAP_CFG;
   dmac_channel_enable(SPI_DMAC_RX_CH);
 }
 //------------------------------------------------------------------------------
@@ -119,11 +119,11 @@ static void spiDmaTX(const uint8_t* src, uint16_t count) {
   DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_DADDR = (uint32_t)&SPI0->SPI_TDR;
   DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_DSCR =  0;
   DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_CTRLA = count |
-    DMAC_CTRLA_SRC_WIDTH_BYTE | DMAC_CTRLA_DST_WIDTH_BYTE;
+      DMAC_CTRLA_SRC_WIDTH_BYTE | DMAC_CTRLA_DST_WIDTH_BYTE;
 
   DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_CTRLB =  DMAC_CTRLB_SRC_DSCR |
-    DMAC_CTRLB_DST_DSCR | DMAC_CTRLB_FC_MEM2PER_DMA_FC |
-    src_incr | DMAC_CTRLB_DST_INCR_FIXED;
+      DMAC_CTRLB_DST_DSCR | DMAC_CTRLB_FC_MEM2PER_DMA_FC |
+      src_incr | DMAC_CTRLB_DST_INCR_FIXED;
 
   DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_CFG = DMAC_CFG_DST_PER(SPI_TX_IDX) |
       DMAC_CFG_DST_H2SEL | DMAC_CFG_SOD | DMAC_CFG_FIFOCFG_ALAP_CFG;
@@ -181,7 +181,9 @@ uint8_t SdSpi::receive(uint8_t* buf, size_t n) {
       break;
     }
   }
-  if (pSpi->SPI_SR & SPI_SR_OVRES) rtn |= 1;
+  if (pSpi->SPI_SR & SPI_SR_OVRES) {
+    rtn |= 1;
+  }
 #else  // USE_SAM3X_DMAC
   for (size_t i = 0; i < n; i++) {
     pSpi->SPI_TDR = 0XFF;
@@ -213,4 +215,4 @@ void SdSpi::send(const uint8_t* buf , size_t n) {
   // leave RDR empty
   uint8_t b = pSpi->SPI_RDR;
 }
-#endif  // USE_NATIVE_SAM3X_SPI
+#endif  // defined(__SAM3X8E__) || defined(__SAM3X8H__)

+ 0 - 61
SdFat/SdSpiSoft.cpp

@@ -1,61 +0,0 @@
-/* Arduino SdSpi Library
- * Copyright (C) 2013 by William Greiman
- *
- * This file is part of the Arduino SdSpi Library
- *
- * This Library is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This Library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with the Arduino SdSpi Library.  If not, see
- * <http://www.gnu.org/licenses/>.
- */
-#include <SdSpi.h>
-#if USE_SOFTWARE_SPI
-#include <SoftSPI.h>
-static
-SoftSPI<SOFT_SPI_MISO_PIN, SOFT_SPI_MOSI_PIN, SOFT_SPI_SCK_PIN, 0> softSpiBus;
-//------------------------------------------------------------------------------
-/**
- * initialize SPI pins
- */
-void SdSpi::begin() {
-  softSpiBus.begin();
-}
-//------------------------------------------------------------------------------
-/**
- * Initialize hardware SPI - dummy for soft SPI
- */
-void SdSpi::init(uint8_t sckDivisor) {}
-//------------------------------------------------------------------------------
-/** Soft SPI receive byte */
-uint8_t SdSpi::receive() {
-  return softSpiBus.receive();
-}
-//------------------------------------------------------------------------------
-/** Soft SPI read data */
-uint8_t SdSpi::receive(uint8_t* buf, size_t n) {
-  for (size_t i = 0; i < n; i++) {
-    buf[i] = receive();
-  }
-  return 0;
-}
-//------------------------------------------------------------------------------
-/** Soft SPI send byte */
-void SdSpi::send(uint8_t data) {
-  softSpiBus.send(data);
-}
-//------------------------------------------------------------------------------
-void SdSpi::send(const uint8_t* buf , size_t n) {
-  for (size_t i = 0; i < n; i++) {
-    send(buf[i]);
-  }
-}
-#endif  // USE_SOFTWARE_SPI

+ 83 - 5
SdFat/SdSpiTeensy3.cpp

@@ -17,10 +17,11 @@
  * along with the Arduino SdSpi Library.  If not, see
  * <http://www.gnu.org/licenses/>.
  */
-#include <SdSpi.h>
-#if USE_NATIVE_TEENSY3_SPI
-// Teensy 3.0 functions
-#include <mk20dx128.h>
+#include "SdSpi.h"
+#if defined(__arm__) && defined(CORE_TEENSY)
+// SPI definitions
+#include "kinetis.h"
+#ifdef KINETISK
 // use 16-bit frame if SPI_USE_8BIT_FRAME is zero
 #define SPI_USE_8BIT_FRAME 0
 // Limit initial fifo to three entries to avoid fifo overrun
@@ -220,4 +221,81 @@ void SdSpi::send(const uint8_t* buf , size_t n) {
   }
 #endif  // SPI_USE_8BIT_FRAME
 }
-#endif  // USE_NATIVE_TEENSY3_SPI
+#else  // KINETISK
+//==============================================================================
+// Use standard SPI library if not KINETISK
+#include "SPI.h"
+/**
+ * Initialize SPI pins.
+ */
+void SdSpi::begin() {
+  SPI.begin();
+}
+/** Set SPI options for access to SD/SDHC cards.
+ *
+ * \param[in] divisor SCK clock divider relative to the system clock.
+ */
+void SdSpi::init(uint8_t divisor) {
+  SPI.setBitOrder(MSBFIRST);
+  SPI.setDataMode(SPI_MODE0);
+#ifndef SPI_CLOCK_DIV128
+  SPI.setClockDivider(divisor);
+#else  // SPI_CLOCK_DIV128
+  int v;
+  if (divisor <= 2) {
+    v = SPI_CLOCK_DIV2;
+  } else  if (divisor <= 4) {
+    v = SPI_CLOCK_DIV4;
+  } else  if (divisor <= 8) {
+    v = SPI_CLOCK_DIV8;
+  } else  if (divisor <= 16) {
+    v = SPI_CLOCK_DIV16;
+  } else  if (divisor <= 32) {
+    v = SPI_CLOCK_DIV32;
+  } else  if (divisor <= 64) {
+    v = SPI_CLOCK_DIV64;
+  } else {
+    v = SPI_CLOCK_DIV128;
+  }
+  SPI.setClockDivider(v);
+#endif  // SPI_CLOCK_DIV128
+}
+/** Receive a byte.
+ *
+ * \return The byte.
+ */
+uint8_t SdSpi::receive() {
+  return SPI.transfer(0XFF);
+}
+/** Receive multiple bytes.
+ *
+ * \param[out] buf Buffer to receive the data.
+ * \param[in] n Number of bytes to receive.
+ *
+ * \return Zero for no error or nonzero error code.
+ */
+uint8_t SdSpi::receive(uint8_t* buf, size_t n) {
+  for (size_t i = 0; i < n; i++) {
+    buf[i] = SPI.transfer(0XFF);
+  }
+  return 0;
+}
+/** Send a byte.
+ *
+ * \param[in] b Byte to send
+ */
+void SdSpi::send(uint8_t b) {
+  SPI.transfer(b);
+}
+/** Send multiple bytes.
+ *
+ * \param[in] buf Buffer for data to be sent.
+ * \param[in] n Number of bytes to send.
+ */
+void SdSpi::send(const uint8_t* buf , size_t n) {
+  for (size_t i = 0; i < n; i++) {
+    SPI.transfer(buf[i]);
+  }
+}
+#endif  // KINETISK
+#endif  // defined(__arm__) && defined(CORE_TEENSY)

+ 0 - 595
SdFat/SdVolume.cpp

@@ -1,595 +0,0 @@
-/* Arduino SdFat Library
- * Copyright (C) 2012 by William Greiman
- *
- * This file is part of the Arduino SdFat Library
- *
- * This Library is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This Library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with the Arduino SdFat Library.  If not, see
- * <http://www.gnu.org/licenses/>.
- */
-#include <SdFat.h>
-//------------------------------------------------------------------------------
-#if !USE_MULTIPLE_CARDS
-// raw block cache
-uint8_t  SdVolume::m_fatCount;          // number of FATs on volume
-uint32_t SdVolume::m_blocksPerFat;      // FAT size in blocks
-cache_t  SdVolume::m_cacheBuffer;       // 512 byte cache for Sd2Card
-uint32_t SdVolume::m_cacheBlockNumber;  // current block number
-uint8_t  SdVolume::m_cacheStatus;       // status of cache block
-#if USE_SEPARATE_FAT_CACHE
-cache_t  SdVolume::m_cacheFatBuffer;       // 512 byte cache for FAT
-uint32_t SdVolume::m_cacheFatBlockNumber;  // current Fat block number
-uint8_t  SdVolume::m_cacheFatStatus;       // status of cache Fatblock
-#endif  // USE_SEPARATE_FAT_CACHE
-Sd2Card* SdVolume::m_sdCard;            // pointer to SD card object
-#endif  // USE_MULTIPLE_CARDS
-//------------------------------------------------------------------------------
-// find a contiguous group of clusters
-bool SdVolume::allocContiguous(uint32_t count, uint32_t* curCluster) {
-  // start of group
-  uint32_t bgnCluster;
-  // end of group
-  uint32_t endCluster;
-  // last cluster of FAT
-  uint32_t fatEnd = m_clusterCount + 1;
-
-  // flag to save place to start next search
-  bool setStart;
-
-  // set search start cluster
-  if (*curCluster) {
-    // try to make file contiguous
-    bgnCluster = *curCluster + 1;
-
-    // don't save new start location
-    setStart = false;
-  } else {
-    // start at likely place for free cluster
-    bgnCluster = m_allocSearchStart;
-
-    // save next search start if no holes.
-    setStart = true;
-  }
-  // end of group
-  endCluster = bgnCluster;
-
-  // search the FAT for free clusters
-  for (uint32_t n = 0;; n++, endCluster++) {
-    // can't find space checked all clusters
-    if (n >= m_clusterCount) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    // past end - start from beginning of FAT
-    if (endCluster > fatEnd) {
-      bgnCluster = endCluster = 2;
-    }
-    uint32_t f;
-    if (!fatGet(endCluster, &f)) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-
-    if (f != 0) {
-      // don't update search start if unallocated clusters before endCluster.
-      if (bgnCluster != endCluster) setStart = false;
-      // cluster in use try next cluster as bgnCluster
-      bgnCluster = endCluster + 1;
-    } else if ((endCluster - bgnCluster + 1) == count) {
-      // done - found space
-      break;
-    }
-  }
-  // remember possible next free cluster
-  if (setStart) m_allocSearchStart = endCluster + 1;
-
-  // mark end of chain
-  if (!fatPutEOC(endCluster)) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // link clusters
-  while (endCluster > bgnCluster) {
-    if (!fatPut(endCluster - 1, endCluster)) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    endCluster--;
-  }
-  if (*curCluster != 0) {
-    // connect chains
-    if (!fatPut(*curCluster, bgnCluster)) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-  }
-  // return first cluster number to caller
-  *curCluster = bgnCluster;
-  return true;
-
- fail:
-  return false;
-}
-//==============================================================================
-// cache functions
-#if USE_SEPARATE_FAT_CACHE
-//------------------------------------------------------------------------------
-cache_t* SdVolume::cacheFetch(uint32_t blockNumber, uint8_t options) {
-  return cacheFetchData(blockNumber, options);
-}
-//------------------------------------------------------------------------------
-cache_t* SdVolume::cacheFetchData(uint32_t blockNumber, uint8_t options) {
-  if (m_cacheBlockNumber != blockNumber) {
-    if (!cacheWriteData()) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    if (!(options & CACHE_OPTION_NO_READ)) {
-      if (!m_sdCard->readBlock(blockNumber, m_cacheBuffer.data)) {
-        DBG_FAIL_MACRO;
-        goto fail;
-      }
-    }
-    m_cacheStatus = 0;
-    m_cacheBlockNumber = blockNumber;
-  }
-  m_cacheStatus |= options & CACHE_STATUS_MASK;
-  return &m_cacheBuffer;
-
- fail:
-  return 0;
-}
-//------------------------------------------------------------------------------
-cache_t* SdVolume::cacheFetchFat(uint32_t blockNumber, uint8_t options) {
-  if (m_cacheFatBlockNumber != blockNumber) {
-    if (!cacheWriteFat()) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    if (!(options & CACHE_OPTION_NO_READ)) {
-      if (!m_sdCard->readBlock(blockNumber, m_cacheFatBuffer.data)) {
-        DBG_FAIL_MACRO;
-        goto fail;
-      }
-    }
-    m_cacheFatStatus = 0;
-    m_cacheFatBlockNumber = blockNumber;
-  }
-  m_cacheFatStatus |= options & CACHE_STATUS_MASK;
-  return &m_cacheFatBuffer;
-
- fail:
-  return 0;
-}
-//------------------------------------------------------------------------------
-bool SdVolume::cacheSync() {
-  return cacheWriteData() && cacheWriteFat();
-}
-//------------------------------------------------------------------------------
-bool SdVolume::cacheWriteData() {
-  if (m_cacheStatus & CACHE_STATUS_DIRTY) {
-    if (!m_sdCard->writeBlock(m_cacheBlockNumber, m_cacheBuffer.data)) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    m_cacheStatus &= ~CACHE_STATUS_DIRTY;
-  }
-  return true;
-
- fail:
-  return false;
-}
-//------------------------------------------------------------------------------
-bool SdVolume::cacheWriteFat() {
-  if (m_cacheFatStatus & CACHE_STATUS_DIRTY) {
-    if (!m_sdCard->writeBlock(m_cacheFatBlockNumber, m_cacheFatBuffer.data)) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    // mirror second FAT
-    if (m_fatCount > 1) {
-      uint32_t lbn = m_cacheFatBlockNumber + m_blocksPerFat;
-      if (!m_sdCard->writeBlock(lbn, m_cacheFatBuffer.data)) {
-        DBG_FAIL_MACRO;
-        goto fail;
-      }
-    }
-    m_cacheFatStatus &= ~CACHE_STATUS_DIRTY;
-  }
-  return true;
-
- fail:
-  return false;
-}
-#else  // USE_SEPARATE_FAT_CACHE
-//------------------------------------------------------------------------------
-cache_t* SdVolume::cacheFetch(uint32_t blockNumber, uint8_t options) {
-  if (m_cacheBlockNumber != blockNumber) {
-    if (!cacheSync()) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    if (!(options & CACHE_OPTION_NO_READ)) {
-      if (!m_sdCard->readBlock(blockNumber, m_cacheBuffer.data)) {
-        DBG_FAIL_MACRO;
-        goto fail;
-      }
-    }
-    m_cacheStatus = 0;
-    m_cacheBlockNumber = blockNumber;
-  }
-  m_cacheStatus |= options & CACHE_STATUS_MASK;
-  return &m_cacheBuffer;
-
- fail:
-  return 0;
-}
-//------------------------------------------------------------------------------
-cache_t* SdVolume::cacheFetchFat(uint32_t blockNumber, uint8_t options) {
-  return cacheFetch(blockNumber, options | CACHE_STATUS_FAT_BLOCK);
-}
-//------------------------------------------------------------------------------
-bool SdVolume::cacheSync() {
-  if (m_cacheStatus & CACHE_STATUS_DIRTY) {
-    if (!m_sdCard->writeBlock(m_cacheBlockNumber, m_cacheBuffer.data)) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    // mirror second FAT
-    if ((m_cacheStatus & CACHE_STATUS_FAT_BLOCK) && m_fatCount > 1) {
-      uint32_t lbn = m_cacheBlockNumber + m_blocksPerFat;
-      if (!m_sdCard->writeBlock(lbn, m_cacheBuffer.data)) {
-        DBG_FAIL_MACRO;
-        goto fail;
-      }
-    }
-    m_cacheStatus &= ~CACHE_STATUS_DIRTY;
-  }
-  return true;
-
- fail:
-  return false;
-}
-//------------------------------------------------------------------------------
-bool SdVolume::cacheWriteData() {
-  return cacheSync();
-}
-#endif  // USE_SEPARATE_FAT_CACHE
-//------------------------------------------------------------------------------
-void SdVolume::cacheInvalidate() {
-    m_cacheBlockNumber = 0XFFFFFFFF;
-    m_cacheStatus = 0;
-}
-//==============================================================================
-//------------------------------------------------------------------------------
-uint32_t SdVolume::clusterStartBlock(uint32_t cluster) const {
-  return m_dataStartBlock + ((cluster - 2) << m_clusterSizeShift);
-}
-//------------------------------------------------------------------------------
-// Fetch a FAT entry
-bool SdVolume::fatGet(uint32_t cluster, uint32_t* value) {
-  uint32_t lba;
-  cache_t* pc;
-  // error if reserved cluster of beyond FAT
-  if (cluster < 2  || cluster > (m_clusterCount + 1)) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  if (FAT12_SUPPORT && m_fatType == 12) {
-    uint16_t index = cluster;
-    index += index >> 1;
-    lba = m_fatStartBlock + (index >> 9);
-    pc = cacheFetchFat(lba, CACHE_FOR_READ);
-    if (!pc) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    index &= 0X1FF;
-    uint16_t tmp = pc->data[index];
-    index++;
-    if (index == 512) {
-      pc = cacheFetchFat(lba + 1, CACHE_FOR_READ);
-      if (!pc) {
-        DBG_FAIL_MACRO;
-        goto fail;
-      }
-      index = 0;
-    }
-    tmp |= pc->data[index] << 8;
-    *value = cluster & 1 ? tmp >> 4 : tmp & 0XFFF;
-    return true;
-  }
-  if (m_fatType == 16) {
-    lba = m_fatStartBlock + (cluster >> 8);
-  } else if (m_fatType == 32) {
-    lba = m_fatStartBlock + (cluster >> 7);
-  } else {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  pc = cacheFetchFat(lba, CACHE_FOR_READ);
-  if (!pc) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  if (m_fatType == 16) {
-    *value = pc->fat16[cluster & 0XFF];
-  } else {
-    *value = pc->fat32[cluster & 0X7F] & FAT32MASK;
-  }
-  return true;
-
- fail:
-  return false;
-}
-//------------------------------------------------------------------------------
-// Store a FAT entry
-bool SdVolume::fatPut(uint32_t cluster, uint32_t value) {
-  uint32_t lba;
-  cache_t* pc;
-  // error if reserved cluster of beyond FAT
-  if (cluster < 2 || cluster > (m_clusterCount + 1)) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  if (FAT12_SUPPORT && m_fatType == 12) {
-    uint16_t index = cluster;
-    index += index >> 1;
-    lba = m_fatStartBlock + (index >> 9);
-    pc = cacheFetchFat(lba, CACHE_FOR_WRITE);
-    if (!pc) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    index &= 0X1FF;
-    uint8_t tmp = value;
-    if (cluster & 1) {
-      tmp = (pc->data[index] & 0XF) | tmp << 4;
-    }
-    pc->data[index] = tmp;
-
-    index++;
-    if (index == 512) {
-      lba++;
-      index = 0;
-      pc = cacheFetchFat(lba, CACHE_FOR_WRITE);
-      if (!pc) {
-        DBG_FAIL_MACRO;
-        goto fail;
-      }
-    }
-    tmp = value >> 4;
-    if (!(cluster & 1)) {
-      tmp = ((pc->data[index] & 0XF0)) | tmp >> 4;
-    }
-    pc->data[index] = tmp;
-    return true;
-  }
-  if (m_fatType == 16) {
-    lba = m_fatStartBlock + (cluster >> 8);
-  } else if (m_fatType == 32) {
-    lba = m_fatStartBlock + (cluster >> 7);
-  } else {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  pc = cacheFetchFat(lba, CACHE_FOR_WRITE);
-  if (!pc) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  // store entry
-  if (m_fatType == 16) {
-    pc->fat16[cluster & 0XFF] = value;
-  } else {
-    pc->fat32[cluster & 0X7F] = value;
-  }
-  return true;
-
- fail:
-  return false;
-}
-//------------------------------------------------------------------------------
-// free a cluster chain
-bool SdVolume::freeChain(uint32_t cluster) {
-  uint32_t next;
-
-  do {
-    if (!fatGet(cluster, &next)) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    // free cluster
-    if (!fatPut(cluster, 0)) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    if (cluster < m_allocSearchStart) m_allocSearchStart = cluster;
-    cluster = next;
-  } while (!isEOC(cluster));
-
-  return true;
-
- fail:
-  return false;
-}
-//------------------------------------------------------------------------------
-/** Volume free space in clusters.
- *
- * \return Count of free clusters for success or -1 if an error occurs.
- */
-int32_t SdVolume::freeClusterCount() {
-  uint32_t free = 0;
-  uint32_t lba;
-  uint32_t todo = m_clusterCount + 2;
-  uint16_t n;
-
-  if (FAT12_SUPPORT && m_fatType == 12) {
-    for (unsigned i = 2; i < todo; i++) {
-      uint32_t c;
-      if (!fatGet(i, &c)) {
-        DBG_FAIL_MACRO;
-        goto fail;
-      }
-      if (c == 0) free++;
-    }
-  } else if (m_fatType == 16 || m_fatType == 32) {
-    lba = m_fatStartBlock;
-    while (todo) {
-      cache_t* pc = cacheFetchFat(lba++, CACHE_FOR_READ);
-      if (!pc) {
-        DBG_FAIL_MACRO;
-        goto fail;
-      }
-      n = m_fatType == 16 ? 256 : 128;
-      if (todo < n) n = todo;
-      if (m_fatType == 16) {
-        for (uint16_t i = 0; i < n; i++) {
-          if (pc->fat16[i] == 0) free++;
-        }
-      } else {
-        for (uint16_t i = 0; i < n; i++) {
-          if (pc->fat32[i] == 0) free++;
-        }
-      }
-      todo -= n;
-    }
-  } else {
-    // invalid FAT type
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  return free;
-
- fail:
-  return -1;
-}
-//------------------------------------------------------------------------------
-/** Initialize a FAT volume.
- *
- * \param[in] dev The SD card where the volume is located.
- *
- * \param[in] part The partition to be used.  Legal values for \a part are
- * 1-4 to use the corresponding partition on a device formatted with
- * a MBR, Master Boot Record, or zero if the device is formatted as
- * a super floppy with the FAT boot sector in block zero.
- *
- * \return The value one, true, is returned for success and
- * the value zero, false, is returned for failure.  Reasons for
- * failure include not finding a valid partition, not finding a valid
- * FAT file system in the specified partition or an I/O error.
- */
-bool SdVolume::init(Sd2Card* dev, uint8_t part) {
-  uint8_t tmp;
-  uint32_t totalBlocks;
-  uint32_t volumeStartBlock = 0;
-  fat32_boot_t* fbs;
-  cache_t* pc;
-  m_sdCard = dev;
-  m_fatType = 0;
-  m_allocSearchStart = 2;
-  m_cacheStatus = 0;  // cacheSync() will write block if true
-  m_cacheBlockNumber = 0XFFFFFFFF;
-#if USE_SEPARATE_FAT_CACHE
-  m_cacheFatStatus = 0;  // cacheSync() will write block if true
-  m_cacheFatBlockNumber = 0XFFFFFFFF;
-#endif  // USE_SEPARATE_FAT_CACHE
-  // if part == 0 assume super floppy with FAT boot sector in block zero
-  // if part > 0 assume mbr volume with partition table
-  if (part) {
-    if (part > 4) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    pc = cacheFetch(volumeStartBlock, CACHE_FOR_READ);
-    if (!pc) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    part_t* p = &pc->mbr.part[part-1];
-    if ((p->boot & 0X7F) != 0 || p->firstSector == 0) {
-      // not a valid partition
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-    volumeStartBlock = p->firstSector;
-  }
-  pc = cacheFetch(volumeStartBlock, CACHE_FOR_READ);
-  if (!pc) {
-    DBG_FAIL_MACRO;
-    goto fail;
-  }
-  fbs = &(pc->fbs32);
-  if (fbs->bytesPerSector != 512 ||
-    fbs->fatCount == 0 ||
-    fbs->reservedSectorCount == 0) {
-       // not valid FAT volume
-      DBG_FAIL_MACRO;
-      goto fail;
-  }
-  m_fatCount = fbs->fatCount;
-  m_blocksPerCluster = fbs->sectorsPerCluster;
-
-  m_clusterBlockMask = m_blocksPerCluster - 1;
-
-  // determine shift that is same as multiply by m_blocksPerCluster
-  m_clusterSizeShift = 0;
-  for (tmp = 1; m_blocksPerCluster != tmp; m_clusterSizeShift++) {
-    tmp <<= 1;
-    if (tmp == 0) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-  }
-
-  m_blocksPerFat = fbs->sectorsPerFat16 ?
-                    fbs->sectorsPerFat16 : fbs->sectorsPerFat32;
-
-  m_fatStartBlock = volumeStartBlock + fbs->reservedSectorCount;
-
-  // count for FAT16 zero for FAT32
-  m_rootDirEntryCount = fbs->rootDirEntryCount;
-
-  // directory start for FAT16 dataStart for FAT32
-  m_rootDirStart = m_fatStartBlock + fbs->fatCount * m_blocksPerFat;
-
-  // data start for FAT16 and FAT32
-  m_dataStartBlock = m_rootDirStart + ((32 * fbs->rootDirEntryCount + 511)/512);
-
-  // total blocks for FAT16 or FAT32
-  totalBlocks = fbs->totalSectors16 ?
-                           fbs->totalSectors16 : fbs->totalSectors32;
-  // total data blocks
-  m_clusterCount = totalBlocks - (m_dataStartBlock - volumeStartBlock);
-
-  // divide by cluster size to get cluster count
-  m_clusterCount >>= m_clusterSizeShift;
-
-  // FAT type is determined by cluster count
-  if (m_clusterCount < 4085) {
-    m_fatType = 12;
-    if (!FAT12_SUPPORT) {
-      DBG_FAIL_MACRO;
-      goto fail;
-    }
-  } else if (m_clusterCount < 65525) {
-    m_fatType = 16;
-  } else {
-    m_rootDirStart = fbs->fat32RootCluster;
-    m_fatType = 32;
-  }
-  return true;
-
- fail:
-  return false;
-}

+ 38 - 183
SdFat/SdVolume.h

@@ -18,199 +18,54 @@
  * <http://www.gnu.org/licenses/>.
  */
 #ifndef SdVolume_h
+#include "SdSpiCard.h"
+#include "utility/FatLib.h"
 #define SdVolume_h
-/**
- * \file
- * \brief SdVolume class
- */
-#include <SdFatConfig.h>
-#include <Sd2Card.h>
-#include <utility/FatStructs.h>
+#ifndef USE_SD_VOLUME
+#error SdVolume is deperacated.  Remove this line to continue using this class.
+#endif   // USE_SD_VOLUME
 //==============================================================================
-// SdVolume class
-/**
- * \brief Cache for an SD data block
- */
-union cache_t {
-           /** Used to access cached file data blocks. */
-  uint8_t  data[512];
-           /** Used to access cached FAT16 entries. */
-  uint16_t fat16[256];
-           /** Used to access cached FAT32 entries. */
-  uint32_t fat32[128];
-           /** Used to access cached directory entries. */
-  dir_t    dir[16];
-           /** Used to access a cached Master Boot Record. */
-  mbr_t    mbr;
-           /** Used to access to a cached FAT boot sector. */
-  fat_boot_t fbs;
-           /** Used to access to a cached FAT32 boot sector. */
-  fat32_boot_t fbs32;
-           /** Used to access to a cached FAT32 FSINFO sector. */
-  fat32_fsinfo_t fsinfo;
-};
-//------------------------------------------------------------------------------
 /**
  * \class SdVolume
- * \brief Access FAT16 and FAT32 volumes on SD and SDHC cards.
+ * \brief SdVolume Soon to be removed.
  */
-class SdVolume {
+class SdVolume : public FatVolume {
  public:
-  /** Create an instance of SdVolume */
-  SdVolume() : m_fatType(0) {}
-  /** Clear the cache and returns a pointer to the cache.  Used by the WaveRP
-   * recorder to do raw write to the SD card.  Not for normal apps.
-   * \return A pointer to the cache buffer or zero if an error occurs.
-   */
-  cache_t* cacheClear() {
-    if (!cacheSync()) return 0;
-    m_cacheBlockNumber = 0XFFFFFFFF;
-    return &m_cacheBuffer;
-  }
   /** Initialize a FAT volume.  Try partition one first then try super
-   * floppy format.
-   *
-   * \param[in] dev The Sd2Card where the volume is located.
-   *
-   * \return The value one, true, is returned for success and
-   * the value zero, false, is returned for failure.  Reasons for
-   * failure include not finding a valid partition, not finding a valid
-   * FAT file system or an I/O error.
-   */
-  bool init(Sd2Card* dev) { return init(dev, 1) ? true : init(dev, 0);}
-  bool init(Sd2Card* dev, uint8_t part);
-
-  // inline functions that return volume info
-  /** \return The volume's cluster size in blocks. */
-  uint8_t blocksPerCluster() const {return m_blocksPerCluster;}
-  /** \return The number of blocks in one FAT. */
-  uint32_t blocksPerFat()  const {return m_blocksPerFat;}
-  /** \return The total number of clusters in the volume. */
-  uint32_t clusterCount() const {return m_clusterCount;}
-  /** \return The shift count required to multiply by blocksPerCluster. */
-  uint8_t clusterSizeShift() const {return m_clusterSizeShift;}
-  /** \return The logical block number for the start of file data. */
-  uint32_t dataStartBlock() const {return clusterStartBlock(2);}
-  /** \return The number of FAT structures on the volume. */
-  uint8_t fatCount() const {return m_fatCount;}
-  /** \return The logical block number for the start of the first FAT. */
-  uint32_t fatStartBlock() const {return m_fatStartBlock;}
-  /** \return The FAT type of the volume. Values are 12, 16 or 32. */
-  uint8_t fatType() const {return m_fatType;}
-  int32_t freeClusterCount();
-  /** \return The number of entries in the root directory for FAT16 volumes. */
-  uint32_t rootDirEntryCount() const {return m_rootDirEntryCount;}
-  /** \return The logical block number for the start of the root directory
-       on FAT16 volumes or the first cluster number on FAT32 volumes. */
-  uint32_t rootDirStart() const {return m_rootDirStart;}
-  /** Sd2Card object for this volume
-   * \return pointer to Sd2Card object.
-   */
-  Sd2Card* sdCard() {return m_sdCard;}
+  * floppy format.
+  *
+  * \param[in] dev The Sd2Card where the volume is located.
+  *
+  * \return true for success else false.
+  */
+  bool init(Sd2Card* dev) {
+    return init(dev, 1) ? true : init(dev, 0);
+  }
+  /** Initialize a FAT volume.
+  *
+  * \param[in] dev The Sd2Card where the volume is located.
+  * \param[in] part the partition to use. Zero for super floppy or 1-4.
+  * \return true for success else false.
+  */
+  bool init(Sd2Card* dev, uint8_t part) {
+    m_sdCard = dev;
+    return FatVolume::init(part);
+  }
 
-  /** Debug access to FAT table
-   *
-   * \param[in] n cluster number.
-   * \param[out] v value of entry
-   * \return true for success or false for failure
-   */
-  bool dbgFat(uint32_t n, uint32_t* v) {return fatGet(n, v);}
-//------------------------------------------------------------------------------
  private:
-  // Allow SdBaseFile access to SdVolume private data.
-  friend class SdBaseFile;
-//------------------------------------------------------------------------------
-  uint32_t m_allocSearchStart;   // Start cluster for alloc search.
-  uint8_t m_blocksPerCluster;    // Cluster size in blocks.
-  uint8_t m_clusterBlockMask;    // Mask to extract block of cluster.
-  uint32_t m_clusterCount;       // Clusters in one FAT.
-  uint8_t m_clusterSizeShift;    // Cluster count to block count shift.
-  uint32_t m_dataStartBlock;     // First data block number.
-  uint32_t m_fatStartBlock;      // Start block for first FAT.
-  uint8_t m_fatType;             // Volume type (12, 16, OR 32).
-  uint16_t m_rootDirEntryCount;  // Number of entries in FAT16 root dir.
-  uint32_t m_rootDirStart;       // Start block for FAT16, cluster for FAT32.
-//------------------------------------------------------------------------------
-// block caches
-// use of static functions save a bit of flash - maybe not worth complexity
-//
-  static const uint8_t CACHE_STATUS_DIRTY = 1;
-  static const uint8_t CACHE_STATUS_FAT_BLOCK = 2;
-  static const uint8_t CACHE_STATUS_MASK
-     = CACHE_STATUS_DIRTY | CACHE_STATUS_FAT_BLOCK;
-  static const uint8_t CACHE_OPTION_NO_READ = 4;
-  // value for option argument in cacheFetch to indicate read from cache
-  static uint8_t const CACHE_FOR_READ = 0;
-  // value for option argument in cacheFetch to indicate write to cache
-  static uint8_t const CACHE_FOR_WRITE = CACHE_STATUS_DIRTY;
-  // reserve cache block with no read
-  static uint8_t const CACHE_RESERVE_FOR_WRITE
-     = CACHE_STATUS_DIRTY | CACHE_OPTION_NO_READ;
-#if USE_MULTIPLE_CARDS
-  uint8_t m_fatCount;           // number of FATs on volume
-  uint32_t m_blocksPerFat;      // FAT size in blocks
-  cache_t m_cacheBuffer;        // 512 byte cache for device blocks
-  uint32_t m_cacheBlockNumber;  // Logical number of block in the cache
-  Sd2Card* m_sdCard;            // Sd2Card object for cache
-  uint8_t m_cacheStatus;        // status of cache block
-#if USE_SEPARATE_FAT_CACHE
-  cache_t m_cacheFatBuffer;       // 512 byte cache for FAT
-  uint32_t m_cacheFatBlockNumber;  // current Fat block number
-  uint8_t  m_cacheFatStatus;       // status of cache Fatblock
-#endif  // USE_SEPARATE_FAT_CACHE
-#else  // USE_MULTIPLE_CARDS
-  static uint8_t m_fatCount;            // number of FATs on volume
-  static uint32_t m_blocksPerFat;       // FAT size in blocks
-  static cache_t m_cacheBuffer;        // 512 byte cache for device blocks
-  static uint32_t m_cacheBlockNumber;  // Logical number of block in the cache
-  static uint8_t m_cacheStatus;        // status of cache block
-#if USE_SEPARATE_FAT_CACHE
-  static cache_t m_cacheFatBuffer;       // 512 byte cache for FAT
-  static uint32_t m_cacheFatBlockNumber;  // current Fat block number
-  static uint8_t  m_cacheFatStatus;       // status of cache Fatblock
-#endif  // USE_SEPARATE_FAT_CACHE
-  static Sd2Card* m_sdCard;            // Sd2Card object for cache
-#endif  // USE_MULTIPLE_CARDS
-
-  cache_t *cacheAddress() {return &m_cacheBuffer;}
-  uint32_t cacheBlockNumber() {return m_cacheBlockNumber;}
-#if USE_MULTIPLE_CARDS
-  cache_t* cacheFetch(uint32_t blockNumber, uint8_t options);
-  cache_t* cacheFetchData(uint32_t blockNumber, uint8_t options);
-  cache_t* cacheFetchFat(uint32_t blockNumber, uint8_t options);
-  void cacheInvalidate();
-  bool cacheSync();
-  bool cacheWriteData();
-  bool cacheWriteFat();
-#else  // USE_MULTIPLE_CARDS
-  static cache_t* cacheFetch(uint32_t blockNumber, uint8_t options);
-  static cache_t* cacheFetchData(uint32_t blockNumber, uint8_t options);
-  static cache_t* cacheFetchFat(uint32_t blockNumber, uint8_t options);
-  static void cacheInvalidate();
-  static bool cacheSync();
-  static bool cacheWriteData();
-  static bool cacheWriteFat();
-#endif  // USE_MULTIPLE_CARDS
-//------------------------------------------------------------------------------
-  bool allocContiguous(uint32_t count, uint32_t* curCluster);
-  uint8_t blockOfCluster(uint32_t position) const {
-    return (position >> 9) & m_clusterBlockMask;}
-  uint32_t clusterStartBlock(uint32_t cluster) const;
-  bool fatGet(uint32_t cluster, uint32_t* value);
-  bool fatPut(uint32_t cluster, uint32_t value);
-  bool fatPutEOC(uint32_t cluster) {
-    return fatPut(cluster, 0x0FFFFFFF);
+//  friend class FatFile;
+  bool readBlock(uint32_t block, uint8_t* dst) {
+    return m_sdCard->readBlock(block, dst);
   }
-  bool freeChain(uint32_t cluster);
-  bool isEOC(uint32_t cluster) const {
-    if (FAT12_SUPPORT && m_fatType == 12) return  cluster >= FAT12EOC_MIN;
-    if (m_fatType == 16) return cluster >= FAT16EOC_MIN;
-    return  cluster >= FAT32EOC_MIN;
+  bool writeBlock(uint32_t block, const uint8_t* src) {
+    return m_sdCard->writeBlock(block, src);
   }
-  bool readBlock(uint32_t block, uint8_t* dst) {
-    return m_sdCard->readBlock(block, dst);}
-  bool writeBlock(uint32_t block, const uint8_t* dst) {
-    return m_sdCard->writeBlock(block, dst);
+  bool readBlocks(uint32_t block, uint8_t* dst, size_t n) {
+    return m_sdCard->readBlocks(block, dst, n);
+  }
+  bool writeBlocks(uint32_t block, const uint8_t* src, size_t n) {
+    return m_sdCard->writeBlocks(block, src, n);
   }
+  Sd2Card* m_sdCard;             // Sd2Card object for cache
 };
-#endif  // SdVolume
+#endif  // SdVolume_h

+ 40 - 24
SdFat/examples/#attic/AnalogLogger/AnalogLogger.ino

@@ -1,5 +1,6 @@
 // A simple data logger for the Arduino analog pins with optional DS1307
 // uses RTClib from https://github.com/adafruit/RTClib
+#include <SPI.h>
 #include <SdFat.h>
 #include <SdFatUtil.h>  // define FreeRam()
 
@@ -28,7 +29,7 @@ char buf[80];
 #endif  // SENSOR_COUNT
 //------------------------------------------------------------------------------
 // store error strings in flash to save RAM
-#define error(s) sd.errorHalt_P(PSTR(s))
+#define error(s) sd.errorHalt(F(s))
 //------------------------------------------------------------------------------
 #if USE_DS1307
 // use RTClib from Adafruit
@@ -44,7 +45,7 @@ RTC_DS1307 RTC;  // define the Real Time Clock object
 //------------------------------------------------------------------------------
 // call back for file timestamps
 void dateTime(uint16_t* date, uint16_t* time) {
-    DateTime now = RTC.now();
+  DateTime now = RTC.now();
 
   // return date using FAT_DATE macro to format fields
   *date = FAT_DATE(now.year(), now.month(), now.day());
@@ -64,21 +65,24 @@ ostream& operator << (ostream& os, DateTime& dt) {
 //------------------------------------------------------------------------------
 void setup() {
   Serial.begin(9600);
-  while (!Serial){}  // wait for Leonardo
-  
-  // pstr stores strings in flash to save RAM
-  cout << endl << pstr("FreeRam: ") << FreeRam() << endl;
+  while (!Serial) {} // wait for Leonardo
+
+  // F() stores strings in flash to save RAM
+  cout << endl << F("FreeRam: ") << FreeRam() << endl;
 
 #if WAIT_TO_START
-  cout << pstr("Type any character to start\n");
+  cout << F("Type any character to start\n");
   while (Serial.read() <= 0) {}
   delay(400);  // catch Due reset problem
+  while (Serial.read() >= 0) {}
 #endif  // WAIT_TO_START
 
 #if USE_DS1307
   // connect to RTC
   Wire.begin();
-  if (!RTC.begin()) error("RTC failed");
+  if (!RTC.begin()) {
+    error("RTC failed");
+  }
 
   // set date time callback function
   SdFile::dateTimeCallback(dateTime);
@@ -87,34 +91,40 @@ void setup() {
 #endif  // USE_DS1307
 
   // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
-  if (!sd.begin(SD_CHIP_SELECT, SPI_HALF_SPEED)) sd.initErrorHalt();
+  if (!sd.begin(SD_CHIP_SELECT, SPI_HALF_SPEED)) {
+    sd.initErrorHalt();
+  }
 
   // create a new file in root, the current working directory
-  char name[] = "LOGGER00.CSV";
+  char name[] = "logger00.csv";
 
   for (uint8_t i = 0; i < 100; i++) {
     name[6] = i/10 + '0';
     name[7] = i%10 + '0';
-    if (sd.exists(name)) continue;
+    if (sd.exists(name)) {
+      continue;
+    }
     logfile.open(name);
     break;
   }
-  if (!logfile.is_open()) error("file.open");
+  if (!logfile.is_open()) {
+    error("file.open");
+  }
+
+  cout << F("Logging to: ") << name << endl;
+  cout << F("Type any character to stop\n\n");
 
-  cout << pstr("Logging to: ") << name << endl;
-  cout << pstr("Type any character to stop\n\n");
-  
   // format header in buffer
   obufstream bout(buf, sizeof(buf));
 
-  bout << pstr("millis");
+  bout << F("millis");
 
 #if USE_DS1307
-  bout << pstr(",date,time");
+  bout << F(",date,time");
 #endif  // USE_DS1307
 
   for (uint8_t i = 0; i < SENSOR_COUNT; i++) {
-    bout << pstr(",sens") << int(i);
+    bout << F(",sens") << int(i);
   }
   logfile << buf << endl;
 
@@ -156,17 +166,23 @@ void loop() {
   logfile << buf << flush;
 
   // check for error
-  if (!logfile) error("write data failed");
+  if (!logfile) {
+    error("write data failed");
+  }
 
 #if ECHO_TO_SERIAL
   cout << buf;
 #endif  // ECHO_TO_SERIAL
 
   // don't log two points in the same millis
-  if (m == millis()) delay(1);
-  
-  if (!Serial.available()) return;
+  if (m == millis()) {
+    delay(1);
+  }
+
+  if (!Serial.available()) {
+    return;
+  }
   logfile.close();
-  cout << pstr("Done!");
+  cout << F("Done!");
   while (1);
-}
+}

+ 38 - 0
SdFat/examples/#attic/BaseExtCaseTest/BaseExtCaseTest.ino

@@ -0,0 +1,38 @@
+/*
+ * Program to test Short File Name character case flags.
+ */
+#include <SPI.h>
+#include <SdFat.h>
+
+SdFat sd;
+
+SdFile file;
+char* name[] = {
+  "low.low", "low.Mix", "low.UP",
+  "Mix.low", "Mix.Mix", "Mix.UP",
+  "UP.low",  "UP.Mix",  "UP.UP"
+};
+//------------------------------------------------------------------------------
+void setup() {
+  Serial.begin(9600);
+  while (!Serial) {}  // wait for Leonardo
+  Serial.println("type any character to start");
+  while (Serial.read() < 0) {}
+  if (!sd.begin()) {
+    Serial.println("begin failed");
+    return;
+  }
+  for (uint8_t i = 0; i < 9; i++) {
+    sd.remove(name[i]);
+    if (!file.open(name[i], O_RDWR | O_CREAT | O_EXCL)) {
+      sd.errorHalt(name[i]);
+    }
+    file.println(name[i]);
+
+    file.close();
+  }
+  sd.ls(LS_DATE|LS_SIZE);
+  Serial.println("Done");
+}
+//------------------------------------------------------------------------------
+void loop() {}

+ 2 - 1
SdFat/examples/#attic/HelloWorld/HelloWorld.ino

@@ -1,3 +1,4 @@
+#include <SPI.h>
 #include <SdFat.h>
 
 //  create a serial output stream
@@ -5,7 +6,7 @@ ArduinoOutStream cout(Serial);
 
 void setup() {
   Serial.begin(9600);
-  
+
   while (!Serial) {}  // wait for Leonardo
   delay(2000);
 

+ 17 - 6
SdFat/examples/#attic/MiniSerial/MiniSerial.ino

@@ -3,16 +3,27 @@
 //
 // This is useful for debug and saves RAM
 // Will not work on Due, Leonardo, or Teensy
+
+#include <SPI.h>
 #include <SdFat.h>
 #include <SdFatUtil.h>
-#ifndef UDR0
-#error no AVR serial port0
-#endif
+#ifdef UDR0  // Must be AVR with serial port zero.
+#include <MinimumSerial.h>
+
+MinimumSerial MiniSerial;
+
 void setup() {
   MiniSerial.begin(9600);
   MiniSerial.println(FreeRam());
+}
+void loop() {
+  int c;
   MiniSerial.println(F("Type any Character"));
-  while(MiniSerial.read() < 0) {}
-  MiniSerial.println(F("Done"));
+  while ((c = MiniSerial.read()) < 0) {}
+  MiniSerial.print(F("Read: "));
+  MiniSerial.println((char)c);
+  while (MiniSerial.read() >= 0) {}
 }
-void loop() {}
+#else  // UDR0
+#error no AVR serial port 0
+#endif  // UDR0

+ 23 - 15
SdFat/examples/#attic/PrintBenchmarkSD/PrintBenchmarkSD.ino

@@ -1,8 +1,8 @@
 /*
- * This sketch is a simple Print benchmark.
+ * This program is a simple Print benchmark.
  */
-#include <SD.h>
 #include <SPI.h>
+#include <SD.h>
 
 // SD chip select pin
 const uint8_t chipSelect = SS;
@@ -34,7 +34,7 @@ void loop() {
 
   while (Serial.read() >= 0) {
   }
-  // pstr stores strings in flash to save RAM
+  // F() stores strings in flash to save RAM
   Serial.println(F("Type any character to start"));
   while (Serial.read() <= 0) {
   }
@@ -42,17 +42,21 @@ void loop() {
 
 
   // initialize the SD card
-  
-  if (!SD.begin(chipSelect)) error("begin");
 
- 
+  if (!SD.begin(chipSelect)) {
+    error("begin");
+  }
+
+
   Serial.println(F("Starting print test.  Please wait.\n"));
 
   // do write test
   for (int test = 0; test < 2; test++) {
-    file = SD.open("BENCH.TXT", FILE_WRITE);
-  
-  if (!file) error("open failed");
+    file = SD.open("bench.txt", FILE_WRITE);
+
+    if (!file) {
+      error("open failed");
+    }
     switch(test) {
     case 0:
       Serial.println(F("Test of println(uint16_t)"));
@@ -79,12 +83,16 @@ void loop() {
         break;
       }
 
-//      if (file.writeError) {
-//        error("write failed");
-//      }
+      if (file.getWriteError()) {
+        error("write failed");
+      }
       m = micros() - m;
-      if (maxLatency < m) maxLatency = m;
-      if (minLatency > m) minLatency = m;
+      if (maxLatency < m) {
+        maxLatency = m;
+      }
+      if (minLatency > m) {
+        minLatency = m;
+      }
       totalLatency += m;
     }
     file.flush();
@@ -106,7 +114,7 @@ void loop() {
     Serial.print(F(" usec, Avg Latency: "));
     Serial.print(totalLatency/N_PRINT);
     Serial.println(F(" usec\n"));
-    SD.remove("BENCH.TXT");
+    SD.remove("bench.txt");
   }
   file.close();
   Serial.println(F("Done!\n"));

+ 2 - 2
SdFat/examples/#attic/SD_Size/SD_Size.ino

@@ -1,6 +1,6 @@
 /*
- * Sketch to compare size of Arduino SD library with SdFat V2.
- * See SdFatSize.pde for SdFat sketch.
+ * Program to compare size of Arduino SD library with SdFat.
+ * See SdFatSize.ino for SdFat program.
  */
 #include <SPI.h>
 #include <SD.h>

+ 4 - 3
SdFat/examples/#attic/SdFatSize/SdFatSize.ino

@@ -1,8 +1,9 @@
 /*
- * Sketch to compare size of SdFat V2 with Arduino SD library.
- * See SD_Size.pde for Arduino SD sketch.
+ * Program to compare size of SdFat with Arduino SD library.
+ * See SD_Size.ino for Arduino SD program.
  *
  */
+#include <SPI.h>
 #include <SdFat.h>
 
 SdFat sd;
@@ -17,7 +18,7 @@ void setup() {
     Serial.println("begin failed");
     return;
   }
-  file.open("SIZE_TST.TXT", O_RDWR | O_CREAT | O_AT_END);
+  file.open("SizeTest.txt", O_RDWR | O_CREAT | O_AT_END);
 
   file.println("Hello");
 

+ 22 - 13
SdFat/examples/#attic/append/append.ino

@@ -1,10 +1,11 @@
 /*
  * Append Example
  *
- * This sketch shows how to use open for append.
- * The sketch will append 100 line each time it opens the file.
- * The sketch will open and close the file 100 times.
+ * This program shows how to use open for append.
+ * The program will append 100 line each time it opens the file.
+ * The program will open and close the file 100 times.
  */
+#include <SPI.h>
 #include <SdFat.h>
 
 // SD chip select pin
@@ -17,30 +18,34 @@ SdFat sd;
 ArduinoOutStream cout(Serial);
 
 // store error strings in flash to save RAM
-#define error(s) sd.errorHalt_P(PSTR(s))
+#define error(s) sd.errorHalt(F(s))
 //------------------------------------------------------------------------------
 void setup() {
   // filename for this example
-  char name[] = "APPEND.TXT";
+  char name[] = "append.txt";
 
   Serial.begin(9600);
   while (!Serial) {}  // wait for Leonardo
 
-  // pstr() stores strings in flash to save RAM
-  cout << endl << pstr("Type any character to start\n");
+  // F() stores strings in flash to save RAM
+  cout << endl << F("Type any character to start\n");
   while (Serial.read() <= 0) {}
   delay(400);  // Catch Due reset problem
 
   // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
   // breadboards.  use SPI_FULL_SPEED for better performance.
-  if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt();
+  if (!sd.begin(chipSelect, SPI_HALF_SPEED)) {
+    sd.initErrorHalt();
+  }
+
+  cout << F("Appending to: ") << name;
 
-  cout << pstr("Appending to: ") << name;
-  
   for (uint8_t i = 0; i < 100; i++) {
     // open stream for append
     ofstream sdout(name, ios::out | ios::app);
-    if (!sdout) error("open failed");
+    if (!sdout) {
+      error("open failed");
+    }
 
     // append 100 lines to the file
     for (uint8_t j = 0; j < 100; j++) {
@@ -51,10 +56,14 @@ void setup() {
     // close the stream
     sdout.close();
 
-    if (!sdout) error("append data failed");
+    if (!sdout) {
+      error("append data failed");
+    }
 
     // output progress indicator
-    if (i % 25 == 0) cout << endl;
+    if (i % 25 == 0) {
+      cout << endl;
+    }
     cout << '.';
   }
   cout << endl << "Done" << endl;

+ 15 - 8
SdFat/examples/#attic/average/average.ino

@@ -1,6 +1,7 @@
 /*
- * Calculate the sum and average of a list of floating point numbers 
+ * Calculate the sum and average of a list of floating point numbers
  */
+#include <SPI.h>
 #include <SdFat.h>
 
 // SD chip select pin
@@ -14,13 +15,15 @@ ArduinoOutStream cout(Serial);
 //------------------------------------------------------------------------------
 void writeTestFile() {
   // open the output file
-  ofstream sdout("AVG_TEST.TXT");
+  ofstream sdout("AvgTest.txt");
 
   // write a series of float numbers
   for (int16_t i = -1001; i < 2000; i += 13) {
     sdout << 0.1 * i << endl;
   }
-  if (!sdout) sd.errorHalt("sdout failed");
+  if (!sdout) {
+    sd.errorHalt("sdout failed");
+  }
 
   sdout.close();
 }
@@ -31,10 +34,12 @@ void calcAverage() {
   double sum = 0;  // sum of input numbers
 
   // open the input file
-  ifstream sdin("AVG_TEST.TXT");
+  ifstream sdin("AvgTest.txt");
 
   // check for an open failure
-  if (!sdin) sd.errorHalt("sdin failed");
+  if (!sdin) {
+    sd.errorHalt("sdin failed");
+  }
 
   // read and sum the numbers
   while (sdin >> num) {
@@ -51,14 +56,16 @@ void setup() {
   Serial.begin(9600);
   while (!Serial) {}  // wait for Leonardo
 
-  // pstr stores strings in flash to save RAM
-  cout << pstr("Type any character to start\n");
+  // F() stores strings in flash to save RAM
+  cout << F("Type any character to start\n");
   while (Serial.read() <= 0) {}
   delay(400);  // Catch Due reset problem
 
   // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
   // breadboards.  use SPI_FULL_SPEED for better performance.
-  if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt();
+  if (!sd.begin(chipSelect, SPI_HALF_SPEED)) {
+    sd.initErrorHalt();
+  }
 
   // write the test file
   writeTestFile();

+ 20 - 13
SdFat/examples/#attic/benchSD/benchSD.ino

@@ -1,11 +1,10 @@
 /*
- * This sketch is a simple binary write/read benchmark
+ * This program is a simple binary write/read benchmark
  * for the standard Arduino SD.h library.
  */
 #include <SPI.h>
 #include <SD.h>
 
-
 // SD chip select pin
 const uint8_t chipSelect = SS;
 
@@ -15,7 +14,6 @@ const uint8_t chipSelect = SS;
 
 uint8_t buf[BUF_SIZE];
 
-
 // test file
 File file;
 
@@ -27,7 +25,7 @@ void error(char* s) {
 //------------------------------------------------------------------------------
 void setup() {
   Serial.begin(9600);
-  while (!Serial){}  // wait for Leonardo
+  while (!Serial) {} // wait for Leonardo
 }
 //------------------------------------------------------------------------------
 void loop() {
@@ -38,16 +36,17 @@ void loop() {
   // discard any input
   while (Serial.read() >= 0) {}
 
-  // pstr stores strings in flash to save RAM
+  // F() stores strings in flash to save RAM
   Serial.println(F("Type any character to start"));
   while (Serial.read() <= 0) {}
   delay(400);  // catch Due reset problem
-  
-  if (!SD.begin(chipSelect)) error("begin");
+
+  if (!SD.begin(chipSelect)) {
+    error("begin");
+  }
 
   // open or create file - truncate existing file.
-  file = SD.open("BENCH.DAT", FILE_WRITE | O_TRUNC);
-//  file = SD.open("BENCH.DAT", O_CREAT | O_TRUNC | O_CREAT);
+  file = SD.open("Bench.dat", O_RDWR | O_TRUNC | O_CREAT);
   if (!file) {
     error("open failed");
   }
@@ -79,8 +78,12 @@ void loop() {
       error("write failed");
     }
     m = micros() - m;
-    if (maxLatency < m) maxLatency = m;
-    if (minLatency > m) minLatency = m;
+    if (maxLatency < m) {
+      maxLatency = m;
+    }
+    if (minLatency > m) {
+      minLatency = m;
+    }
     totalLatency += m;
   }
   file.flush();
@@ -110,8 +113,12 @@ void loop() {
       error("read failed");
     }
     m = micros() - m;
-    if (maxLatency < m) maxLatency = m;
-    if (minLatency > m) minLatency = m;
+    if (maxLatency < m) {
+      maxLatency = m;
+    }
+    if (minLatency > m) {
+      minLatency = m;
+    }
     totalLatency += m;
     if (buf[BUF_SIZE-1] != '\n') {
       error("data check");

+ 2 - 1
SdFat/examples/#attic/bufstream/bufstream.ino

@@ -1,6 +1,7 @@
 /*
  * Use of ibufsteam to parse a line and obufstream to format a line
  */
+#include <SPI.h>
 #include <SdFat.h>
 
 // create a serial output stream
@@ -13,7 +14,7 @@ void setup() {
   Serial.begin(9600);
   while (!Serial) {}  // wait for Leonardo
   delay(2000);
-  
+
   // initialize input string
   ibufstream bin("123 456 789");
 

+ 13 - 8
SdFat/examples/#attic/eventlog/eventlog.ino

@@ -1,6 +1,7 @@
 /*
  * Append a line to a file - demo of pathnames and streams
  */
+#include <SPI.h>
 #include <SdFat.h>
 
 // SD chip select pin
@@ -13,20 +14,22 @@ SdFat sd;
 ArduinoOutStream cout(Serial);
 //------------------------------------------------------------------------------
 /*
- * Append a line to LOGFILE.TXT
+ * Append a line to logfile.txt
  */
 void logEvent(const char *msg) {
   // create dir if needed
-  sd.mkdir("LOGS/2011/JAN");
+  sd.mkdir("logs/2014/Jan");
 
   // create or open a file for append
-  ofstream sdlog("LOGS/2011/JAN/LOGFILE.TXT", ios::out | ios::app);
+  ofstream sdlog("logs/2014/Jan/logfile.txt", ios::out | ios::app);
 
   // append a line to the file
   sdlog << msg << endl;
 
   // check for errors
-  if (!sdlog) sd.errorHalt("append failed");
+  if (!sdlog) {
+    sd.errorHalt("append failed");
+  }
 
   sdlog.close();
 }
@@ -35,19 +38,21 @@ void setup() {
   Serial.begin(9600);
   while (!Serial) {}  // wait for Leonardo
 
-  // pstr stores strings in flash to save RAM
-  cout << pstr("Type any character to start\n");
+  // F() stores strings in flash to save RAM
+  cout << F("Type any character to start\n");
   while (Serial.read() <= 0) {}
   delay(400);  // catch Due reset problem
 
   // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
   // breadboards.  use SPI_FULL_SPEED for better performance.
-  if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt();
+  if (!sd.begin(chipSelect, SPI_HALF_SPEED)) {
+    sd.initErrorHalt();
+  }
 
   // append a line to the logfile
   logEvent("Another line for the logfile");
 
-  cout << "Done - check /LOGS/2011/JAN/LOGFILE.TXT on the SD" << endl;
+  cout << F("Done - check /logs/2014/Jan/logfile.txt on the SD") << endl;
 }
 //------------------------------------------------------------------------------
 void loop() {}

+ 52 - 37
SdFat/examples/#attic/fgetsRewrite/fgetsRewrite.ino

@@ -1,4 +1,5 @@
 // Demo of rewriting a line read by fgets
+#include <SPI.h>
 #include <SdFat.h>
 
 // SD card chip select pin
@@ -11,24 +12,28 @@ SdFat sd;
 ArduinoOutStream cout(Serial);
 //------------------------------------------------------------------------------
 // store error strings in flash memory
-#define error(s) sd.errorHalt_P(PSTR(s))
+#define error(s) sd.errorHalt(F(s))
 //------------------------------------------------------------------------------
 void demoFgets() {
   char line[25];
   int c;
   uint32_t pos;
-  
+
   // open test file
-  SdFile rdfile("FGETS.TXT", O_RDWR);
-  
+  SdFile rdfile("fgets.txt", O_RDWR);
+
   // check for open error
-  if (!rdfile.isOpen()) error("demoFgets");
-  
+  if (!rdfile.isOpen()) {
+    error("demoFgets");
+  }
+
   // list file
-  cout << pstr("-----Before Rewrite\r\n");
-  while ((c = rdfile.read()) >= 0) Serial.write(c); 
-  
-  rdfile.rewind();  
+  cout << F("-----Before Rewrite\r\n");
+  while ((c = rdfile.read()) >= 0) {
+    Serial.write(c);
+  }
+
+  rdfile.rewind();
   // read lines from the file to get position
   while (1) {
     pos = rdfile.curPosition();
@@ -36,56 +41,66 @@ void demoFgets() {
       error("Line not found");
     }
     // find line that contains "Line C"
-    if (strstr(line, "Line C"))break;
+    if (strstr(line, "Line C")) {
+      break;
+    }
+  }
+
+  // rewrite line with 'C'
+  if (!rdfile.seekSet(pos)) {
+    error("seekSet");
   }
-  
-  // rewrite line with 'C'  
-  if (!rdfile.seekSet(pos))error("seekSet");
   rdfile.println("Line R");
   rdfile.rewind();
-  
+
   // list file
-  cout << pstr("\r\n-----After Rewrite\r\n");
-  while ((c = rdfile.read()) >= 0) Serial.write(c);
-  
+  cout << F("\r\n-----After Rewrite\r\n");
+  while ((c = rdfile.read()) >= 0) {
+    Serial.write(c);
+  }
+
   // close so rewrite is not lost
   rdfile.close();
 }
 //------------------------------------------------------------------------------
 void makeTestFile() {
   // create or open test file
-  SdFile wrfile("FGETS.TXT", O_WRITE | O_CREAT | O_TRUNC);
-  
+  SdFile wrfile("fgets.txt", O_WRITE | O_CREAT | O_TRUNC);
+
   // check for open error
-  if (!wrfile.isOpen()) error("MakeTestFile");
-  
+  if (!wrfile.isOpen()) {
+    error("MakeTestFile");
+  }
+
   // write test file
-  wrfile.write_P(PSTR(
-    "Line A\r\n"
-    "Line B\r\n"
-    "Line C\r\n"
-    "Line D\r\n"
-    "Line E\r\n"
-  ));
+  wrfile.print(F(
+                 "Line A\r\n"
+                 "Line B\r\n"
+                 "Line C\r\n"
+                 "Line D\r\n"
+                 "Line E\r\n"
+               ));
   wrfile.close();
 }
 //------------------------------------------------------------------------------
 void setup(void) {
   Serial.begin(9600);
-  while (!Serial){}  // wait for Leonardo
+  while (!Serial) {} // wait for Leonardo
 
-  cout << pstr("Type any character to start\n");
+  cout << F("Type any character to start\n");
   while (Serial.read() <= 0) {}
   delay(400);  // catch Due reset problem
-  
+
   // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
   // breadboards.  use SPI_FULL_SPEED for better performance.
-  if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt();
-  
+  if (!sd.begin(chipSelect, SPI_HALF_SPEED)) {
+    sd.initErrorHalt();
+  }
+
   makeTestFile();
-  
+
   demoFgets();
-  
-  cout << pstr("\nDone\n");
+
+  cout << F("\nDone\n");
 }
 void loop(void) {}

+ 14 - 7
SdFat/examples/#attic/readlog/readlog.ino

@@ -1,7 +1,8 @@
 /*
- * Read the logfile created by the eventlog.pde example.
+ * Read the logfile created by the eventlog.ino example.
  * Demo of pathnames and working directories
  */
+#include <SPI.h>
 #include <SdFat.h>
 
 // SD chip select pin
@@ -20,19 +21,25 @@ void setup() {
 
   // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
   // breadboards.  use SPI_FULL_SPEED for better performance.
-  if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt();
+  if (!sd.begin(chipSelect, SPI_HALF_SPEED)) {
+    sd.initErrorHalt();
+  }
 
   // set current working directory
-  if (!sd.chdir("LOGS/2011/JAN/")) {
-    sd.errorHalt("chdir failed. Did you run eventlog.pde?");
+  if (!sd.chdir("logs/2014/Jan/")) {
+    sd.errorHalt("chdir failed. Did you run eventlog.ino?");
   }
   // open file in current working directory
-  ifstream file("LOGFILE.TXT");
+  ifstream file("logfile.txt");
 
-  if (!file.is_open()) sd.errorHalt("open failed");
+  if (!file.is_open()) {
+    sd.errorHalt("open failed");
+  }
 
   // copy the file to Serial
-  while ((c = file.get()) >= 0) cout << (char)c;
+  while ((c = file.get()) >= 0) {
+    cout << (char)c;
+  }
 
   cout << "Done" << endl;
 }

+ 0 - 2
SdFat/examples/#attic/readme.txt

@@ -26,5 +26,3 @@ readlog - Read file. Demo of pathnames and current working directory.
 SD_Size - Determine flash used by SD.h example.
 
 SdFatSize - Determine flash used by SdFat.
-
-TestMkdirRmdir - Test mkdir, rmdir, and directory management.

+ 3 - 3
SdFat/examples/AnalogBinLogger/AnalogBinLogger.h

@@ -14,7 +14,7 @@ struct metadata_t {
 // Data block for 8-bit ADC mode.
 const size_t DATA_DIM8 = 508;
 struct block8_t {
-  unsigned short count;    // count of data bytes
+  unsigned short count;    // count of data values
   unsigned short overrun;  // count of overruns since last block
   unsigned char  data[DATA_DIM8];
 };
@@ -22,14 +22,14 @@ struct block8_t {
 // Data block for 10-bit ADC mode.
 const size_t DATA_DIM16 = 254;
 struct block16_t {
-  unsigned short count;    // count of data bytes
+  unsigned short count;    // count of data values
   unsigned short overrun;  // count of overruns since last block
   unsigned short data[DATA_DIM16];
 };
 //------------------------------------------------------------------------------
 // Data block for PC use
 struct adcdata_t {
-  unsigned short count;    // count of data bytes
+  unsigned short count;    // count of data values
   unsigned short overrun;  // count of overruns since last block
   union {
     unsigned char  u8[DATA_DIM8];

+ 121 - 79
SdFat/examples/AnalogBinLogger/AnalogBinLogger.ino

@@ -4,7 +4,7 @@
  * Samples are logged at regular intervals. Each Sample consists of the ADC
  * values for the analog pins defined in the PIN_LIST array.  The pins numbers
  * may be in any order.
- * 
+ *
  * Edit the configuration constants below to set the sample pins, sample rate,
  * and other configuration values.
  *
@@ -19,9 +19,10 @@
  *
  * Data is written to the file using a SD multiple block write command.
  */
+#ifdef __AVR__
+#include <SPI.h>
 #include <SdFat.h>
 #include <SdFatUtil.h>
-#include <StdioStream.h>
 #include "AnalogBinLogger.h"
 //------------------------------------------------------------------------------
 // Analog pin number list for a sample.  Pins may be in any order and pin
@@ -49,7 +50,7 @@ const float SAMPLE_INTERVAL = 1.0/SAMPLE_RATE;
 //
 // You can select an ADC clock rate by defining the symbol ADC_PRESCALER to
 // one of these values.  You must choose an appropriate ADC clock rate for
-// your sample interval. 
+// your sample interval.
 // #define ADC_PRESCALER 7 // F_CPU/128 125 kHz on an Uno
 // #define ADC_PRESCALER 6 // F_CPU/64  250 kHz on an Uno
 // #define ADC_PRESCALER 5 // F_CPU/32  500 kHz on an Uno
@@ -71,7 +72,7 @@ uint8_t const ADC_REF = (1 << REFS0);  // Vcc Reference.
 const uint32_t FILE_BLOCK_COUNT = 256000;
 
 // log file base name.  Must be six characters or less.
-#define FILE_BASE_NAME "ANALOG"
+#define FILE_BASE_NAME "analog"
 
 // Set RECORD_EIGHT_BITS non-zero to record only the high 8-bits of the ADC.
 #define RECORD_EIGHT_BITS 0
@@ -88,7 +89,7 @@ const uint8_t SD_CS_PIN = SS;
 //------------------------------------------------------------------------------
 // Buffer definitions.
 //
-// The logger will use SdFat's buffer plus BUFFER_BLOCK_COUNT additional 
+// The logger will use SdFat's buffer plus BUFFER_BLOCK_COUNT additional
 // buffers.  QUEUE_DIM must be a power of two larger than
 //(BUFFER_BLOCK_COUNT + 1).
 //
@@ -123,7 +124,7 @@ const uint8_t QUEUE_DIM = 32;  // Must be a power of two!
 // End of configuration constants.
 //==============================================================================
 // Temporary log file.  Will be deleted if a reset or power failure occurs.
-#define TMP_FILE_NAME "TMP_LOG.BIN"
+#define TMP_FILE_NAME "tmp_log.bin"
 
 // Size of file base name.  Must not be larger than six.
 const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1;
@@ -144,7 +145,7 @@ SdFat sd;
 
 SdBaseFile binFile;
 
-char binName[13] = FILE_BASE_NAME "00.BIN";
+char binName[13] = FILE_BASE_NAME "00.bin";
 
 #if RECORD_EIGHT_BITS
 const size_t SAMPLES_PER_BLOCK = DATA_DIM8/PIN_COUNT;
@@ -163,7 +164,9 @@ volatile uint8_t fullHead;  // volatile insures non-interrupt code sees changes.
 uint8_t fullTail;
 
 // queueNext assumes QUEUE_DIM is a power of two
-inline uint8_t queueNext(uint8_t ht) {return (ht + 1) & (QUEUE_DIM -1);}
+inline uint8_t queueNext(uint8_t ht) {
+  return (ht + 1) & (QUEUE_DIM -1);
+}
 //==============================================================================
 // Interrupt Service Routines
 
@@ -192,14 +195,16 @@ ISR(ADC_vect) {
 #if RECORD_EIGHT_BITS
   uint8_t d = ADCH;
 #else  // RECORD_EIGHT_BITS
-  // This will access ADCL first. 
+  // This will access ADCL first.
   uint16_t d = ADC;
 #endif  // RECORD_EIGHT_BITS
 
   if (isrBufNeeded && emptyHead == emptyTail) {
     // no buffers - count overrun
-    if (isrOver < 0XFFFF) isrOver++;
-    
+    if (isrOver < 0XFFFF) {
+      isrOver++;
+    }
+
     // Avoid missed timer error.
     timerFlag = false;
     return;
@@ -209,30 +214,32 @@ ISR(ADC_vect) {
     ADMUX = adcmux[adcindex];
     ADCSRB = adcsrb[adcindex];
     ADCSRA = adcsra[adcindex];
-    if (adcindex == 0) timerFlag = false;
+    if (adcindex == 0) {
+      timerFlag = false;
+    }
     adcindex =  adcindex < (PIN_COUNT - 1) ? adcindex + 1 : 0;
   } else {
     timerFlag = false;
   }
   // Check for buffer needed.
-  if (isrBufNeeded) {   
+  if (isrBufNeeded) {
     // Remove buffer from empty queue.
     isrBuf = emptyQueue[emptyTail];
     emptyTail = queueNext(emptyTail);
     isrBuf->count = 0;
     isrBuf->overrun = isrOver;
-    isrBufNeeded = false;    
+    isrBufNeeded = false;
   }
   // Store ADC data.
   isrBuf->data[isrBuf->count++] = d;
 
   // Check for buffer full.
   if (isrBuf->count >= PIN_COUNT*SAMPLES_PER_BLOCK) {
-    // Put buffer isrIn full queue.  
+    // Put buffer isrIn full queue.
     uint8_t tmp = fullHead;  // Avoid extra fetch of volatile fullHead.
     fullQueue[tmp] = (block_t*)isrBuf;
     fullHead = queueNext(tmp);
-   
+
     // Set buffer needed and clear overruns.
     isrBufNeeded = true;
     isrOver = 0;
@@ -242,15 +249,17 @@ ISR(ADC_vect) {
 // timer1 interrupt to clear OCF1B
 ISR(TIMER1_COMPB_vect) {
   // Make sure ADC ISR responded to timer event.
-  if (timerFlag) timerError = true;
+  if (timerFlag) {
+    timerError = true;
+  }
   timerFlag = true;
 }
 //==============================================================================
 // Error messages stored in flash.
-#define error(msg) error_P(PSTR(msg))
+#define error(msg) errorFlash(F(msg))
 //------------------------------------------------------------------------------
-void error_P(const char* msg) {
-  sd.errorPrint_P(msg);
+void errorFlash(const __FlashStringHelper* msg) {
+  sd.errorPrint(msg);
   fatalBlink();
 }
 //------------------------------------------------------------------------------
@@ -272,7 +281,7 @@ void fatalBlink() {
 //------------------------------------------------------------------------------
 // initialize ADC and timer1
 void adcInit(metadata_t* meta) {
-  uint8_t adps;  // prescaler bits for ADCSRA 
+  uint8_t adps;  // prescaler bits for ADCSRA
   uint32_t ticks = F_CPU*SAMPLE_INTERVAL + 0.5;  // Sample interval cpu cycles.
 
   if (ADC_REF & ~((1 << REFS0) | (1 << REFS1))) {
@@ -286,10 +295,12 @@ void adcInit(metadata_t* meta) {
 #else  // ADC_PRESCALER
   // Allow extra cpu cycles to change ADC settings if more than one pin.
   int32_t adcCycles = (ticks - ISR_TIMER0)/PIN_COUNT;
-                      - (PIN_COUNT > 1 ? ISR_SETUP_ADC : 0);
-                      
+  - (PIN_COUNT > 1 ? ISR_SETUP_ADC : 0);
+
   for (adps = 7; adps > 0; adps--) {
-     if (adcCycles >= (MIN_ADC_CYCLES << adps)) break;
+    if (adcCycles >= (MIN_ADC_CYCLES << adps)) {
+      break;
+    }
   }
 #endif  // ADC_PRESCALER
   meta->adcFrequency = F_CPU >> adps;
@@ -308,20 +319,26 @@ void adcInit(metadata_t* meta) {
   }
   meta->pinCount = PIN_COUNT;
   meta->recordEightBits = RECORD_EIGHT_BITS;
-  
+
   for (int i = 0; i < PIN_COUNT; i++) {
     uint8_t pin = PIN_LIST[i];
-    if (pin >= NUM_ANALOG_INPUTS) error("Invalid Analog pin number");
+    if (pin >= NUM_ANALOG_INPUTS) {
+      error("Invalid Analog pin number");
+    }
     meta->pinNumber[i] = pin;
-    
-   // Set ADC reference and low three bits of analog pin number.   
+
+    // Set ADC reference and low three bits of analog pin number.
     adcmux[i] = (pin & 7) | ADC_REF;
-    if (RECORD_EIGHT_BITS) adcmux[i] |= 1 << ADLAR;
-    
+    if (RECORD_EIGHT_BITS) {
+      adcmux[i] |= 1 << ADLAR;
+    }
+
     // If this is the first pin, trigger on timer/counter 1 compare match B.
     adcsrb[i] = i == 0 ? (1 << ADTS2) | (1 << ADTS0) : 0;
 #ifdef MUX5
-    if (pin > 7) adcsrb[i] |= (1 << MUX5);
+    if (pin > 7) {
+      adcsrb[i] |= (1 << MUX5);
+    }
 #endif  // MUX5
     adcsra[i] = (1 << ADEN) | (1 << ADIE) | adps;
     adcsra[i] |= i == 0 ? 1 << ADATE : 1 << ADSC;
@@ -359,10 +376,10 @@ void adcInit(metadata_t* meta) {
   ICR1 = ticks - 1;
   // compare for ADC start
   OCR1B = 0;
-  
+
   // multiply by prescaler
   ticks <<= tshift;
-  
+
   // Sample interval in CPU clock ticks.
   meta->sampleInterval = ticks;
   meta->cpuFrequency = F_CPU;
@@ -372,15 +389,15 @@ void adcInit(metadata_t* meta) {
     Serial.print(' ');
     Serial.print(meta->pinNumber[i], DEC);
   }
-  Serial.println(); 
+  Serial.println();
   Serial.print(F("ADC bits: "));
   Serial.println(meta->recordEightBits ? 8 : 10);
   Serial.print(F("ADC clock kHz: "));
   Serial.println(meta->adcFrequency/1000);
   Serial.print(F("Sample Rate: "));
-  Serial.println(sampleRate);  
+  Serial.println(sampleRate);
   Serial.print(F("Sample interval usec: "));
-  Serial.println(1000000.0/sampleRate, 4); 
+  Serial.println(1000000.0/sampleRate, 4);
 }
 //------------------------------------------------------------------------------
 // enable ADC and timer1 interrupts
@@ -392,7 +409,7 @@ void adcStart() {
 
   // Clear any pending interrupt.
   ADCSRA |= 1 << ADIF;
-  
+
   // Setup for first pin.
   ADMUX = adcmux[0];
   ADCSRB = adcsrb[0];
@@ -411,7 +428,7 @@ void adcStop() {
   ADCSRA = 0;
 }
 //------------------------------------------------------------------------------
-// Convert binary file to CSV file.
+// Convert binary file to csv file.
 void binaryToCsv() {
   uint8_t lastPct = 0;
   block_t buf;
@@ -419,19 +436,21 @@ void binaryToCsv() {
   uint32_t t0 = millis();
   char csvName[13];
   StdioStream csvStream;
-  
+
   if (!binFile.isOpen()) {
     Serial.println(F("No current binary file"));
     return;
   }
   binFile.rewind();
-  if (!binFile.read(&buf , 512) == 512) error("Read metadata failed");
-  // Create a new CSV file.
+  if (!binFile.read(&buf , 512) == 512) {
+    error("Read metadata failed");
+  }
+  // Create a new csv file.
   strcpy(csvName, binName);
-  strcpy_P(&csvName[BASE_NAME_SIZE + 3], PSTR("CSV"));
+  strcpy(&csvName[BASE_NAME_SIZE + 3], "csv");
 
   if (!csvStream.fopen(csvName, "w")) {
-    error("open csvStream failed");  
+    error("open csvStream failed");
   }
   Serial.println();
   Serial.print(F("Writing: "));
@@ -443,23 +462,29 @@ void binaryToCsv() {
   csvStream.print(intervalMicros, 4);
   csvStream.println(F(",usec"));
   for (uint8_t i = 0; i < pm->pinCount; i++) {
-    if (i) csvStream.putc(',');
+    if (i) {
+      csvStream.putc(',');
+    }
     csvStream.print(F("pin"));
     csvStream.print(pm->pinNumber[i]);
   }
-  csvStream.println(); 
+  csvStream.println();
   uint32_t tPct = millis();
   while (!Serial.available() && binFile.read(&buf, 512) == 512) {
     uint16_t i;
-    if (buf.count == 0) break;
+    if (buf.count == 0) {
+      break;
+    }
     if (buf.overrun) {
       csvStream.print(F("OVERRUN,"));
-      csvStream.println(buf.overrun);     
+      csvStream.println(buf.overrun);
     }
     for (uint16_t j = 0; j < buf.count; j += PIN_COUNT) {
       for (uint16_t i = 0; i < PIN_COUNT; i++) {
-        if (i) csvStream.putc(',');
-        csvStream.print(buf.data[i + j]);     
+        if (i) {
+          csvStream.putc(',');
+        }
+        csvStream.print(buf.data[i + j]);
       }
       csvStream.println();
     }
@@ -472,9 +497,11 @@ void binaryToCsv() {
         Serial.println('%');
       }
     }
-    if (Serial.available()) break;
+    if (Serial.available()) {
+      break;
+    }
   }
-  csvStream.fclose();  
+  csvStream.fclose();
   Serial.print(F("Done: "));
   Serial.print(0.001*(millis() - t0));
   Serial.println(F(" Seconds"));
@@ -486,7 +513,7 @@ void checkOverrun() {
   block_t buf;
   uint32_t bgnBlock, endBlock;
   uint32_t bn = 0;
-  
+
   if (!binFile.isOpen()) {
     Serial.println(F("No current binary file"));
     return;
@@ -502,7 +529,9 @@ void checkOverrun() {
   }
   bn++;
   while (binFile.read(&buf, 512) == 512) {
-    if (buf.count == 0) break;
+    if (buf.count == 0) {
+      break;
+    }
     if (buf.overrun) {
       if (!headerPrinted) {
         Serial.println();
@@ -540,7 +569,9 @@ void dumpData() {
   Serial.println(F("Type any character to stop"));
   delay(1000);
   while (!Serial.available() && binFile.read(&buf , 512) == 512) {
-    if (buf.count == 0) break;
+    if (buf.count == 0) {
+      break;
+    }
     if (buf.overrun) {
       Serial.print(F("OVERRUN,"));
       Serial.println(buf.overrun);
@@ -562,15 +593,15 @@ void dumpData() {
 uint32_t const ERASE_SIZE = 262144L;
 void logData() {
   uint32_t bgnBlock, endBlock;
-  
+
   // Allocate extra buffer space.
   block_t block[BUFFER_BLOCK_COUNT];
-  
+
   Serial.println();
-  
+
   // Initialize ADC and timer1.
   adcInit((metadata_t*) &block[0]);
-  
+
   // Find unused file name.
   if (BASE_NAME_SIZE > 6) {
     error("FILE_BASE_NAME too long");
@@ -597,7 +628,7 @@ void logData() {
   Serial.println(F("Creating new file"));
   binFile.close();
   if (!binFile.createContiguous(sd.vwd(),
-    TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) {
+                                TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) {
     error("createContiguous failed");
   }
   // Get the address of the file on the SD.
@@ -606,15 +637,19 @@ void logData() {
   }
   // Use SdFat's internal buffer.
   uint8_t* cache = (uint8_t*)sd.vol()->cacheClear();
-  if (cache == 0) error("cacheClear failed"); 
- 
+  if (cache == 0) {
+    error("cacheClear failed");
+  }
+
   // Flash erase all data in the file.
   Serial.println(F("Erasing all data"));
   uint32_t bgnErase = bgnBlock;
   uint32_t endErase;
   while (bgnErase < endBlock) {
     endErase = bgnErase + ERASE_SIZE;
-    if (endErase > endBlock) endErase = endBlock;
+    if (endErase > endBlock) {
+      endErase = endBlock;
+    }
     if (!sd.card()->erase(bgnErase, endErase)) {
       error("erase failed");
     }
@@ -627,15 +662,15 @@ void logData() {
   // Write metadata.
   if (!sd.card()->writeData((uint8_t*)&block[0])) {
     error("Write metadata failed");
-  } 
+  }
   // Initialize queues.
   emptyHead = emptyTail = 0;
   fullHead = fullTail = 0;
-  
+
   // Use SdFat buffer for one block.
   emptyQueue[emptyHead] = (block_t*)cache;
   emptyHead = queueNext(emptyHead);
-  
+
   // Put rest of buffers in the empty queue.
   for (uint8_t i = 0; i < BUFFER_BLOCK_COUNT; i++) {
     emptyQueue[emptyHead] = &block[i];
@@ -660,7 +695,7 @@ void logData() {
     if (fullHead != fullTail) {
       // Get address of block to write.
       block_t* pBlock = fullQueue[fullTail];
-      
+
       // Write block to SD.
       uint32_t usec = micros();
       if (!sd.card()->writeData((uint8_t*)pBlock)) {
@@ -668,10 +703,12 @@ void logData() {
       }
       usec = micros() - usec;
       t1 = millis();
-      if (usec > maxLatency) maxLatency = usec;
+      if (usec > maxLatency) {
+        maxLatency = usec;
+      }
       count += pBlock->count;
-      
-      // Add overruns and possibly light LED. 
+
+      // Add overruns and possibly light LED.
       if (pBlock->overrun) {
         overruns += pBlock->overrun;
         if (ERROR_LED_PIN >= 0) {
@@ -703,22 +740,24 @@ void logData() {
         fullHead = queueNext(fullHead);
         isrBuf = 0;
       }
-      if (fullHead == fullTail) break;
+      if (fullHead == fullTail) {
+        break;
+      }
     }
   }
   if (!sd.card()->writeStop()) {
     error("writeStop failed");
   }
   // Truncate file if recording stopped early.
-  if (bn != FILE_BLOCK_COUNT) {    
+  if (bn != FILE_BLOCK_COUNT) {
     Serial.println(F("Truncating file"));
     if (!binFile.truncate(512L * bn)) {
       error("Can't truncate file");
     }
   }
   if (!binFile.rename(sd.vwd(), binName)) {
-     error("Can't rename file");
-   }
+    error("Can't rename file");
+  }
   Serial.print(F("File renamed: "));
   Serial.println(binName);
   Serial.print(F("Max block write usec: "));
@@ -739,10 +778,10 @@ void setup(void) {
     pinMode(ERROR_LED_PIN, OUTPUT);
   }
   Serial.begin(9600);
-  
+
   // Read the first sample pin to init the ADC.
   analogRead(PIN_LIST[0]);
-  
+
   Serial.print(F("FreeRam: "));
   Serial.println(FreeRam());
 
@@ -758,8 +797,8 @@ void loop(void) {
   while (Serial.read() >= 0) {}
   Serial.println();
   Serial.println(F("type:"));
-  Serial.println(F("c - convert file to CSV")); 
-  Serial.println(F("d - dump data to Serial"));  
+  Serial.println(F("c - convert file to csv"));
+  Serial.println(F("d - dump data to Serial"));
   Serial.println(F("e - overrun error details"));
   Serial.println(F("r - record ADC data"));
 
@@ -772,12 +811,12 @@ void loop(void) {
   do {
     delay(10);
   } while (Serial.read() >= 0);
-  
+
   if (c == 'c') {
     binaryToCsv();
   } else if (c == 'd') {
     dumpData();
-  } else if (c == 'e') {    
+  } else if (c == 'e') {
     checkOverrun();
   } else if (c == 'r') {
     logData();
@@ -785,3 +824,6 @@ void loop(void) {
     Serial.println(F("Invalid entry"));
   }
 }
+#else  // __AVR__
+#error This program is only for AVR.
+#endif  // __AVR__

+ 94 - 0
SdFat/examples/LongFileName/LongFileName.ino

@@ -0,0 +1,94 @@
+// Example use of lfnOpenNext and open by index.
+// You can use test files located in
+// SdFat/examples/LongFileName/testFiles.
+#include<SPI.h>
+#include <SdFat.h>
+#include <SdFatUtil.h>
+
+// SD card chip select pin.
+const uint8_t SD_CS_PIN = SS;
+
+SdFat sd;
+SdFile file;
+SdFile dirFile;
+
+// Number of files found.
+uint16_t n = 0;
+
+// Max of ten files since files are selected with a single digit.
+const uint16_t nMax = 10;
+
+// Position of file's directory entry.
+uint16_t dirIndex[nMax];
+//------------------------------------------------------------------------------
+void setup() {
+  Serial.begin(9600);
+  while (!Serial) {}
+  delay(1000);
+
+  // Print the location of some test files.
+  Serial.println(F("\r\n"
+                   "You can use test files located in\r\n"
+                   "SdFat/examples/LongFileName/testFiles"));
+
+  if (!sd.begin(SD_CS_PIN)) {
+    sd.initErrorHalt();
+  }
+  Serial.print(F("Free RAM: "));
+  Serial.println(FreeRam());
+  Serial.println();
+
+  // List files in root directory.
+  if (!dirFile.open("/", O_READ)) {
+    sd.errorHalt("open root failed");
+  }
+  while (n < nMax && file.openNext(&dirFile, O_READ)) {
+
+    // Skip directories and hidden files.
+    if (!file.isSubDir() && !file.isHidden()) {
+
+      // Save dirIndex of file in directory.
+      dirIndex[n] = file.dirIndex();
+
+      // Print the file number and name.
+      Serial.print(n++);
+      Serial.write(' ');
+      file.printName(&Serial);
+      Serial.println();
+    }
+    file.close();
+  }
+}
+//------------------------------------------------------------------------------
+void loop() {
+  int c;
+
+  // Discard any Serial input.
+  while (Serial.read() > 0) {}
+  Serial.print(F("\r\nEnter File Number: "));
+
+  while ((c = Serial.read()) < 0) {};
+  if (!isdigit(c) || (c -= '0') >= n) {
+    Serial.println(F("Invald number"));
+    return;
+  }
+  Serial.println(c);
+  if (!file.open(&dirFile, dirIndex[c], O_READ)) {
+    sd.errorHalt(F("open"));
+  }
+  Serial.println();
+
+  char last;
+
+  // Copy up to 500 characters to Serial.
+  for (int i = 0; i < 500 && (c = file.read()) > 0; i++)  {
+    Serial.write(last = (char)c);
+  }
+  // Add new line if missing from last line.
+  if (last != '\n') {
+    Serial.println();
+  }
+  file.close();
+  Serial.flush();
+  delay(100);
+}

+ 4 - 0
SdFat/examples/LongFileName/testFiles/A long name can be 255 characters.txt

@@ -0,0 +1,4 @@
+This is "A long name can be 255 characters.txt"
+This file has a typical Long File Name.
+
+The maximum length of a Long File Name is 255 characters.

+ 1 - 0
SdFat/examples/LongFileName/testFiles/LFN,NAME.TXT

@@ -0,0 +1 @@
+LFN,NAME.TXT is not 8.3 since it has a comma.

+ 5 - 0
SdFat/examples/LongFileName/testFiles/MIXCASE.txt

@@ -0,0 +1,5 @@
+MIXCASE.txt does not have a Long File Name.
+
+Starting with NT, file names of this form,
+have the basename and extension character case
+encoded in two bits of the 8.3 directory entry.

+ 2 - 0
SdFat/examples/LongFileName/testFiles/Not_8_3.txt

@@ -0,0 +1,2 @@
+Not_8_3.txt has a Long File Name
+since the basename is mixed case.

+ 1 - 0
SdFat/examples/LongFileName/testFiles/OK%83.TXT

@@ -0,0 +1 @@
+OK%83.TXT is a valid 8.3 name.

+ 1 - 0
SdFat/examples/LongFileName/testFiles/STD_8_3.TXT

@@ -0,0 +1 @@
+STD_8_3.TXT - a vanilla 8.3 name.

+ 2 - 0
SdFat/examples/LongFileName/testFiles/With Blank.txt

@@ -0,0 +1,2 @@
+With Blank.txt
+Just another example of a Long File Name.

+ 2 - 0
SdFat/examples/LongFileName/testFiles/With.Two dots.txt

@@ -0,0 +1,2 @@
+"With Two.dots.txt" 
+Lots of reasons this is a Long File Name.

+ 5 - 0
SdFat/examples/LongFileName/testFiles/lower.txt

@@ -0,0 +1,5 @@
+lower.txt does not have a Long File Name.
+
+Starting with NT, file names of this form,
+have the basename and extension character case
+encoded in two bits of the 8.3 directory entry.

+ 5 - 0
SdFat/examples/LongFileName/testFiles/mixed.TXT

@@ -0,0 +1,5 @@
+mixed.TXT does not have a Long File Name.
+
+Starting with NT, file names of this form,
+have the basename and extension character case
+encoded in two bits of the 8.3 directory entry.

+ 76 - 50
SdFat/examples/LowLatencyLogger/LowLatencyLogger.ino

@@ -1,6 +1,6 @@
 /**
  * This program logs data to a binary file.  Functions are included
- * to convert the binary file to a CSV text file.
+ * to convert the binary file to a csv text file.
  *
  * Samples are logged at regular intervals.  The maximum logging rate
  * depends on the quality of your SD card and the time required to
@@ -13,6 +13,7 @@
  *
  * Data is written to the file using a SD multiple block write command.
  */
+#include <SPI.h>
 #include <SdFat.h>
 #include <SdFatUtil.h>
 //------------------------------------------------------------------------------
@@ -31,7 +32,7 @@ void acquireData(data_t* data) {
 void printData(Print* pr, data_t* data) {
   pr->print(data->time);
   for (int i = 0; i < ADC_DIM; i++) {
-    pr->write(',');  
+    pr->write(',');
     pr->print(data->adc[i]);
   }
   pr->println();
@@ -60,7 +61,7 @@ const uint8_t SD_CS_PIN = SS;
 // Digital pin to indicate an error, set to -1 if not used.
 // The led blinks for fatal errors. The led goes on solid for SD write
 // overrun errors and logging continues.
-const int8_t ERROR_LED_PIN = 3;
+const int8_t ERROR_LED_PIN = -1;
 //------------------------------------------------------------------------------
 // File definitions.
 //
@@ -71,11 +72,11 @@ const int8_t ERROR_LED_PIN = 3;
 const uint32_t FILE_BLOCK_COUNT = 256000;
 
 // log file base name.  Must be six characters or less.
-#define FILE_BASE_NAME "DATA"
+#define FILE_BASE_NAME "data"
 //------------------------------------------------------------------------------
 // Buffer definitions.
 //
-// The logger will use SdFat's buffer plus BUFFER_BLOCK_COUNT additional 
+// The logger will use SdFat's buffer plus BUFFER_BLOCK_COUNT additional
 // buffers.
 //
 #ifndef RAMEND
@@ -101,7 +102,7 @@ const uint8_t BUFFER_BLOCK_COUNT = 12;
 // End of configuration constants.
 //==============================================================================
 // Temporary log file.  Will be deleted if a reset or power failure occurs.
-#define TMP_FILE_NAME "TMP_LOG.BIN"
+#define TMP_FILE_NAME "tmp_log.bin"
 
 // Size of file base name.  Must not be larger than six.
 const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1;
@@ -110,7 +111,7 @@ SdFat sd;
 
 SdBaseFile binFile;
 
-char binName[13] = FILE_BASE_NAME "00.BIN";
+char binName[13] = FILE_BASE_NAME "00.bin";
 
 // Number of data records in a block.
 const uint16_t DATA_DIM = (512 - 4)/sizeof(data_t);
@@ -136,13 +137,15 @@ uint8_t fullHead;
 uint8_t fullTail;
 
 // Advance queue index.
-inline uint8_t queueNext(uint8_t ht) {return ht < (QUEUE_DIM - 1) ? ht + 1 : 0;}
+inline uint8_t queueNext(uint8_t ht) {
+  return ht < (QUEUE_DIM - 1) ? ht + 1 : 0;
+}
 //==============================================================================
 // Error messages stored in flash.
-#define error(msg) error_P(PSTR(msg))
+#define error(msg) errorFlash(F(msg))
 //------------------------------------------------------------------------------
-void error_P(const char* msg) {
-  sd.errorPrint_P(msg);
+void errorFlash(const __FlashStringHelper* msg) {
+  sd.errorPrint(msg);
   fatalBlink();
 }
 //------------------------------------------------------------------------------
@@ -158,7 +161,7 @@ void fatalBlink() {
   }
 }
 //==============================================================================
-// Convert binary file to CSV file.
+// Convert binary file to csv file.
 void binaryToCsv() {
   uint8_t lastPct = 0;
   block_t block;
@@ -166,7 +169,7 @@ void binaryToCsv() {
   uint32_t syncCluster = 0;
   SdFile csvFile;
   char csvName[13];
-  
+
   if (!binFile.isOpen()) {
     Serial.println();
     Serial.println(F("No current binary file"));
@@ -175,10 +178,10 @@ void binaryToCsv() {
   binFile.rewind();
   // Create a new csvFile.
   strcpy(csvName, binName);
-  strcpy_P(&csvName[BASE_NAME_SIZE + 3], PSTR("CSV"));
+  strcpy(&csvName[BASE_NAME_SIZE + 3], "csv");
 
   if (!csvFile.open(csvName, O_WRITE | O_CREAT | O_TRUNC)) {
-    error("open csvFile failed");  
+    error("open csvFile failed");
   }
   Serial.println();
   Serial.print(F("Writing: "));
@@ -188,7 +191,9 @@ void binaryToCsv() {
   uint32_t tPct = millis();
   while (!Serial.available() && binFile.read(&block, 512) == 512) {
     uint16_t i;
-    if (block.count == 0) break;
+    if (block.count == 0) {
+      break;
+    }
     if (block.overrun) {
       csvFile.print(F("OVERRUN,"));
       csvFile.println(block.overrun);
@@ -209,7 +214,9 @@ void binaryToCsv() {
         Serial.println('%');
       }
     }
-    if (Serial.available()) break;
+    if (Serial.available()) {
+      break;
+    }
   }
   csvFile.close();
   Serial.print(F("Done: "));
@@ -223,7 +230,7 @@ void checkOverrun() {
   block_t block;
   uint32_t bgnBlock, endBlock;
   uint32_t bn = 0;
-  
+
   if (!binFile.isOpen()) {
     Serial.println();
     Serial.println(F("No current binary file"));
@@ -236,7 +243,9 @@ void checkOverrun() {
   Serial.println();
   Serial.println(F("Checking overrun errors - type any character to stop"));
   while (binFile.read(&block, 512) == 512) {
-    if (block.count == 0) break;
+    if (block.count == 0) {
+      break;
+    }
     if (block.overrun) {
       if (!headerPrinted) {
         Serial.println();
@@ -273,7 +282,9 @@ void dumpData() {
   delay(1000);
   printHeader(&Serial);
   while (!Serial.available() && binFile.read(&block , 512) == 512) {
-    if (block.count == 0) break;
+    if (block.count == 0) {
+      break;
+    }
     if (block.overrun) {
       Serial.print(F("OVERRUN,"));
       Serial.println(block.overrun);
@@ -290,12 +301,12 @@ void dumpData() {
 uint32_t const ERASE_SIZE = 262144L;
 void logData() {
   uint32_t bgnBlock, endBlock;
-  
+
   // Allocate extra buffer space.
   block_t block[BUFFER_BLOCK_COUNT];
   block_t* curBlock = 0;
   Serial.println();
-  
+
   // Find unused file name.
   if (BASE_NAME_SIZE > 6) {
     error("FILE_BASE_NAME too long");
@@ -322,7 +333,7 @@ void logData() {
   Serial.println(F("Creating new file"));
   binFile.close();
   if (!binFile.createContiguous(sd.vwd(),
-    TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) {
+                                TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) {
     error("createContiguous failed");
   }
   // Get the address of the file on the SD.
@@ -331,15 +342,19 @@ void logData() {
   }
   // Use SdFat's internal buffer.
   uint8_t* cache = (uint8_t*)sd.vol()->cacheClear();
-  if (cache == 0) error("cacheClear failed"); 
- 
+  if (cache == 0) {
+    error("cacheClear failed");
+  }
+
   // Flash erase all data in the file.
   Serial.println(F("Erasing all data"));
   uint32_t bgnErase = bgnBlock;
   uint32_t endErase;
   while (bgnErase < endBlock) {
     endErase = bgnErase + ERASE_SIZE;
-    if (endErase > endBlock) endErase = endBlock;
+    if (endErase > endBlock) {
+      endErase = endBlock;
+    }
     if (!sd.card()->erase(bgnErase, endErase)) {
       error("erase failed");
     }
@@ -352,11 +367,11 @@ void logData() {
   // Initialize queues.
   emptyHead = emptyTail = 0;
   fullHead = fullTail = 0;
-  
+
   // Use SdFat buffer for one block.
   emptyQueue[emptyHead] = (block_t*)cache;
   emptyHead = queueNext(emptyHead);
-  
+
   // Put rest of buffers in the empty queue.
   for (uint8_t i = 0; i < BUFFER_BLOCK_COUNT; i++) {
     emptyQueue[emptyHead] = &block[i];
@@ -381,15 +396,17 @@ void logData() {
   while (1) {
     // Time for next data record.
     logTime += LOG_INTERVAL_USEC;
-    if (Serial.available()) closeFile = true;
-    
+    if (Serial.available()) {
+      closeFile = true;
+    }
+
     if (closeFile) {
-       if (curBlock != 0 && curBlock->count >= 0) {
+      if (curBlock != 0 && curBlock->count >= 0) {
         // Put buffer in full queue.
         fullQueue[fullHead] = curBlock;
         fullHead = queueNext(fullHead);
         curBlock = 0;
-      }   
+      }
     } else {
       if (curBlock == 0 && emptyTail != emptyHead) {
         curBlock = emptyQueue[emptyTail];
@@ -401,26 +418,30 @@ void logData() {
       do {
         diff = logTime - micros();
       } while(diff > 0);
-      if (diff < -10) error("LOG_INTERVAL_USEC too small");
+      if (diff < -10) {
+        error("LOG_INTERVAL_USEC too small");
+      }
       if (curBlock == 0) {
         overrun++;
       } else {
         acquireData(&curBlock->data[curBlock->count++]);
         if (curBlock->count == DATA_DIM) {
           fullQueue[fullHead] = curBlock;
-          fullHead = queueNext(fullHead);        
+          fullHead = queueNext(fullHead);
           curBlock = 0;
         }
       }
     }
-    
+
     if (fullHead == fullTail) {
       // Exit loop if done.
-      if (closeFile) break;
+      if (closeFile) {
+        break;
+      }
     } else if (!sd.card()->isBusy()) {
       // Get address of block to write.
       block_t* pBlock = fullQueue[fullTail];
-      fullTail = queueNext(fullTail);     
+      fullTail = queueNext(fullTail);
       // Write block to SD.
       uint32_t usec = micros();
       if (!sd.card()->writeData((uint8_t*)pBlock)) {
@@ -428,10 +449,12 @@ void logData() {
       }
       usec = micros() - usec;
       t1 = millis();
-      if (usec > maxLatency) maxLatency = usec;
+      if (usec > maxLatency) {
+        maxLatency = usec;
+      }
       count += pBlock->count;
-      
-      // Add overruns and possibly light LED. 
+
+      // Add overruns and possibly light LED.
       if (pBlock->overrun) {
         overrunTotal += pBlock->overrun;
         if (ERROR_LED_PIN >= 0) {
@@ -452,15 +475,15 @@ void logData() {
     error("writeStop failed");
   }
   // Truncate file if recording stopped early.
-  if (bn != FILE_BLOCK_COUNT) {    
+  if (bn != FILE_BLOCK_COUNT) {
     Serial.println(F("Truncating file"));
     if (!binFile.truncate(512L * bn)) {
       error("Can't truncate file");
     }
   }
   if (!binFile.rename(sd.vwd(), binName)) {
-     error("Can't rename file");
-   }
+    error("Can't rename file");
+  }
   Serial.print(F("File renamed: "));
   Serial.println(binName);
   Serial.print(F("Max block write usec: "));
@@ -481,12 +504,15 @@ void setup(void) {
     pinMode(ERROR_LED_PIN, OUTPUT);
   }
   Serial.begin(9600);
-  
+  while (!Serial) {}
+
   Serial.print(F("FreeRam: "));
   Serial.println(FreeRam());
   Serial.print(F("Records/block: "));
   Serial.println(DATA_DIM);
-  if (sizeof(block_t) != 512) error("Invalid block size");
+  if (sizeof(block_t) != 512) {
+    error("Invalid block size");
+  }
   // initialize file system.
   if (!sd.begin(SD_CS_PIN, SPI_FULL_SPEED)) {
     sd.initErrorPrint();
@@ -499,19 +525,19 @@ void loop(void) {
   while (Serial.read() >= 0) {}
   Serial.println();
   Serial.println(F("type:"));
-  Serial.println(F("c - convert file to CSV")); 
-  Serial.println(F("d - dump data to Serial"));  
+  Serial.println(F("c - convert file to csv"));
+  Serial.println(F("d - dump data to Serial"));
   Serial.println(F("e - overrun error details"));
   Serial.println(F("r - record data"));
 
   while(!Serial.available()) {}
   char c = tolower(Serial.read());
-  
+
   // Discard extra Serial data.
   do {
     delay(10);
   } while (Serial.read() >= 0);
-  
+
   if (ERROR_LED_PIN >= 0) {
     digitalWrite(ERROR_LED_PIN, LOW);
   }
@@ -519,7 +545,7 @@ void loop(void) {
     binaryToCsv();
   } else if (c == 'd') {
     dumpData();
-  } else if (c == 'e') {    
+  } else if (c == 'e') {
     checkOverrun();
   } else if (c == 'r') {
     logData();

+ 7 - 3
SdFat/examples/OpenNext/OpenNext.ino

@@ -1,6 +1,7 @@
 /*
  * Print size, modify date/time, and name for all files in root.
  */
+#include <SPI.h>
 #include <SdFat.h>
 
 // SD chip select pin
@@ -15,17 +16,20 @@ void setup() {
   Serial.begin(9600);
   while (!Serial) {} // wait for Leonardo
   delay(1000);
-  
+  Serial.println();
+
   // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
   // breadboards.  use SPI_FULL_SPEED for better performance.
-  if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt();
+  if (!sd.begin(chipSelect, SPI_HALF_SPEED)) {
+    sd.initErrorHalt();
+  }
 
   // open next file in root.  The volume working directory, vwd, is root
   while (file.openNext(sd.vwd(), O_READ)) {
     file.printFileSize(&Serial);
     Serial.write(' ');
     file.printModifyDateTime(&Serial);
-    Serial.write(' ');    
+    Serial.write(' ');
     file.printName(&Serial);
     if (file.isDir()) {
       // Indicate a directory.

+ 43 - 36
SdFat/examples/PrintBenchmark/PrintBenchmark.ino

@@ -1,6 +1,7 @@
 /*
- * This sketch is a simple Print benchmark.
+ * This program is a simple Print benchmark.
  */
+#include <SPI.h>
 #include <SdFat.h>
 #include <SdFatUtil.h>
 
@@ -20,7 +21,7 @@ SdFile file;
 ArduinoOutStream cout(Serial);
 //------------------------------------------------------------------------------
 // store error strings in flash to save RAM
-#define error(s) sd.errorHalt_P(PSTR(s))
+#define error(s) sd.errorHalt(F(s))
 //------------------------------------------------------------------------------
 void setup() {
   Serial.begin(9600);
@@ -37,24 +38,26 @@ void loop() {
   while (Serial.read() >= 0) {
   }
   // pstr stores strings in flash to save RAM
-  cout << pstr("Type any character to start\n");
+  cout << F("Type any character to start\n");
   while (Serial.read() <= 0) {
   }
   delay(400);  // catch Due reset problem
 
-  cout << pstr("Free RAM: ") << FreeRam() << endl;
+  cout << F("Free RAM: ") << FreeRam() << endl;
 
   // initialize the SD card at SPI_FULL_SPEED for best performance.
   // try SPI_HALF_SPEED if bus errors occur.
-  if (!sd.begin(chipSelect, SPI_FULL_SPEED)) sd.initErrorHalt();
+  if (!sd.begin(chipSelect, SPI_FULL_SPEED)) {
+    sd.initErrorHalt();
+  }
 
-  cout << pstr("Type is FAT") << int(sd.vol()->fatType()) << endl;
+  cout << F("Type is FAT") << int(sd.vol()->fatType()) << endl;
 
-  cout << pstr("Starting print test.  Please wait.\n\n");
+  cout << F("Starting print test.  Please wait.\n\n");
 
   // do write test
   for (int test = 0; test < 6; test++) {
-    char fileName[13] = "BENCH0.TXT";
+    char fileName[13] = "bench0.txt";
     fileName[5] = '0' + test;
     // open or create file - truncate existing file.
     if (!file.open(fileName, O_CREAT | O_TRUNC | O_RDWR)) {
@@ -65,29 +68,29 @@ void loop() {
     totalLatency = 0;
     switch(test) {
     case 0:
-      cout << pstr("Test of println(uint16_t)\n");
+      cout << F("Test of println(uint16_t)\n");
       break;
 
     case 1:
-      cout << pstr("Test of printField(uint16_t, char)\n");
+      cout << F("Test of printField(uint16_t, char)\n");
       break;
 
     case 2:
-      cout << pstr("Test of println(uint32_t)\n");
+      cout << F("Test of println(uint32_t)\n");
       break;
-      
+
     case 3:
-      cout << pstr("Test of printField(uint32_t, char)\n");
-      break;      
+      cout << F("Test of printField(uint32_t, char)\n");
+      break;
     case 4:
-      cout << pstr("Test of println(float)\n");
-      break;     
-    
+      cout << F("Test of println(float)\n");
+      break;
+
     case 5:
-      cout << pstr("Test of printField(float, char)\n");
-      break;         
+      cout << F("Test of printField(float, char)\n");
+      break;
     }
-    
+
     uint32_t t = millis();
     for (uint16_t i = 0; i < N_PRINT; i++) {
       uint32_t m = micros();
@@ -103,38 +106,42 @@ void loop() {
 
       case 2:
         file.println(12345678UL + i);
-        break;     
-      
+        break;
+
       case 3:
         file.printField(12345678UL + i, '\n');
-        break;        
-        
+        break;
+
       case 4:
         file.println((float)0.01*i);
         break;
-      
+
       case 5:
         file.printField((float)0.01*i, '\n');
         break;
       }
-      if (file.writeError) {
+      if (file.getWriteError()) {
         error("write failed");
       }
       m = micros() - m;
-      if (maxLatency < m) maxLatency = m;
-      if (minLatency > m) minLatency = m;
+      if (maxLatency < m) {
+        maxLatency = m;
+      }
+      if (minLatency > m) {
+        minLatency = m;
+      }
       totalLatency += m;
     }
     file.close();
     t = millis() - t;
     double s = file.fileSize();
-    cout << pstr("Time ") << 0.001*t << pstr(" sec\n");
-    cout << pstr("File size ") << 0.001*s << pstr(" KB\n");
-    cout << pstr("Write ") << s/t << pstr(" KB/sec\n");
-    cout << pstr("Maximum latency: ") << maxLatency;
-    cout << pstr(" usec, Minimum Latency: ") << minLatency;
-    cout << pstr(" usec, Avg Latency: ");
-    cout << totalLatency/N_PRINT << pstr(" usec\n\n");
+    cout << F("Time ") << 0.001*t << F(" sec\n");
+    cout << F("File size ") << 0.001*s << F(" KB\n");
+    cout << F("Write ") << s/t << F(" KB/sec\n");
+    cout << F("Maximum latency: ") << maxLatency;
+    cout << F(" usec, Minimum Latency: ") << minLatency;
+    cout << F(" usec, Avg Latency: ");
+    cout << totalLatency/N_PRINT << F(" usec\n\n");
   }
-  cout << pstr("Done!\n\n");
+  cout << F("Done!\n\n");
 }

+ 80 - 79
SdFat/examples/QuickStart/QuickStart.ino

@@ -1,5 +1,6 @@
 // Quick hardware test.
 //
+#include <SPI.h>
 #include <SdFat.h>
 //
 // Set DISABLE_CHIP_SELECT to disable a second SPI device.
@@ -12,12 +13,8 @@ const int8_t DISABLE_CHIP_SELECT = -1;
 // Use SPI_QUARTER_SPEED for even slower SPI bus speed
 const uint8_t spiSpeed = SPI_HALF_SPEED;
 //------------------------------------------------------------------------------
-// Normally the SdFat class is used in applications in place
-// of Sd2Card, SdVolume, and SdFile for root.
-// These internal classes are used here to diagnose problems.
-Sd2Card card;
-SdVolume volume;
-SdFile root;
+// File system object.
+SdFat sd;
 
 // Serial streams
 ArduinoOutStream cout(Serial);
@@ -30,39 +27,39 @@ ArduinoInStream cin(Serial, cinBuf, sizeof(cinBuf));
 int chipSelect;
 
 void cardOrSpeed() {
-  cout << pstr("Try another SD card or reduce the SPI bus speed.\n");
-  cout << pstr("Edit spiSpeed in this sketch to change it.\n");
+  cout << F("Try another SD card or reduce the SPI bus speed.\n");
+  cout << F("Edit spiSpeed in this program to change it.\n");
 }
 
 void reformatMsg() {
-  cout << pstr("Try reformatting the card.  For best results use\n");
-  cout << pstr("the SdFormatter sketch in SdFat/examples or download\n");
-  cout << pstr("and use SDFormatter from www.sdcard.org/downloads.\n");
+  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);
   while (!Serial) {}  // Wait for Leonardo.
-  delay(1000);        // Delay for Due.
-  
-  cout << pstr("\nSPI pins:\n");
-  cout << pstr("MOSI: ") << int(MOSI) << endl;  
-  cout << pstr("MISO: ") << int(MISO) << endl;
-  cout << pstr("SCK:  ") << int(SCK) << endl;
-  
+
+  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 << pstr(
-      "\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(
+           "\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 << pstr(
-    "\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");
+  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;
@@ -70,87 +67,91 @@ void loop() {
   // read any existing Serial data
   while (Serial.read() >= 0) {}
 
-  if (!firstTry) cout << pstr("\nRestarting\n");
+  if (!firstTry) {
+    cout << F("\nRestarting\n");
+  }
   firstTry = false;
 
-  cout << pstr("\nEnter the chip select pin number: ");
+  cout << F("\nEnter the chip select pin number: ");
+  while (!Serial.available()) {}
+  delay(400);  // catch Due restart problem
+
   cin.readline();
   if (cin >> chipSelect) {
     cout << chipSelect << endl;
   } else {
-    cout << pstr("\nInvalid pin number\n");
+    cout << F("\nInvalid pin number\n");
     return;
   }
   if (DISABLE_CHIP_SELECT < 0) {
-    cout << pstr(
-      "\nAssuming the SD is the only SPI device.\n"
-      "Edit DISABLE_CHIP_SELECT to disable another device.\n");
+    cout << F(
+           "\nAssuming the SD is the only SPI device.\n"
+           "Edit DISABLE_CHIP_SELECT to disable another device.\n");
   } else {
-    cout << pstr("\nDisabling SPI device on pin ");
+    cout << F("\nDisabling SPI device on pin ");
     cout << int(DISABLE_CHIP_SELECT) << endl;
     pinMode(DISABLE_CHIP_SELECT, OUTPUT);
     digitalWrite(DISABLE_CHIP_SELECT, HIGH);
   }
-  if (!card.init(spiSpeed, chipSelect)) {
-    cout << pstr(
-      "\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 << pstr("errorCode: ") << hex << showbase << int(card.errorCode());
-    cout << pstr(", errorData: ") << int(card.errorData());
-    cout << dec << noshowbase << endl;
+  if (!sd.begin(chipSelect, spiSpeed)) {
+    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;
+    }
+    cout << F("\nCard successfully initialized.\n");
+    if (sd.vol()->fatType() == 0) {
+      cout << F("Can't find a valid FAT16/FAT32 partition.\n");
+      reformatMsg();
+      return;
+    }
+    if (!sd.vwd()->isOpen()) {
+      cout << F("Can't open root directory.\n");
+      reformatMsg();
+      return;
+    }
+    cout << F("Can't determine error type\n");
     return;
   }
-  cout << pstr("\nCard successfully initialized.\n");
+  cout << F("\nCard successfully initialized.\n");
   cout << endl;
 
-  uint32_t size = card.cardSize();
+  uint32_t size = sd.card()->cardSize();
   if (size == 0) {
-    cout << pstr("Can't determine the card size.\n");
+    cout << F("Can't determine the card size.\n");
     cardOrSpeed();
     return;
   }
   uint32_t sizeMB = 0.000512 * size + 0.5;
-  cout << pstr("Card size: ") << sizeMB;
-  cout << pstr(" MB (MB = 1,000,000 bytes)\n");
+  cout << F("Card size: ") << sizeMB;
+  cout << F(" MB (MB = 1,000,000 bytes)\n");
   cout << endl;
-
-  if (!volume.init(&card)) {
-    if (card.errorCode()) {
-      cout << pstr("Can't read the card.\n");
-      cardOrSpeed();
-    } else {
-      cout << pstr("Can't find a valid FAT16/FAT32 partition.\n");
-      reformatMsg();
-    }
-    return;
-  }
-  cout << pstr("Volume is FAT") << int(volume.fatType());
-  cout << pstr(", Cluster size (bytes): ") << 512L * volume.blocksPerCluster();
+  cout << F("Volume is FAT") << int(sd.vol()->fatType());
+  cout << F(", Cluster size (bytes): ") << 512L * sd.vol()->blocksPerCluster();
   cout << endl << endl;
 
-  root.close();
-  if (!root.openRoot(&volume)) {
-    cout << pstr("Can't open root directory.\n");
-    reformatMsg();
-    return;
-  }
-  cout << pstr("Files found (name date time size):\n");
-  root.ls(LS_R | LS_DATE | LS_SIZE);
+  cout << F("Files found (date time size name):\n");
+  sd.ls(LS_R | LS_DATE | LS_SIZE);
 
-  if ((sizeMB > 1100 && volume.blocksPerCluster() < 64)
-    || (sizeMB < 2200 && volume.fatType() == 32)) {
-    cout << pstr("\nThis card should be reformatted for best performance.\n");
-    cout << pstr("Use a cluster size of 32 KB for cards larger than 1 GB.\n");
-    cout << pstr("Only cards larger than 2 GB should be formatted FAT32.\n");
+  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 existing Serial data
   while (Serial.read() >= 0) {}
-  cout << pstr("\nSuccess!  Type any character to restart.\n");
+  cout << F("\nSuccess!  Type any character to restart.\n");
   while (Serial.read() < 0) {}
 }

+ 33 - 34
SdFat/examples/RawWrite/RawWrite.ino

@@ -1,26 +1,20 @@
 /*
- * This sketch illustrates raw write functions in SdFat that
- * can be used for high speed data logging.  These functions
- * are used in the WaveRP library to record audio with the
- * Adafruit Wave Shield using the built-in Arduino ADC.
+ * This program illustrates raw write functions in SdFat that
+ * can be used for high speed data logging.
  *
- * The WaveRP library captures data from the ADC in an ISR
- * that is driven driven by timer one.  Data is collected in
- * two 512 byte buffers and written to the SD card.
- *
- * This sketch simulates logging from a source that produces
+ * This program simulates logging from a source that produces
  * data at a constant rate of one block every MICROS_PER_BLOCK.
  *
- * If a high quality SanDisk card is used with this sketch
+ * If a high quality SanDisk card is used with this program
  * no overruns occur and the maximum block write time is
  * under 2000 micros.
  *
- * Note: WaveRP creates a very large file then truncates it
- * to the length that is used for a recording. It only takes
+ * Note: Apps should create a very large file then truncates it
+ * to the length that is used for a logging. It only takes
  * a few seconds to erase a 500 MB file since the card only
  * marks the blocks as erased; no data transfer is required.
  */
-
+#include <SPI.h>
 #include <SdFat.h>
 #include <SdFatUtil.h>
 
@@ -46,7 +40,7 @@ uint32_t bgnBlock, endBlock;
 ArduinoOutStream cout(Serial);
 //------------------------------------------------------------------------------
 // store error strings in flash to save RAM
-#define error(s) sd.errorHalt_P(PSTR(s))
+#define error(s) sd.errorHalt(F(s))
 //------------------------------------------------------------------------------
 // log of first overruns
 #define OVER_DIM 20
@@ -63,21 +57,23 @@ void setup(void) {
 void loop(void) {
   while (Serial.read() >= 0) {}
   // pstr stores strings in flash to save RAM
-  cout << pstr("Type any character to start\n");
+  cout << F("Type any character to start\n");
   while (Serial.read() <= 0) {}
   delay(400);  // catch Due reset problem
 
-  cout << pstr("Free RAM: ") << FreeRam() << endl;
+  cout << F("Free RAM: ") << FreeRam() << endl;
 
   // initialize the SD card at SPI_FULL_SPEED for best performance.
   // try SPI_HALF_SPEED if bus errors occur.
-  if (!sd.begin(chipSelect, SPI_FULL_SPEED)) sd.initErrorHalt();
+  if (!sd.begin(chipSelect, SPI_FULL_SPEED)) {
+    sd.initErrorHalt();
+  }
 
   // delete possible existing file
-  sd.remove("RAW.TXT");
+  sd.remove("RawWrite.txt");
 
   // create a contiguous file
-  if (!file.createContiguous(sd.vwd(), "RAW.TXT", 512UL*BLOCK_COUNT)) {
+  if (!file.createContiguous(sd.vwd(), "RawWrite.txt", 512UL*BLOCK_COUNT)) {
     error("createContiguous failed");
   }
   // get the location of the file's blocks
@@ -100,10 +96,10 @@ void loop(void) {
     pCache[i + 63] = '\n';
   }
 
-  cout << pstr("Start raw write of ") << file.fileSize() << pstr(" bytes at\n");
-  cout << 512000000UL/MICROS_PER_BLOCK << pstr(" bytes per second\n");
-  cout << pstr("Please wait ") << (BLOCK_COUNT*MICROS_PER_BLOCK)/1000000UL;
-  cout << pstr(" seconds\n");
+  cout << F("Start raw write of ") << file.fileSize() << F(" bytes at\n");
+  cout << 512000000UL/MICROS_PER_BLOCK << F(" bytes per second\n");
+  cout << F("Please wait ") << (BLOCK_COUNT*MICROS_PER_BLOCK)/1000000UL;
+  cout << F(" seconds\n");
 
   // tell card to setup for multiple block write with pre-erase
   if (!sd.card()->writeStart(bgnBlock, BLOCK_COUNT)) {
@@ -122,13 +118,15 @@ void loop(void) {
 
     // put block number at start of first line in block
     uint32_t n = b;
-    for (int8_t d = 5; d >= 0; d--){
+    for (int8_t d = 5; d >= 0; d--) {
       pCache[d] = n || d == 5 ? n % 10 + '0' : ' ';
       n /= 10;
     }
     // write a 512 byte block
     uint32_t tw = micros();
-    if (!sd.card()->writeData(pCache)) error("writeData failed");
+    if (!sd.card()->writeData(pCache)) {
+      error("writeData failed");
+    }
     tw = micros() - tw;
 
     // check for max write time
@@ -144,8 +142,7 @@ void loop(void) {
       overruns++;
       // advance time to reflect overrun
       tNext = micros();
-    }
-    else {
+    } else {
       // wait for time to write next block
       while(micros() < tNext);
     }
@@ -154,16 +151,18 @@ void loop(void) {
   t = micros() - t;
 
   // end multiple block write mode
-  if (!sd.card()->writeStop()) error("writeStop failed");
+  if (!sd.card()->writeStop()) {
+    error("writeStop failed");
+  }
 
-  cout << pstr("Done\n");
-  cout << pstr("Elapsed time: ") << setprecision(3)<< 1.e-6*t;
-  cout << pstr(" seconds\n");
-  cout << pstr("Max write time: ") << maxWriteTime << pstr(" micros\n");
-  cout << pstr("Overruns: ") << overruns << endl;
+  cout << F("Done\n");
+  cout << F("Elapsed time: ") << setprecision(3)<< 1.e-6*t;
+  cout << F(" seconds\n");
+  cout << F("Max write time: ") << maxWriteTime << F(" micros\n");
+  cout << F("Overruns: ") << overruns << endl;
   if (overruns) {
     uint8_t n = overruns > OVER_DIM ? OVER_DIM : overruns;
-    cout << pstr("fileBlock,micros") << endl;
+    cout << F("fileBlock,micros") << endl;
     for (uint8_t i = 0; i < n; i++) {
       cout << over[i].block << ',' << over[i].micros << endl;
     }

+ 88 - 0
SdFat/examples/ReadWrite/ReadWrite.ino

@@ -0,0 +1,88 @@
+/*
+  SD card read/write
+
+ This example shows how to read and write data to and from an SD card file
+ The circuit:
+ * SD card attached to SPI bus as follows:
+ ** MOSI - pin 11
+ ** MISO - pin 12
+ ** CLK - pin 13
+ ** CS - pin 4
+
+ created   Nov 2010
+ by David A. Mellis
+ modified 9 Apr 2012
+ by Tom Igoe
+
+ This example code is in the public domain.
+
+ */
+#define SD_CS_PIN SS
+#include <SPI.h>
+//#include <SD.h>
+#include <SdFat.h>
+SdFat SD;
+
+File myFile;
+
+void setup()
+{
+  // Open serial communications and wait for port to open:
+  Serial.begin(9600);
+  while (!Serial) {
+    ; // wait for serial port to connect. Needed for Leonardo only
+  }
+
+
+  Serial.print("Initializing SD card...");
+  // On the Ethernet Shield, CS is pin 4. It's set as an output by default.
+  // Note that even if it's not used as the CS pin, the hardware SS pin
+  // (10 on most Arduino boards, 53 on the Mega) must be left as an output
+  // or the SD library functions will not work.
+  pinMode(10, OUTPUT);
+
+  if (!SD.begin(SD_CS_PIN)) {
+    Serial.println("initialization failed!");
+    return;
+  }
+  Serial.println("initialization done.");
+
+  // open the file. note that only one file can be open at a time,
+  // so you have to close this one before opening another.
+  myFile = SD.open("test.txt", FILE_WRITE);
+
+  // if the file opened okay, write to it:
+  if (myFile) {
+    Serial.print("Writing to test.txt...");
+    myFile.println("testing 1, 2, 3.");
+    // close the file:
+    myFile.close();
+    Serial.println("done.");
+  } else {
+    // if the file didn't open, print an error:
+    Serial.println("error opening test.txt");
+  }
+
+  // re-open the file for reading:
+  myFile = SD.open("test.txt");
+  if (myFile) {
+    Serial.println("test.txt:");
+
+    // read from the file until there's nothing else in it:
+    while (myFile.available()) {
+      Serial.write(myFile.read());
+    }
+    // close the file:
+    myFile.close();
+  } else {
+    // if the file didn't open, print an error:
+    Serial.println("error opening test.txt");
+  }
+}
+
+void loop()
+{
+  // nothing happens after setup
+}
+
+

+ 12 - 7
SdFat/examples/ReadWriteSdFat/ReadWriteSdFat.ino

@@ -3,23 +3,24 @@
 const int chipSelect = 4;
 /*
  SD card read/write
-  
- This example shows how to read and write data to and from an SD card file 	
+
+ This example shows how to read and write data to and from an SD card file
  The circuit:
  * SD card attached to SPI bus as follows:
  ** MOSI - pin 11
  ** MISO - pin 12
  ** CLK - pin 13
  ** CS - pin 4
- 
+
  created   Nov 2010
  by David A. Mellis
  updated 2 Dec 2010
  by Tom Igoe
  modified by Bill Greiman 11 Apr 2011
  This example code is in the public domain.
- 	 
+
  */
+#include <SPI.h>
 #include <SdFat.h>
 SdFat sd;
 SdFile myFile;
@@ -30,11 +31,13 @@ void setup() {
   Serial.println("Type any character to start");
   while (Serial.read() <= 0) {}
   delay(400);  // catch Due reset problem
-  
+
   // Initialize SdFat or print a detailed error message and halt
   // Use half speed like the native library.
   // change to SPI_FULL_SPEED for more performance.
-  if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt();
+  if (!sd.begin(chipSelect, SPI_HALF_SPEED)) {
+    sd.initErrorHalt();
+  }
 
   // open the file for write at end like the Native SD library
   if (!myFile.open("test.txt", O_RDWR | O_CREAT | O_AT_END)) {
@@ -56,7 +59,9 @@ void setup() {
 
   // read from the file until there's nothing else in it:
   int data;
-  while ((data = myFile.read()) >= 0) Serial.write(data);
+  while ((data = myFile.read()) >= 0) {
+    Serial.write(data);
+  }
   // close the file:
   myFile.close();
 }

+ 103 - 78
SdFat/examples/SdFormatter/SdFormatter.ino

@@ -1,18 +1,19 @@
 /*
- * This sketch will format an SD or SDHC card.
+ * This program will format an SD or SDHC card.
  * Warning all data will be deleted!
  *
  * For SD/SDHC cards larger than 64 MB this
- * sketch attempts to match the format
+ * program attempts to match the format
  * generated by SDFormatter available here:
  *
  * http://www.sdcard.org/consumers/formatter/
  *
- * For smaller cards this sketch uses FAT16
+ * For smaller cards this program uses FAT16
  * and SDFormatter uses FAT12.
  */
 // Print extra info for debug if DEBUG_PRINT is nonzero
 #define DEBUG_PRINT 0
+#include <SPI.h>
 #include <SdFat.h>
 #if DEBUG_PRINT
 #include <SdFatUtil.h>
@@ -64,13 +65,13 @@ char noName[] = "NO NAME    ";
 char fat16str[] = "FAT16   ";
 char fat32str[] = "FAT32   ";
 //------------------------------------------------------------------------------
-#define sdError(msg) sdError_P(PSTR(msg))
+#define sdError(msg) sdError_F(F(msg))
 
-void sdError_P(const char* str) {
-  cout << pstr("error: ");
-  cout << pgm(str) << endl;
+void sdError_F(const __FlashStringHelper* str) {
+  cout << F("error: ");
+  cout << str << endl;
   if (card.errorCode()) {
-    cout << pstr("SD error: ") << hex << int(card.errorCode());
+    cout << F("SD error: ") << hex << int(card.errorCode());
     cout << ',' << int(card.errorData()) << dec << endl;
   }
   while (1);
@@ -78,19 +79,19 @@ void sdError_P(const char* str) {
 //------------------------------------------------------------------------------
 #if DEBUG_PRINT
 void debugPrint() {
-  cout << pstr("FreeRam: ") << FreeRam() << endl;
-  cout << pstr("partStart: ") << relSector << endl;
-  cout << pstr("partSize: ") << partSize << endl;
-  cout << pstr("reserved: ") << reservedSectors << endl;
-  cout << pstr("fatStart: ") << fatStart << endl;
-  cout << pstr("fatSize: ") << fatSize << endl;
-  cout << pstr("dataStart: ") << dataStart << endl;
-  cout << pstr("clusterCount: ");
+  cout << F("FreeRam: ") << FreeRam() << 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 << pstr("Heads: ") << int(numberOfHeads) << endl;
-  cout << pstr("Sectors: ") << int(sectorsPerTrack) << endl;
-  cout << pstr("Cylinders: ");
+  cout << F("Heads: ") << int(numberOfHeads) << endl;
+  cout << F("Sectors: ") << int(sectorsPerTrack) << endl;
+  cout << F("Cylinders: ");
   cout << cardSizeBlocks/(numberOfHeads*sectorsPerTrack) << endl;
 }
 #endif  // DEBUG_PRINT
@@ -121,7 +122,7 @@ void initSizes() {
     sectorsPerCluster = 128;
   }
 
-  cout << pstr("Blocks/Cluster: ") << int(sectorsPerCluster) << endl;
+  cout << F("Blocks/Cluster: ") << int(sectorsPerCluster) << endl;
   // set fake disk geometry
   sectorsPerTrack = cardCapacityMB <= 256 ? 32 : 63;
 
@@ -160,7 +161,9 @@ void clearFatDir(uint32_t bgn, uint32_t count) {
     sdError("Clear FAT/DIR writeStart failed");
   }
   for (uint32_t i = 0; i < count; i++) {
-    if ((i & 0XFF) == 0) cout << '.';
+    if ((i & 0XFF) == 0) {
+      cout << '.';
+    }
     if (!card.writeData(cache.data)) {
       sdError("Clear FAT/DIR writeData failed");
     }
@@ -192,7 +195,9 @@ void writeMbr() {
   part_t* p = cache.mbr.part;
   p->boot = 0;
   uint16_t c = lbnToCylinder(relSector);
-  if (c > 1023) sdError("MBR CHS");
+  if (c > 1023) {
+    sdError("MBR CHS");
+  }
   p->beginCylinderHigh = c >> 8;
   p->beginCylinderLow = c & 0XFF;
   p->beginHead = lbnToHead(relSector);
@@ -214,7 +219,9 @@ void writeMbr() {
   }
   p->firstSector = relSector;
   p->totalSectors = partSize;
-  if (!writeCache(0)) sdError("write MBR");
+  if (!writeCache(0)) {
+    sdError("write MBR");
+  }
 }
 //------------------------------------------------------------------------------
 // generate serial number from card size and micros since boot
@@ -229,12 +236,16 @@ void makeFat16() {
     nc = (cardSizeBlocks - dataStart)/sectorsPerCluster;
     fatSize = (nc + 2 + 255)/256;
     uint32_t r = BU16 + 1 + 2 * fatSize + 32;
-    if (dataStart < r) continue;
+    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");
+  if (nc < 4085 || nc >= 65525) {
+    sdError("Bad cluster count");
+  }
   reservedSectors = 1;
   fatStart = relSector + reservedSectors;
   partSize = nc * sectorsPerCluster + 2 * fatSize + reservedSectors + 32;
@@ -282,7 +293,7 @@ void makeFat16() {
   cache.fat16[1] = 0XFFFF;
   // write first block of FAT and backup for reserved clusters
   if (!writeCache(fatStart)
-    || !writeCache(fatStart + fatSize)) {
+      || !writeCache(fatStart + fatSize)) {
     sdError("FAT16 reserve failed");
   }
 }
@@ -295,10 +306,14 @@ void makeFat32() {
     nc = (cardSizeBlocks - dataStart)/sectorsPerCluster;
     fatSize = (nc + 2 + 127)/128;
     uint32_t r = relSector + 9 + 2 * fatSize;
-    if (dataStart >= r) break;
+    if (dataStart >= r) {
+      break;
+    }
   }
   // error if too few clusters in FAT32 volume
-  if (nc < 65525) sdError("Bad cluster count");
+  if (nc < 65525) {
+    sdError("Bad cluster count");
+  }
   reservedSectors = dataStart - relSector - 2 * fatSize;
   fatStart = relSector + reservedSectors;
   partSize = nc * sectorsPerCluster + dataStart - relSector;
@@ -341,13 +356,13 @@ void makeFat32() {
   memcpy(pb->fileSystemType, fat32str, sizeof(pb->fileSystemType));
   // write partition boot sector and backup
   if (!writeCache(relSector)
-    || !writeCache(relSector + 6)) {
+      || !writeCache(relSector + 6)) {
     sdError("FAT32 write PBS failed");
   }
   clearCache(true);
   // write extra boot area and backup
   if (!writeCache(relSector + 2)
-    || !writeCache(relSector + 8)) {
+      || !writeCache(relSector + 8)) {
     sdError("FAT32 PBS ext failed");
   }
   fat32_fsinfo_t* pf = &cache.fsinfo;
@@ -357,7 +372,7 @@ void makeFat32() {
   pf->nextFree = 0XFFFFFFFF;
   // write FSINFO sector and backup
   if (!writeCache(relSector + 1)
-    || !writeCache(relSector + 7)) {
+      || !writeCache(relSector + 7)) {
     sdError("FAT32 FSINFO failed");
   }
   clearFatDir(fatStart, 2 * fatSize + sectorsPerCluster);
@@ -367,7 +382,7 @@ void makeFat32() {
   cache.fat32[2] = 0x0FFFFFFF;
   // write first block of FAT and backup for reserved clusters
   if (!writeCache(fatStart)
-    || !writeCache(fatStart + fatSize)) {
+      || !writeCache(fatStart + fatSize)) {
     sdError("FAT32 reserve failed");
   }
 }
@@ -375,43 +390,51 @@ void makeFat32() {
 // flash erase all data
 uint32_t const ERASE_SIZE = 262144L;
 void eraseCard() {
-  cout << endl << pstr("Erasing\n");
+  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");
+    if (lastBlock >= cardSizeBlocks) {
+      lastBlock = cardSizeBlocks - 1;
+    }
+    if (!card.erase(firstBlock, lastBlock)) {
+      sdError("erase failed");
+    }
     cout << '.';
-    if ((n++)%32 == 31) cout << endl;
+    if ((n++)%32 == 31) {
+      cout << endl;
+    }
     firstBlock += ERASE_SIZE;
   } while (firstBlock < cardSizeBlocks);
   cout << endl;
 
-  if (!card.readBlock(0, cache.data)) sdError("readBlock");
+  if (!card.readBlock(0, cache.data)) {
+    sdError("readBlock");
+  }
   cout << hex << showbase << setfill('0') << internal;
-  cout << pstr("All data set to ") << setw(4) << int(cache.data[0]) << endl;
+  cout << F("All data set to ") << setw(4) << int(cache.data[0]) << endl;
   cout << dec << noshowbase << setfill(' ') << right;
-  cout << pstr("Erase done\n");
+  cout << F("Erase done\n");
 }
 //------------------------------------------------------------------------------
 void formatCard() {
   cout << endl;
-  cout << pstr("Formatting\n");
+  cout << F("Formatting\n");
   initSizes();
   if (card.type() != SD_CARD_TYPE_SDHC) {
-    cout << pstr("FAT16\n");
+    cout << F("FAT16\n");
     makeFat16();
   } else {
-    cout << pstr("FAT32\n");
+    cout << F("FAT32\n");
     makeFat32();
   }
 #if DEBUG_PRINT
   debugPrint();
 #endif  // DEBUG_PRINT
-  cout << pstr("Format done\n");
+  cout << F("Format done\n");
 }
 //------------------------------------------------------------------------------
 void setup() {
@@ -419,61 +442,63 @@ void setup() {
   Serial.begin(9600);
   while (!Serial) {} // wait for Leonardo
 
-  cout << pstr(
-    "\n"
-    "This sketch 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: ");
+  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()) {}
   delay(400);  // catch Due restart problem
-  
+
   c = Serial.read();
   cout << c << endl;
   if (c != 'Y') {
-    cout << pstr("Quiting, you did not enter 'Y'.\n");
+    cout << F("Quiting, you did not enter 'Y'.\n");
     return;
   }
   // read any existing Serial data
   while (Serial.read() >= 0) {}
-  
-  cout << pstr(
-    "\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: ");
-    
+
+  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()) {}
   c = Serial.read();
   cout << c << endl;
   if (!strchr("EFQ", c)) {
-    cout << pstr("Quiting, invalid option entered.") << endl;
+    cout << F("Quiting, invalid option entered.") << endl;
     return;
   }
 
-  if (!card.init(spiSpeed, chipSelect)) {
-    cout << pstr(
-     "\nSD initialization failure!\n"
-     "Is the SD card inserted correctly?\n"
-     "Is chip select correct at the top of this sketch?\n");
-    sdError("card.init failed");
+  if (!card.begin(chipSelect, spiSpeed)) {
+    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");
   }
   cardSizeBlocks = card.cardSize();
-  if (cardSizeBlocks == 0) sdError("cardSize");
+  if (cardSizeBlocks == 0) {
+    sdError("cardSize");
+  }
   cardCapacityMB = (cardSizeBlocks + 2047)/2048;
 
-  cout << pstr("Card Size: ") << cardCapacityMB;
-  cout << pstr(" MB, (MB = 1,048,576 bytes)") << endl;
+  cout << F("Card Size: ") << cardCapacityMB;
+  cout << F(" MB, (MB = 1,048,576 bytes)") << endl;
 
   if (c == 'E' || c == 'F') {
     eraseCard();

+ 107 - 83
SdFat/examples/SdInfo/SdInfo.ino

@@ -1,6 +1,7 @@
 /*
- * This sketch attempts to initialize an SD card and analyze its structure.
+ * This program attempts to initialize an SD card and analyze its structure.
  */
+#include <SPI.h>
 #include <SdFat.h>
 /*
  * SD chip select pin.  Common values are:
@@ -10,10 +11,14 @@
  * Adafruit SD shields and modules, pin 10.
  * Default SD chip select is the SPI SS pin.
  */
-const uint8_t SdChipSelect = SS;
-
-Sd2Card card;
-SdVolume vol;
+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;
+SdFat sd;
 
 // serial output steam
 ArduinoOutStream cout(Serial);
@@ -25,34 +30,34 @@ uint32_t cardSize;
 uint32_t eraseSize;
 //------------------------------------------------------------------------------
 // store error strings in flash
-#define sdErrorMsg(msg) sdErrorMsg_P(PSTR(msg));
-void sdErrorMsg_P(const char* str) {
-  cout << pgm(str) << endl;
-  if (card.errorCode()) {
-    cout << pstr("SD errorCode: ");
-    cout << hex << int(card.errorCode()) << endl;
-    cout << pstr("SD errorData: ");
-    cout << int(card.errorData()) << dec << endl;
+#define sdErrorMsg(msg) sdErrorMsg_F(F(msg));
+void sdErrorMsg_F(const __FlashStringHelper* str) {
+  cout << str << endl;
+  if (sd.card()->errorCode()) {
+    cout << F("SD errorCode: ");
+    cout << hex << int(sd.card()->errorCode()) << endl;
+    cout << F("SD errorData: ");
+    cout << int(sd.card()->errorData()) << dec << endl;
   }
 }
 //------------------------------------------------------------------------------
 uint8_t cidDmp() {
   cid_t cid;
-  if (!card.readCID(&cid)) {
+  if (!sd.card()->readCID(&cid)) {
     sdErrorMsg("readCID failed");
     return false;
   }
-  cout << pstr("\nManufacturer ID: ");
+  cout << F("\nManufacturer ID: ");
   cout << hex << int(cid.mid) << dec << endl;
-  cout << pstr("OEM ID: ") << cid.oid[0] << cid.oid[1] << endl;
-  cout << pstr("Product: ");
+  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 << pstr("\nVersion: ");
+  cout << F("\nVersion: ");
   cout << int(cid.prv_n) << '.' << int(cid.prv_m) << endl;
-  cout << pstr("Serial number: ") << hex << cid.psn << dec << endl;
-  cout << pstr("Manufacturing date: ");
+  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;
@@ -62,7 +67,7 @@ uint8_t cidDmp() {
 uint8_t csdDmp() {
   csd_t csd;
   uint8_t eraseSingleBlock;
-  if (!card.readCSD(&csd)) {
+  if (!sd.card()->readCSD(&csd)) {
     sdErrorMsg("readCSD failed");
     return false;
   }
@@ -73,43 +78,43 @@ uint8_t csdDmp() {
     eraseSingleBlock = csd.v2.erase_blk_en;
     eraseSize = (csd.v2.sector_size_high << 1) | csd.v2.sector_size_low;
   } else {
-    cout << pstr("csd version error\n");
+    cout << F("csd version error\n");
     return false;
   }
   eraseSize++;
-  cout << pstr("cardSize: ") << 0.000512*cardSize;
-  cout << pstr(" MB (MB = 1,000,000 bytes)\n");
+  cout << F("cardSize: ") << 0.000512*cardSize;
+  cout << F(" MB (MB = 1,000,000 bytes)\n");
 
-  cout << pstr("flashEraseSize: ") << int(eraseSize) << pstr(" blocks\n");
-  cout << pstr("eraseSingleBlock: ");
+  cout << F("flashEraseSize: ") << int(eraseSize) << F(" blocks\n");
+  cout << F("eraseSingleBlock: ");
   if (eraseSingleBlock) {
-    cout << pstr("true\n");
+    cout << F("true\n");
   } else {
-    cout << pstr("false\n");
+    cout << F("false\n");
   }
   return true;
 }
 //------------------------------------------------------------------------------
 // print partition table
 uint8_t partDmp() {
-  cache_t *p = vol.cacheClear();
+  cache_t *p = sd.vol()->cacheClear();
   if (!p) {
     sdErrorMsg("cacheClear failed");
     return false;
   }
-  if (!card.readBlock(0, p->data)) {
-      sdErrorMsg("read MBR failed");
-      return false;
+  if (!sd.card()->readBlock(0, p->data)) {
+    sdErrorMsg("read MBR failed");
+    return false;
   }
   for (uint8_t ip = 1; ip < 5; ip++) {
     part_t *pt = &p->mbr.part[ip - 1];
     if ((pt->boot & 0X7F) != 0 || pt->firstSector > cardSize) {
-      cout << pstr("\nNo MBR. Assuming Super Floppy format.\n");
+      cout << F("\nNo MBR. Assuming Super Floppy format.\n");
       return true;
     }
   }
-  cout << pstr("\nSD Partition Table\n");
-  cout << pstr("part,boot,type,start,length\n");
+  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 = &p->mbr.part[ip - 1];
     cout << int(ip) << ',' << hex << int(pt->boot) << ',' << int(pt->type);
@@ -119,21 +124,22 @@ uint8_t partDmp() {
 }
 //------------------------------------------------------------------------------
 void volDmp() {
-  cout << pstr("\nVolume is FAT") << int(vol.fatType()) << endl;
-  cout << pstr("blocksPerCluster: ") << int(vol.blocksPerCluster()) << endl;
-  cout << pstr("clusterCount: ") << vol.clusterCount() << endl;
-  uint32_t volFree = vol.freeClusterCount();
-  cout << pstr("freeClusters: ") <<  volFree << endl;
-  float fs = 0.000512*volFree*vol.blocksPerCluster();
-  cout << pstr("freeSpace: ") << fs << pstr(" MB (MB = 1,000,000 bytes)\n");
-  cout << pstr("fatStartBlock: ") << vol.fatStartBlock() << endl;
-  cout << pstr("fatCount: ") << int(vol.fatCount()) << endl;
-  cout << pstr("blocksPerFat: ") << vol.blocksPerFat() << endl;
-  cout << pstr("rootDirStart: ") << vol.rootDirStart() << endl;
-  cout << pstr("dataStartBlock: ") << vol.dataStartBlock() << endl;
-  if (vol.dataStartBlock() % eraseSize) {
-    cout << pstr("Data area is not aligned on flash erase boundaries!\n");
-    cout << pstr("Download and use formatter from www.sdcard.org/consumer!\n");
+  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.sdsd.card()->org/consumer!\n");
   }
 }
 //------------------------------------------------------------------------------
@@ -145,7 +151,19 @@ void setup() {
   cout << uppercase << showbase << endl;
 
   // pstr stores strings in flash to save RAM
-  cout << pstr("SdFat version: ") << SD_FAT_VERSION << endl;
+  cout << F("SdFat version: ") << SD_FAT_VERSION << endl;
+  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");
 }
 //------------------------------------------------------------------------------
 void loop() {
@@ -153,57 +171,63 @@ void loop() {
   while (Serial.read() >= 0) {}
 
   // pstr stores strings in flash to save RAM
-  cout << pstr("\ntype any character to start\n");
+  cout << F("\ntype any character to start\n");
   while (Serial.read() <= 0) {}
   delay(400);  // catch Due reset problem
-  
+
   uint32_t t = millis();
   // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
   // breadboards.  use SPI_FULL_SPEED for better performance.
-  if (!card.init(SPI_HALF_SPEED, SdChipSelect)) {
-    sdErrorMsg("\ncard.init failed");
+  if (!sd.cardBegin(SD_CHIP_SELECT, SPI_HALF_SPEED)) {
+    sdErrorMsg("\ncardBegin failed");
     return;
   }
   t = millis() - t;
-  
-  cardSize = card.cardSize();
+
+  cardSize = sd.card()->cardSize();
   if (cardSize == 0) {
     sdErrorMsg("cardSize failed");
     return;
   }
-  cout << pstr("\ninit time: ") << t << " ms" << endl;
-  cout << pstr("\nCard type: ");
-  switch (card.type()) {
-    case SD_CARD_TYPE_SD1:
-      cout << pstr("SD1\n");
-      break;
+  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 << pstr("SD2\n");
-      break;
+  case SD_CARD_TYPE_SD2:
+    cout << F("SD2\n");
+    break;
 
-    case SD_CARD_TYPE_SDHC:
-      if (cardSize < 70000000) {
-        cout << pstr("SDHC\n");
-      } else {
-        cout << pstr("SDXC\n");
-      }
-      break;
+  case SD_CARD_TYPE_SDHC:
+    if (cardSize < 70000000) {
+      cout << F("SDHC\n");
+    } else {
+      cout << F("SDXC\n");
+    }
+    break;
 
-    default:
-      cout << pstr("Unknown\n");
+  default:
+    cout << F("Unknown\n");
+  }
+  if (!cidDmp()) {
+    return;
+  }
+  if (!csdDmp()) {
+    return;
   }
-  if (!cidDmp()) return;
-  if (!csdDmp()) return;
   uint32_t ocr;
-  if (!card.readOCR(&ocr)) {
+  if (!sd.card()->readOCR(&ocr)) {
     sdErrorMsg("\nreadOCR failed");
-    return;    
+    return;
+  }
+  cout << F("OCR: ") << hex << ocr << dec << endl;
+  if (!partDmp()) {
+    return;
   }
-  cout << pstr("OCR: ") << hex << ocr << dec << endl;
-  if (!partDmp()) return;
-  if (!vol.init(&card)) {
-    sdErrorMsg("\nvol.init failed");
+  if (!sd.fsBegin()) {
+    sdErrorMsg("\nFile System initialization failed.\n");
     return;
   }
   volDmp();

+ 48 - 0
SdFat/examples/SoftwareSpi/SoftwareSpi.ino

@@ -0,0 +1,48 @@
+// 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>
+#if SD_SPI_CONFIGURATION >= 3  // 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);
+  while (!Serial) {}  // Wait for Leonardo
+
+  Serial.println("Type any character to start");
+  while (Serial.read() <= 0) {}
+
+  if (!sd.begin(SD_CHIP_SELECT_PIN)) {
+    sd.initErrorHalt();
+  }
+
+  if (!file.open("SoftSPI.txt", O_CREAT | O_RDWR)) {
+    sd.errorHalt(F("open failed"));
+  }
+  file.println(F("This line was printed using software SPI."));
+
+  file.close();
+
+  Serial.println(F("Done."));
+}
+//------------------------------------------------------------------------------
+void loop() {}
+#else  // SD_SPI_CONFIGURATION >= 3
+#error SD_SPI_CONFIGURATION must be set to 3 in SdFat/SdFatConfig.h
+#endif  //SD_SPI_CONFIGURATION >= 3

+ 54 - 47
SdFat/examples/StdioBench/StdioBench.ino

@@ -1,4 +1,5 @@
 // Benchmark comparing SdFile and StdioStream.
+#include <SPI.h>
 #include <SdFat.h>
 
 // Define PRINT_FIELD nonzero to use printField.
@@ -16,10 +17,11 @@ StdioStream stdioFile;
 
 float f[100];
 char buf[20];
-char* label[] = 
-  {"uint8_t 0 to 255, 100 times ", "uint16_t 0 to 20000",
+char* label[] =
+{ "uint8_t 0 to 255, 100 times ", "uint16_t 0 to 20000",
   "uint32_t 0 to 20000", "uint32_t 1000000000 to 1000010000",
-  "float nnn.ffff, 10000 times"};
+  "float nnn.ffff, 10000 times"
+};
 //------------------------------------------------------------------------------
 void setup() {
   uint32_t m;
@@ -27,23 +29,27 @@ void setup() {
   uint32_t stdioSize;
   uint32_t printTime;
   uint32_t stdioTime;
-  
+
   Serial.begin(9600);
+  while (!Serial) {}
+
   Serial.println(F("Type any character to start"));
   while (!Serial.available());
   Serial.println(F("Starting test"));
-  if (!sd.begin(SD_CS_PIN)) sd.errorHalt();
+  if (!sd.begin(SD_CS_PIN)) {
+    sd.errorHalt();
+  }
 
   for (uint8_t i = 0; i < 100; i++) {
-    f[i] = 123.0 + 0.12345*i;
-  }  
+    f[i] = 123.0 + 0.1234*i;
+  }
 
   for (uint8_t dataType = 0; dataType < 5; dataType++) {
     for (uint8_t fileType = 0; fileType < 2; fileType++) {
       if (!fileType) {
-        if (!printFile.open("PRRINT.TXT", O_CREAT | O_RDWR | O_TRUNC)) {
+        if (!printFile.open("print.txt", O_CREAT | O_RDWR | O_TRUNC)) {
           Serial.println("open fail");
-            return;
+          return;
         }
         printTime = millis();
         switch (dataType) {
@@ -52,121 +58,122 @@ void setup() {
             for (uint8_t j = 0; j < 255; j++) {
               printFile.println(j);
             }
-          }            
+          }
           break;
         case 1:
           for (uint16_t i = 0; i < 20000; i++) {
             printFile.println(i);
           }
           break;
-             
+
         case 2:
           for (uint32_t i = 0; i < 20000; i++) {
             printFile.println(i);
           }
           break;
-             
+
         case 3:
           for (uint16_t i = 0; i < 10000; i++) {
             printFile.println(i + 1000000000UL);
           }
           break;
-        
+
         case 4:
           for (int j = 0; j < 100; j++) {
             for (uint8_t i = 0; i < 100; i++) {
               printFile.println(f[i], 4);
             }
           }
-          break;        
+          break;
         default:
           break;
         }
-        printFile.sync();        
+        printFile.sync();
         printTime = millis() - printTime;
         printFile.rewind();
-        printSize = printFile.fileSize(); 
+        printSize = printFile.fileSize();
 
       } else {
-        if (!stdioFile.fopen("STREAM.TXT", "w+")) {
+        if (!stdioFile.fopen("stream.txt", "w+")) {
           Serial.println("fopen fail");
           return;
         }
         stdioTime = millis();
-        
-         switch (dataType) {
+
+        switch (dataType) {
         case 0:
           for (uint16_t i =0; i < 100; i++) {
             for (uint8_t j = 0; j < 255; j++) {
-              #if PRINT_FIELD
+#if PRINT_FIELD
               stdioFile.printField(j, '\n');
-              #else  // PRINT_FIELD
+#else  // PRINT_FIELD
               stdioFile.println(j);
-              #endif  // PRINT_FIELD
+#endif  // PRINT_FIELD
             }
-          }            
+          }
           break;
         case 1:
           for (uint16_t i = 0; i < 20000; i++) {
-            #if PRINT_FIELD
+#if PRINT_FIELD
             stdioFile.printField(i, '\n');
-            #else  // PRINT_FIELD
+#else  // PRINT_FIELD
             stdioFile.println(i);
-            #endif  // PRINT_FIELD
+#endif  // PRINT_FIELD
           }
           break;
-             
+
         case 2:
           for (uint32_t i = 0; i < 20000; i++) {
-            #if PRINT_FIELD
+#if PRINT_FIELD
             stdioFile.printField(i, '\n');
-            #else  // PRINT_FIELD
+#else  // PRINT_FIELD
             stdioFile.println(i);
-            #endif  // PRINT_FIELD
+#endif  // PRINT_FIELD
           }
           break;
-             
+
         case 3:
           for (uint16_t i = 0; i < 10000; i++) {
-            #if PRINT_FIELD
+#if PRINT_FIELD
             stdioFile.printField(i + 1000000000UL, '\n');
-            #else  // PRINT_FIELD
+#else  // PRINT_FIELD
             stdioFile.println(i + 1000000000UL);
-            #endif  // PRINT_FIELD      
+#endif  // PRINT_FIELD      
           }
           break;
-        
+
         case 4:
           for (int j = 0; j < 100; j++) {
             for (uint8_t i = 0; i < 100; i++) {
-              #if PRINT_FIELD
+#if PRINT_FIELD
               stdioFile.printField(f[i], '\n', 4);
-              #else  // PRINT_FIELD
-              stdioFile.println(f[i], 4);              
-              #endif  // PRINT_FIELD                            
+#else  // PRINT_FIELD
+              stdioFile.println(f[i], 4);
+#endif  // PRINT_FIELD                            
             }
           }
-          break;        
+          break;
         default:
           break;
         }
         stdioFile.fflush();
         stdioTime = millis() - stdioTime;
-        stdioSize = stdioFile.ftell();   
+        stdioSize = stdioFile.ftell();
         if (STDIO_LIST_COUNT) {
           size_t len;
           stdioFile.rewind();
           for (int i = 0; i < STDIO_LIST_COUNT; i++) {
             stdioFile.fgets(buf, sizeof(buf), &len);
-            Serial.print(len);Serial.print(',');
+            Serial.print(len);
+            Serial.print(',');
             Serial.print(buf);
           }
         }
 
       }
-     
+
     }
-    Serial.println(label[dataType]);    
+    Serial.println(label[dataType]);
     if (VERIFY_CONTENT && printSize == stdioSize) {
       printFile.rewind();
       stdioFile.rewind();
@@ -184,7 +191,7 @@ void setup() {
       Serial.print(printSize);
       Serial.print(" != ");
     }
-    Serial.println(stdioSize);    
+    Serial.println(stdioSize);
     Serial.print("print millis: ");
     Serial.println(printTime);
     Serial.print("stdio millis: ");
@@ -192,8 +199,8 @@ void setup() {
     Serial.print("ratio: ");
     Serial.println((float)printTime/(float)stdioTime);
     Serial.println();
-    printFile.close();     
-    stdioFile.fclose();    
+    printFile.close();
+    stdioFile.fclose();
   }
   Serial.println("Done");
 }

+ 13 - 6
SdFat/examples/StreamParseInt/StreamParseInt.ino

@@ -1,14 +1,20 @@
 // Simple demo of the Stream parsInt() member function.
+#include <SPI.h>
+// The next two lines replace #include <SD.h>.
 #include <SdFat.h>
+SdFat SD;
 
 // SD card chip select pin - Modify the value of csPin for your SD module.
 const uint8_t csPin = 10;
 
-SdFat SD;
 File file;
 //------------------------------------------------------------------------------
 void setup() {
   Serial.begin(9600);
+  // Wait for USB Serial.
+  while(!Serial) {}
+  Serial.println(F("Type any character to start"));
+  while (!Serial.available()) {}
 
   // Initialize the SD.
   if (!SD.begin(csPin)) {
@@ -16,19 +22,20 @@ void setup() {
     return;
   }
   // Create and open the file.  Use flag to truncate an existing file.
-  if (!file.open("stream.txt", O_RDWR|O_CREAT|O_TRUNC)) {
+  file = SD.open("stream.txt", O_RDWR|O_CREAT|O_TRUNC);
+  if (!file) {
     Serial.println(F("open error"));
     return;
   }
   // Write a test number to the file.
   file.println("12345");
-  
+
   // Rewind the file and read the number with parseInt().
-  file.rewind();
+  file.seek(0);
   int i = file.parseInt();
   Serial.print(F("parseInt: "));
-  Serial.println(i);  
+  Serial.println(i);
   file.close();
 }
 
-void loop() {}
+void loop() {}

+ 228 - 0
SdFat/examples/ThreeCards/ThreeCards.ino

@@ -0,0 +1,228 @@
+/*
+ * Example use of three SD cards.
+ */
+#include <SPI.h>
+#include <SdFat.h>
+#include <SdFatUtil.h>
+#if SD_SPI_CONFIGURATION >= 3  // Must be set in SdFat/SdFatConfig.h
+
+// SD1 is a microSD on hardware SPI pins 50-52
+// Using my fast custom SPI
+SdFat sd1;
+const uint8_t SD1_CS = 53;
+
+// SD2 is a Catalex shield on hardware SPI pins 50-52
+// Using the standard Arduino SPI library
+SdFatLibSpi sd2;
+const uint8_t SD2_CS = 4;
+
+// SD3 is a Adafruit data logging shield on pins 10-13
+// Using Software SPI
+SdFatSoftSpi<12, 11, 13> sd3;
+const uint8_t SD3_CS = 10;
+
+const uint8_t BUF_DIM = 100;
+uint8_t buf[BUF_DIM];
+
+const uint32_t FILE_SIZE = 1000000;
+const uint16_t NWRITE = FILE_SIZE/BUF_DIM;
+//------------------------------------------------------------------------------
+// print error msg, any SD error codes, and halt.
+// store messages in flash
+#define errorExit(msg) errorHalt(F(msg))
+#define initError(msg) initErrorHalt(F(msg))
+//------------------------------------------------------------------------------
+void list() {
+// list current directory on all cards
+  Serial.println(F("------sd1-------"));
+  sd1.ls("/", LS_SIZE|LS_R);
+  Serial.println(F("------sd2-------"));
+  sd2.ls("/", LS_SIZE|LS_R);
+  Serial.println(F("------sd3-------"));
+  sd3.ls("/", LS_SIZE|LS_R);
+  Serial.println(F("---------------------"));
+}
+//------------------------------------------------------------------------------
+void setup() {
+  Serial.begin(9600);
+  while (!Serial) {}  // wait for Leonardo
+  Serial.print(F("FreeRam: "));
+
+  Serial.println(FreeRam());
+
+  // fill buffer with known data
+  for (int i = 0; i < sizeof(buf); i++) {
+    buf[i] = i;
+  }
+
+  Serial.println(F("type any character to start"));
+  while (Serial.read() <= 0) {}
+
+  // disable sd2 while initializing sd1
+  pinMode(SD2_CS, OUTPUT);
+  digitalWrite(SD2_CS, HIGH);
+
+  // initialize the first card
+  if (!sd1.begin(SD1_CS)) {
+    sd1.initError("sd1:");
+  }
+
+  // initialize the second card
+  if (!sd2.begin(SD2_CS)) {
+    sd2.initError("sd2:");
+  }
+
+  // initialize the third card
+  if (!sd3.begin(SD3_CS)) {
+    sd3.initError("sd3:");
+  }
+
+  Serial.println(F("Cards OK - creating directories"));
+
+  // create Dir1 on sd1 if it does not exist
+  if (!sd1.exists("/Dir1")) {
+    if (!sd1.mkdir("/Dir1")) {
+      sd1.errorExit("sd1.mkdir");
+    }
+  }
+  // make /Dir1 the default directory for sd1
+  if (!sd1.chdir("/Dir1")) {
+    sd1.errorExit("sd1.chdir");
+  }
+
+  // create Dir2 on sd2 if it does not exist
+  if (!sd2.exists("/Dir2")) {
+    if (!sd2.mkdir("/Dir2")) {
+      sd2.errorExit("sd2.mkdir");
+    }
+  }
+  // make /Dir2 the default directory for sd2
+  if (!sd2.chdir("/Dir2")) {
+    sd2.errorExit("sd2.chdir");
+  }
+
+  // create Dir3 on sd3 if it does not exist
+  if (!sd3.exists("/Dir3")) {
+    if (!sd3.mkdir("/Dir3")) {
+      sd2.errorExit("sd3.mkdir");
+    }
+  }
+  // make /Dir3 the default directory for sd3
+  if (!sd3.chdir("/Dir3")) {
+    sd3.errorExit("sd3.chdir");
+  }
+
+  Serial.println(F("Directories created - removing old files"));
+
+  if (sd1.exists("TEST1.bin")) {
+    if (!sd1.remove("TEST1.bin")) {
+      sd1.errorExit("sd1.remove");
+    }
+  }
+  if (sd2.exists("TEST2.bin")) {
+    if (!sd2.remove("TEST2.bin")) {
+      sd2.errorExit("sd2.remove");
+    }
+  }
+  if (sd3.exists("TEST3.bin")) {
+    if (!sd3.remove("TEST3.bin")) {
+      sd2.errorExit("sd3.remove");
+    }
+  }
+  Serial.println("Initial SD directories");
+  list();
+
+  // create or open /Dir1/TEST1.bin and truncate it to zero length
+  SdFile file1;
+  if (!file1.open(&sd1, "TEST1.bin", O_RDWR | O_CREAT | O_TRUNC)) {
+    sd1.errorExit("file1");
+  }
+  Serial.println(F("Writing SD1:/Dir1/TEST1.bin"));
+
+  // write data to /Dir1/TEST1.bin on sd1
+  for (int i = 0; i < NWRITE; i++) {
+    if (file1.write(buf, sizeof(buf)) != sizeof(buf)) {
+      sd1.errorExit("sd1.write");
+    }
+  }
+  file1.sync();
+  list();
+
+  // create or open /Dir2/TEST2.bin and truncate it to zero length
+  SdFile file2;
+  if (!file2.open(&sd2, "TEST2.bin", O_RDWR | O_CREAT | O_TRUNC)) {
+    sd2.errorExit("file2");
+  }
+  Serial.println(F("Copying SD1:/Dir1/TEST1.bin to SD2::/Dir2/TEST2.bin"));
+
+  // copy file1 to file2
+  file1.rewind();
+
+  uint32_t t = millis();
+
+  while (1) {
+    int n = file1.read(buf, sizeof(buf));
+    if (n < 0) {
+      sd1.errorExit("read1");
+    }
+    if (n == 0) {
+      break;
+    }
+    if (file2.write(buf, n) != n) {
+      sd2.errorExit("write3");
+    }
+  }
+  t = millis() - t;
+  file2.sync();
+  Serial.print(F("File size: "));
+  Serial.println(file2.fileSize());
+  Serial.print(F("Copy time: "));
+  Serial.print(t);
+  Serial.println(F(" millis"));
+  list();
+
+  // create or open /Dir3/TEST3.bin and truncate it to zero length
+  SdFile file3;
+  if (!file3.open(&sd3, "TEST3.bin", O_RDWR | O_CREAT | O_TRUNC)) {
+    sd3.errorExit("file3");
+  }
+  file2.rewind();
+  Serial.println(F("Copying SD2:/Dir2/TEST2.bin to SD3:/Dir3/TEST3.bin"));
+  while (1) {
+    int n = file2.read(buf, sizeof(buf));
+    if (n == 0) {
+      break;
+    }
+    if (n != sizeof(buf)) {
+      sd2.errorExit("read2");
+    }
+    if (file3.write(buf, n) != n) {
+      sd3.errorExit("write2");
+    }
+  }
+  file3.sync();
+  list();
+
+  // Verify content of file3
+  file3.rewind();
+  Serial.println(F("Verifying content of TEST3.bin"));
+  for (int i = 0; i < NWRITE; i++) {
+    if (file3.read(buf, sizeof(buf)) != sizeof(buf)) {
+      sd3.errorExit("sd3.read");
+    }
+    for (int j = 0; j < sizeof(buf); j++) {
+      if (j != buf[j]) {
+        sd3.errorExit("Verify error");
+      }
+    }
+  }
+  Serial.println(F("Done - Verify OK"));
+  file1.close();
+  file2.close();
+  file3.close();
+}
+//------------------------------------------------------------------------------
+void loop() {}
+#else  // SD_SPI_CONFIGURATION >= 3
+#error SD_SPI_CONFIGURATION must be set to 3 in SdFat/SdFatConfig.h
+#endif  //SD_SPI_CONFIGURATION >= 3

+ 33 - 28
SdFat/examples/Timestamp/Timestamp.ino

@@ -1,7 +1,8 @@
 /*
- * This sketch tests the dateTimeCallback() function
+ * This program tests the dateTimeCallback() function
  * and the timestamp() function.
  */
+#include <SPI.h>
 #include <SdFat.h>
 
 SdFat sd;
@@ -15,14 +16,14 @@ const uint8_t chipSelect = SS;
 ArduinoOutStream cout(Serial);
 //------------------------------------------------------------------------------
 // store error strings in flash to save RAM
-#define error(s) sd.errorHalt_P(PSTR(s))
+#define error(s) sd.errorHalt(F(s))
 //------------------------------------------------------------------------------
 /*
  * date/time values for debug
  * normally supplied by a real-time clock or GPS
  */
-// date 1-Oct-09
-uint16_t year = 2009;
+// date 1-Oct-14
+uint16_t year = 2014;
 uint8_t month = 10;
 uint8_t day = 1;
 
@@ -51,21 +52,23 @@ void dateTime(uint16_t* date, uint16_t* time) {
  */
 void printTimestamps(SdFile& f) {
   dir_t d;
-  if (!f.dirEntry(&d)) error("f.dirEntry failed");
+  if (!f.dirEntry(&d)) {
+    error("f.dirEntry failed");
+  }
 
-  cout << pstr("Creation: ");
+  cout << F("Creation: ");
   f.printFatDate(d.creationDate);
   cout << ' ';
   f.printFatTime(d.creationTime);
   cout << endl;
 
-  cout << pstr("Modify: ");
+  cout << F("Modify: ");
   f.printFatDate(d.lastWriteDate);
   cout <<' ';
   f.printFatTime(d.lastWriteTime);
   cout << endl;
 
-  cout << pstr("Access: ");
+  cout << F("Access: ");
   f.printFatDate(d.lastAccessDate);
   cout << endl;
 }
@@ -74,24 +77,26 @@ void setup(void) {
   Serial.begin(9600);
   while (!Serial) {}  // wait for Leonardo
 
-  cout << pstr("Type any character to start\n");
+  cout << F("Type any character to start\n");
   while (!Serial.available());
   delay(400);  // catch Due reset problem
-  
+
   // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
   // breadboards.  use SPI_FULL_SPEED for better performance.
-  if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt();
+  if (!sd.begin(chipSelect, SPI_HALF_SPEED)) {
+    sd.initErrorHalt();
+  }
 
   // remove files if they exist
-  sd.remove("CALLBACK.TXT");
-  sd.remove("DEFAULT.TXT");
-  sd.remove("STAMP.TXT");
+  sd.remove("callback.txt");
+  sd.remove("default.txt");
+  sd.remove("stamp.txt");
 
   // create a new file with default timestamps
-  if (!file.open("DEFAULT.TXT", O_CREAT | O_WRITE)) {
-    error("open DEFAULT.TXT failed");
+  if (!file.open("default.txt", O_CREAT | O_WRITE)) {
+    error("open default.txt failed");
   }
-  cout << pstr("\nOpen with default times\n");
+  cout << F("\nOpen with default times\n");
   printTimestamps(file);
 
   // close file
@@ -111,8 +116,8 @@ void setup(void) {
   SdFile::dateTimeCallback(dateTime);
 
   // create a new file with callback timestamps
-  if (!file.open("CALLBACK.TXT", O_CREAT | O_WRITE)) {
-    error("open CALLBACK.TXT failed");
+  if (!file.open("callback.txt", O_CREAT | O_WRITE)) {
+    error("open callback.txt failed");
   }
   cout << ("\nOpen with callback times\n");
   printTimestamps(file);
@@ -129,7 +134,7 @@ void setup(void) {
   // force dir update
   file.sync();
 
-  cout << pstr("\nTimes after write\n");
+  cout << F("\nTimes after write\n");
   printTimestamps(file);
 
   // close file
@@ -143,26 +148,26 @@ void setup(void) {
   SdFile::dateTimeCallbackCancel();
 
   // create a new file with default timestamps
-  if (!file.open("STAMP.TXT", O_CREAT | O_WRITE)) {
-    error("open STAMP.TXT failed");
+  if (!file.open("stamp.txt", O_CREAT | O_WRITE)) {
+    error("open stamp.txt failed");
   }
   // set creation date time
-  if (!file.timestamp(T_CREATE, 2009, 11, 10, 1, 2, 3)) {
+  if (!file.timestamp(T_CREATE, 2014, 11, 10, 1, 2, 3)) {
     error("set create time failed");
   }
   // set write/modification date time
-  if (!file.timestamp(T_WRITE, 2009, 11, 11, 4, 5, 6)) {
+  if (!file.timestamp(T_WRITE, 2014, 11, 11, 4, 5, 6)) {
     error("set write time failed");
   }
   // set access date
-  if (!file.timestamp(T_ACCESS, 2009, 11, 12, 7, 8, 9)) {
+  if (!file.timestamp(T_ACCESS, 2014, 11, 12, 7, 8, 9)) {
     error("set access time failed");
   }
-  cout << pstr("\nTimes after timestamp() calls\n");
+  cout << F("\nTimes after timestamp() calls\n");
   printTimestamps(file);
 
   file.close();
-  cout << pstr("\nDone\n");
+  cout << F("\nDone\n");
 }
 
-void loop(void){}
+void loop(void) {}

+ 82 - 57
SdFat/examples/TwoCards/TwoCards.ino

@@ -1,17 +1,15 @@
 /*
  * Example use of two SD cards.
  */
+#include <SPI.h>
 #include <SdFat.h>
 #include <SdFatUtil.h>
-#if !USE_MULTIPLE_CARDS
-#error You must set USE_MULTIPLE_CARDS nonzero in SdFatConfig.h
-#endif
 
 SdFat sd1;
 const uint8_t SD1_CS = 10;  // chip select for sd1
 
 SdFat sd2;
-const uint8_t SD2_CS = 9;   // chip select for sd2
+const uint8_t SD2_CS = 4;   // chip select for sd2
 
 const uint8_t BUF_DIM = 100;
 uint8_t buf[BUF_DIM];
@@ -21,79 +19,89 @@ const uint16_t NWRITE = FILE_SIZE/BUF_DIM;
 //------------------------------------------------------------------------------
 // print error msg, any SD error codes, and halt.
 // store messages in flash
-#define errorExit(msg) errorHalt_P(PSTR(msg))
-#define initError(msg) initErrorHalt_P(PSTR(msg))
+#define errorExit(msg) errorHalt(F(msg))
+#define initError(msg) initErrorHalt(F(msg))
 //------------------------------------------------------------------------------
 void setup() {
   Serial.begin(9600);
   while (!Serial) {}  // wait for Leonardo
-  PgmPrint("FreeRam: ");
+  Serial.print(F("FreeRam: "));
 
   Serial.println(FreeRam());
-  
+
   // fill buffer with known data
-  for (int i = 0; i < sizeof(buf); i++) buf[i] = i;
-  
-  PgmPrintln("type any character to start");
+  for (int i = 0; i < sizeof(buf); i++) {
+    buf[i] = i;
+  }
+
+  Serial.println(F("type any character to start"));
   while (Serial.read() <= 0) {}
   delay(400);  // catch Due reset problem
 
   // disable sd2 while initializing sd1
   pinMode(SD2_CS, OUTPUT);
   digitalWrite(SD2_CS, HIGH);
-  
+
   // initialize the first card
   if (!sd1.begin(SD1_CS)) {
     sd1.initError("sd1:");
   }
-  // create DIR1 on sd1 if it does not exist
-  if (!sd1.exists("/DIR1")) {
-    if (!sd1.mkdir("/DIR1")) sd1.errorExit("sd1.mkdir");
+  // create Dir1 on sd1 if it does not exist
+  if (!sd1.exists("/Dir1")) {
+    if (!sd1.mkdir("/Dir1")) {
+      sd1.errorExit("sd1.mkdir");
+    }
   }
   // initialize the second card
   if (!sd2.begin(SD2_CS)) {
     sd2.initError("sd2:");
   }
- // create DIR2 on sd2 if it does not exist
-  if (!sd2.exists("/DIR2")) {
-    if (!sd2.mkdir("/DIR2")) sd2.errorExit("sd2.mkdir");
+// create Dir2 on sd2 if it does not exist
+  if (!sd2.exists("/Dir2")) {
+    if (!sd2.mkdir("/Dir2")) {
+      sd2.errorExit("sd2.mkdir");
+    }
   }
   // list root directory on both cards
-  PgmPrintln("------sd1 root-------");
+  Serial.println(F("------sd1 root-------"));
   sd1.ls();
-  PgmPrintln("------sd2 root-------");
+  Serial.println(F("------sd2 root-------"));
   sd2.ls();
 
-  // make /DIR1 the default directory for sd1
-  if (!sd1.chdir("/DIR1")) sd1.errorExit("sd1.chdir");
-  
-  // make /DIR2 the default directory for sd2
-  if (!sd2.chdir("/DIR2")) sd2.errorExit("sd2.chdir");
-  
+  // make /Dir1 the default directory for sd1
+  if (!sd1.chdir("/Dir1")) {
+    sd1.errorExit("sd1.chdir");
+  }
+
+  // make /Dir2 the default directory for sd2
+  if (!sd2.chdir("/Dir2")) {
+    sd2.errorExit("sd2.chdir");
+  }
+
   // list current directory on both cards
-  PgmPrintln("------sd1 DIR1-------");
+  Serial.println(F("------sd1 Dir1-------"));
   sd1.ls();
-  PgmPrintln("------sd2 DIR2-------");
+  Serial.println(F("------sd2 Dir2-------"));
   sd2.ls();
-  PgmPrintln("---------------------");
-  
-  // remove RENAME.BIN from /DIR2 directory of sd2
-  if (sd2.exists("RENAME.BIN")) {
-    if (!sd2.remove("RENAME.BIN")) {
-      sd2.errorExit("remove RENAME.BIN");
+  Serial.println(F("---------------------"));
+
+  // remove rename.bin from /Dir2 directory of sd2
+  if (sd2.exists("rename.bin")) {
+    if (!sd2.remove("rename.bin")) {
+      sd2.errorExit("remove rename.bin");
     }
   }
   // set the current working directory for open() to sd1
   sd1.chvol();
-  
-  // create or open /DIR1/TEST.BIN and truncate it to zero length
+
+  // create or open /Dir1/test.bin and truncate it to zero length
   SdFile file1;
-  if (!file1.open("TEST.BIN", O_RDWR | O_CREAT | O_TRUNC)) {
+  if (!file1.open("test.bin", O_RDWR | O_CREAT | O_TRUNC)) {
     sd1.errorExit("file1");
   }
-  PgmPrintln("Writing TEST.BIN to sd1");
-  
-  // write data to /DIR1/TEST.BIN on sd1
+  Serial.println(F("Writing test.bin to sd1"));
+
+  // write data to /Dir1/test.bin on sd1
   for (int i = 0; i < NWRITE; i++) {
     if (file1.write(buf, sizeof(buf)) != sizeof(buf)) {
       sd1.errorExit("sd1.write");
@@ -101,40 +109,57 @@ void setup() {
   }
   // set the current working directory for open() to sd2
   sd2.chvol();
-  
-  // create or open /DIR2/COPY.BIN and truncate it to zero length
+
+  // create or open /Dir2/copy.bin and truncate it to zero length
   SdFile file2;
-  if (!file2.open("COPY.BIN", O_WRITE | O_CREAT | O_TRUNC)) {
+  if (!file2.open("copy.bin", O_WRITE | O_CREAT | O_TRUNC)) {
     sd2.errorExit("file2");
   }
-  PgmPrintln("Copying TEST.BIN to COPY.BIN");
-  
+  Serial.println(F("Copying test.bin to copy.bin"));
+
   // copy file1 to file2
   file1.rewind();
   uint32_t t = millis();
 
   while (1) {
     int n = file1.read(buf, sizeof(buf));
-    if (n < 0) sd1.errorExit("read1");
-    if (n == 0) break;
-    if (file2.write(buf, n) != n) sd2.errorExit("write2");
+    if (n < 0) {
+      sd1.errorExit("read1");
+    }
+    if (n == 0) {
+      break;
+    }
+    if (file2.write(buf, n) != n) {
+      sd2.errorExit("write2");
+    }
   }
   t = millis() - t;
-  PgmPrint("File size: ");
+  Serial.print(F("File size: "));
   Serial.println(file2.fileSize());
-  PgmPrint("Copy time: ");
+  Serial.print(F("Copy time: "));
   Serial.print(t);
-  PgmPrintln(" millis");
-  
-  // close TEST.BIN
+  Serial.println(F(" millis"));
+  // close test.bin
   file1.close();
-  
+  file2.close(); 
+  // list current directory on both cards
+  Serial.println(F("------sd1 -------"));
+  sd1.ls("/", LS_R | LS_DATE | LS_SIZE);
+  Serial.println(F("------sd2 -------"));
+  sd2.ls("/", LS_R | LS_DATE | LS_SIZE);
+  Serial.println(F("---------------------"));
+  Serial.println(F("Renaming copy.bin"));
   // rename the copy
-  file2.close();
-  if (!sd2.rename("COPY.BIN", "RENAME.BIN")) {
+  if (!sd2.rename("copy.bin", "rename.bin")) {
     sd2.errorExit("sd2.rename");
   }
-  PgmPrintln("Done");
+  // list current directory on both cards
+  Serial.println(F("------sd1 -------"));
+  sd1.ls("/", LS_R | LS_DATE | LS_SIZE);
+  Serial.println(F("------sd2 -------"));
+  sd2.ls("/", LS_R | LS_DATE | LS_SIZE);
+  Serial.println(F("---------------------"));
+  Serial.println(F("Done"));
 }
 //------------------------------------------------------------------------------
 void loop() {}

+ 51 - 39
SdFat/examples/bench/bench.ino

@@ -1,6 +1,7 @@
 /*
- * This sketch is a simple binary write/read benchmark.
+ * This program is a simple binary write/read benchmark.
  */
+#include <SPI.h>
 #include <SdFat.h>
 #include <SdFatUtil.h>
 
@@ -36,24 +37,24 @@ SdFile file;
 ArduinoOutStream cout(Serial);
 //------------------------------------------------------------------------------
 // store error strings in flash to save RAM
-#define error(s) sd.errorHalt_P(PSTR(s))
+#define error(s) sd.errorHalt(F(s))
 //------------------------------------------------------------------------------
 void cidDmp() {
   cid_t cid;
   if (!sd.card()->readCID(&cid)) {
     error("readCID failed");
   }
-  cout << pstr("\nManufacturer ID: ");
+  cout << F("\nManufacturer ID: ");
   cout << hex << int(cid.mid) << dec << endl;
-  cout << pstr("OEM ID: ") << cid.oid[0] << cid.oid[1] << endl;
-  cout << pstr("Product: ");
+  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 << pstr("\nVersion: ");
+  cout << F("\nVersion: ");
   cout << int(cid.prv_n) << '.' << int(cid.prv_m) << endl;
-  cout << pstr("Serial number: ") << hex << cid.psn << dec << endl;
-  cout << pstr("Manufacturing date: ");
+  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;
@@ -61,9 +62,10 @@ void cidDmp() {
 //------------------------------------------------------------------------------
 void setup() {
   Serial.begin(9600);
-  while (!Serial){}  // wait for Leonardo
-  cout << pstr("\nUse a freshly formatted SD for best performance.\n");
-  
+  while (!Serial) {} // wait for Leonardo
+  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;
 }
@@ -78,25 +80,27 @@ void loop() {
   // discard any input
   while (Serial.read() >= 0) {}
 
-  // pstr stores strings in flash to save RAM
-  cout << pstr("Type any character to start\n");
+  // F( stores strings in flash to save RAM
+  cout << F("Type any character to start\n");
   while (Serial.read() <= 0) {}
   delay(400);  // catch Due reset problem
-  
-  cout << pstr("Free RAM: ") << FreeRam() << endl;
+
+  cout << F("Free RAM: ") << FreeRam() << endl;
 
   // initialize the SD card at SPI_FULL_SPEED for best performance.
   // try SPI_HALF_SPEED if bus errors occur.
-  if (!sd.begin(chipSelect, SPI_FULL_SPEED)) sd.initErrorHalt();
+  if (!sd.begin(chipSelect, SPI_FULL_SPEED)) {
+    sd.initErrorHalt();
+  }
+
+  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;
 
-  cout << pstr("Type is FAT") << int(sd.vol()->fatType()) << endl;
-  cout << pstr("Card size: ") << sd.card()->cardSize()*512E-9;
-  cout << pstr(" GB (GB = 1E9 bytes)") << endl;
-  
   cidDmp();
-  
+
   // open or create file - truncate existing file.
-  if (!file.open("BENCH.DAT", O_CREAT | O_TRUNC | O_RDWR)) {
+  if (!file.open("bench.dat", O_CREAT | O_TRUNC | O_RDWR)) {
     error("open failed");
   }
 
@@ -107,15 +111,15 @@ void loop() {
   buf[BUF_SIZE-2] = '\r';
   buf[BUF_SIZE-1] = '\n';
 
-  cout << pstr("File size ") << FILE_SIZE_MB << pstr(" MB\n");
-  cout << pstr("Buffer size ") << BUF_SIZE << pstr(" bytes\n");
-  cout << pstr("Starting write test, please wait.") << endl << endl;
+  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 <<pstr("write speed and latency") << endl;
-  cout << pstr("speed,max,min,avg") << endl;
-  cout << pstr("KB/Sec,usec,usec,usec") << endl;
+  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;
@@ -128,21 +132,25 @@ void loop() {
         error("write failed");
       }
       m = micros() - m;
-      if (maxLatency < m) maxLatency = m;
-      if (minLatency > m) minLatency = 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 << s/t <<',' << maxLatency << ',' << minLatency;
     cout << ',' << totalLatency/n << endl;
   }
 
-  cout << endl << pstr("Starting read test, please wait.") << endl;
-  cout << endl <<pstr("read speed and latency") << endl;
-  cout << pstr("speed,max,min,avg") << endl;
-  cout << pstr("KB/Sec,usec,usec,usec") << 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();
@@ -157,17 +165,21 @@ void loop() {
         error("read failed");
       }
       m = micros() - m;
-      if (maxLatency < m) maxLatency = m;
-      if (minLatency > m) minLatency = m;
+      if (maxLatency < m) {
+        maxLatency = m;
+      }
+      if (minLatency > m) {
+        minLatency = m;
+      }
       totalLatency += m;
       if (buf[BUF_SIZE-1] != '\n') {
         error("data check");
       }
     }
     t = millis() - t;
-    cout << s/t <<',' << maxLatency << ',' << minLatency; 
+    cout << s/t <<',' << maxLatency << ',' << minLatency;
     cout << ',' << totalLatency/n << endl;
   }
-  cout << endl << pstr("Done") << endl;
+  cout << endl << F("Done") << endl;
   file.close();
 }

+ 1 - 0
SdFat/examples/cin_cout/cin_cout.ino

@@ -1,6 +1,7 @@
 /*
  * Demo of ArduinoInStream and ArduinoOutStream
  */
+#include <SPI.h>
 #include <SdFat.h>
 
 // create serial output stream

+ 32 - 27
SdFat/examples/dataLogger/dataLogger.ino

@@ -1,19 +1,20 @@
 /*
  * Simple data logger.
  */
+#include <SPI.h>
 #include <SdFat.h>
 
 // SD chip select pin.  Be sure to disable any other SPI devices such as Enet.
 const uint8_t chipSelect = SS;
 
-// Interval between data records in milliseconds. 
+// Interval between data records in milliseconds.
 // The interval must be greater than the maximum SD write latency plus the
 // time to acquire and write data to the SD to avoid overrun errors.
 // Run the bench example to check the quality of your SD card.
 const uint32_t SAMPLE_INTERVAL_MS = 200;
 
 // Log file base name.  Must be six characters or less.
-#define FILE_BASE_NAME "DATA"
+#define FILE_BASE_NAME "Data"
 //------------------------------------------------------------------------------
 // File system object.
 SdFat sd;
@@ -42,14 +43,14 @@ void writeHeader() {
 // Log a data record.
 void logData() {
   uint16_t data[ANALOG_COUNT];
-  
+
   // Read all channels to avoid SD write latency between readings.
   for (uint8_t i = 0; i < ANALOG_COUNT; i++) {
     data[i] = analogRead(i);
   }
   // Write data to file.  Start with log time in micros.
   file.print(logTime);
-  
+
   // Write ADC data to CSV record.
   for (uint8_t i = 0; i < ANALOG_COUNT; i++) {
     file.write(',');
@@ -59,27 +60,25 @@ void logData() {
 }
 //==============================================================================
 // Error messages stored in flash.
-#define error(msg) error_P(PSTR(msg))
-//------------------------------------------------------------------------------
-void error_P(const char* msg) {
-  sd.errorHalt_P(msg);
-}
+#define error(msg) sd.errorHalt(F(msg))
 //------------------------------------------------------------------------------
 void setup() {
   const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1;
-  char fileName[13] = FILE_BASE_NAME "00.CSV";
-  
+  char fileName[13] = FILE_BASE_NAME "00.csv";
+
   Serial.begin(9600);
   while (!Serial) {} // wait for Leonardo
   delay(1000);
-  
+
   Serial.println(F("Type any character to start"));
   while (!Serial.available()) {}
-  
+
   // Initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
   // breadboards.  use SPI_FULL_SPEED for better performance.
-  if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt();
-  
+  if (!sd.begin(chipSelect, SPI_HALF_SPEED)) {
+    sd.initErrorHalt();
+  }
+
   // Find an unused file name.
   if (BASE_NAME_SIZE > 6) {
     error("FILE_BASE_NAME too long");
@@ -94,18 +93,20 @@ void setup() {
       error("Can't create file name");
     }
   }
-  if (!file.open(fileName, O_CREAT | O_WRITE | O_EXCL)) error("file.open");
+  if (!file.open(fileName, O_CREAT | O_WRITE | O_EXCL)) {
+    error("file.open");
+  }
   do {
     delay(10);
   } while (Serial.read() >= 0);
-  
+
   Serial.print(F("Logging to: "));
   Serial.println(fileName);
   Serial.println(F("Type any character to stop"));
-  
+
   // Write data header.
   writeHeader();
-  
+
   // Start on a multiple of the sample interval.
   logTime = micros()/(1000UL*SAMPLE_INTERVAL_MS) + 1;
   logTime *= 1000UL*SAMPLE_INTERVAL_MS;
@@ -114,21 +115,25 @@ void setup() {
 void loop() {
   // Time for next record.
   logTime += 1000UL*SAMPLE_INTERVAL_MS;
-  
+
   // Wait for log time.
   int32_t diff;
   do {
     diff = micros() - logTime;
   } while (diff < 0);
-  
-  // Check for data rate too high. 
-  if (diff > 10) error("Missed data record");
-  
+
+  // Check for data rate too high.
+  if (diff > 10) {
+    error("Missed data record");
+  }
+
   logData();
-  
+
   // Force data to SD and update the directory entry to avoid data loss.
-  if (!file.sync() || file.getWriteError()) error("write error"); 
-  
+  if (!file.sync() || file.getWriteError()) {
+    error("write error");
+  }
+
   if (Serial.available()) {
     // Close file and stop.
     file.close();

+ 55 - 44
SdFat/examples/directoryFunctions/directoryFunctions.ino

@@ -1,6 +1,7 @@
 /*
  * Example use of chdir(), ls(), mkdir(), and  rmdir().
  */
+#include <SPI.h>
 #include <SdFat.h>
 // SD card chip select pin.
 const uint8_t SD_CHIP_SELECT = SS;
@@ -24,32 +25,30 @@ char cinBuf[40];
 ArduinoInStream cin(Serial, cinBuf, sizeof(cinBuf));
 //==============================================================================
 // Error messages stored in flash.
-#define error(msg) error_P(PSTR(msg))
-//------------------------------------------------------------------------------
-void error_P(const char* msg) {
-  sd.errorHalt_P(msg);
-}
+#define error(msg) sd.errorHalt(F(msg))
 //------------------------------------------------------------------------------
 void setup() {
   Serial.begin(9600);
   while (!Serial) {} // wait for Leonardo
   delay(1000);
-  
-  cout << pstr("Type any character to start\n");
+
+  cout << F("Type any character to start\n");
   // Wait for input line and discard.
   cin.readline();
 
   // Initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
   // breadboards.  use SPI_FULL_SPEED for better performance.
-  if (!sd.begin(SD_CHIP_SELECT, SPI_HALF_SPEED)) sd.initErrorHalt();
-  
+  if (!sd.begin(SD_CHIP_SELECT, SPI_HALF_SPEED)) {
+    sd.initErrorHalt();
+  }
+
   // Check for empty SD.
   if (file.openNext(sd.vwd(), O_READ)) {
-    cout << pstr("Found files/folders in the root directory.\n");    
+    cout << F("Found files/folders in the root directory.\n");
     if (!ALLOW_WIPE) {
-      error("SD not empty, use a blank SD or set ALLOW_WIPE true.");  
+      error("SD not empty, use a blank SD or set ALLOW_WIPE true.");
     } else {
-      cout << pstr("Type: 'WIPE' to delete all SD files.\n");
+      cout << F("Type: 'WIPE' to delete all SD files.\n");
       char buf[10];
       cin.readline();
       cin.get(buf, sizeof(buf));
@@ -57,51 +56,63 @@ void setup() {
         error("Invalid WIPE input");
       }
       file.close();
-      sd.vwd()->rmRfStar();
-      cout << pstr("***SD wiped clean.***\n\n");
+      if (!sd.vwd()->rmRfStar()) {
+        error("wipe failed");
+      }
+      cout << F("***SD wiped clean.***\n\n");
     }
   }
-  
+
   // Create a new folder.
-  if (!sd.mkdir("FOLDER1")) error("Create FOLDER1 failed");
-  cout << pstr("Created FOLDER1\n");
-  
-  // Create a file in FOLDER1 using a path.
-  if (!file.open("FOLDER1/FILE1.TXT", O_CREAT | O_WRITE)) {
-    error("create FOLDER1/FILE1.TXT failed");
+  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_CREAT | O_WRITE)) {
+    error("create Folder1/file1.txt failed");
   }
   file.close();
-  cout << pstr("Created FOLDER1/FILE1.TXT\n");
-  
-  // Change volume working directory to FOLDER1.
-  if (!sd.chdir("FOLDER1")) error("chdir failed for FOLDER1.\n");
-  cout << pstr("chdir to FOLDER1\n");
-  
-  // Create FILE2.TXT in current directory.
-  if (!file.open("FILE2.TXT", O_CREAT | O_WRITE)) {
-    error("create FILE2.TXT failed");
+  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_CREAT | O_WRITE)) {
+    error("create File2.txt failed");
   }
   file.close();
-  cout << pstr("Created FILE2.TXT in current directory\n");
-  
-  cout << pstr("List of files on the SD.\n");
+  cout << F("Created File2.txt in current directory\n");
+
+  cout << F("List 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 << pstr("\nFILE1.TXT and FILE2.TXT removed.\n");
+  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 << pstr("List of files on the SD.\n");
+  if (!sd.chdir()) {
+    error("chdir to root failed.\n");
+  }
+
+  cout << F("List of files on the SD.\n");
   sd.ls(LS_R);
-  
-  // Remove FOLDER1.
-  if (!sd.rmdir("FOLDER1")) error("rmdir for FOLDER1 failed\n");
-  
-  cout << pstr("\nFOLDER1 removed, SD empty.\n");
-  cout << pstr("Done!\n");
+
+  // Remove Folder1.
+  if (!sd.rmdir("Folder1")) {
+    error("rmdir for Folder1 failed\n");
+  }
+
+  cout << F("\nFolder1 removed, SD empty.\n");
+  cout << F("Done!\n");
 }
 //------------------------------------------------------------------------------
 // Nothing happens in loop.

+ 35 - 28
SdFat/examples/fgets/fgets.ino

@@ -1,4 +1,5 @@
 // Demo of fgets function to read lines from a file.
+#include <SPI.h>
 #include <SdFat.h>
 
 // SD chip select pin
@@ -9,22 +10,24 @@ SdFat sd;
 ArduinoOutStream cout(Serial);
 //------------------------------------------------------------------------------
 // store error strings in flash memory
-#define error(s) sd.errorHalt_P(PSTR(s))
+#define error(s) sd.errorHalt(F(s))
 //------------------------------------------------------------------------------
 void demoFgets() {
   char line[25];
   int n;
   // open test file
-  SdFile rdfile("FGETS.TXT", O_READ);
-  
+  SdFile rdfile("fgets.txt", O_READ);
+
   // check for open error
-  if (!rdfile.isOpen()) error("demoFgets");
-  
-  cout << endl << pstr(
-    "Lines with '>' end with a '\\n' character\n"
-    "Lines with '#' do not end with a '\\n' character\n"
-    "\n");
-    
+  if (!rdfile.isOpen()) {
+    error("demoFgets");
+  }
+
+  cout << endl << F(
+         "Lines with '>' end with a '\\n' character\n"
+         "Lines with '#' do not end with a '\\n' character\n"
+         "\n");
+
   // read lines from the file
   while ((n = rdfile.fgets(line, sizeof(line))) > 0) {
     if (line[n - 1] == '\n') {
@@ -37,19 +40,21 @@ void demoFgets() {
 //------------------------------------------------------------------------------
 void makeTestFile() {
   // create or open test file
-  SdFile wrfile("FGETS.TXT", O_WRITE | O_CREAT | O_TRUNC);
-  
+  SdFile wrfile("fgets.txt", O_WRITE | O_CREAT | O_TRUNC);
+
   // check for open error
-  if (!wrfile.isOpen()) error("MakeTestFile");
-  
+  if (!wrfile.isOpen()) {
+    error("MakeTestFile");
+  }
+
   // write test file
-  wrfile.write_P(PSTR(
-    "Line with CRLF\r\n"
-    "Line with only LF\n"
-    "Long line that will require an extra read\n"
-    "\n"  // empty line
-    "Line at EOF without NL"
-  ));
+  wrfile.print(F(
+                 "Line with CRLF\r\n"
+                 "Line with only LF\n"
+                 "Long line that will require an extra read\n"
+                 "\n"  // empty line
+                 "Line at EOF without NL"
+               ));
   wrfile.close();
 }
 //------------------------------------------------------------------------------
@@ -57,18 +62,20 @@ void setup(void) {
   Serial.begin(9600);
   while (!Serial) {}  // Wait for Leonardo
 
-  cout << pstr("Type any character to start\n");
+  cout << F("Type any character to start\n");
   while (Serial.read() <= 0) {}
   delay(400);  // catch Due reset problem
-  
+
   // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
   // breadboards.  use SPI_FULL_SPEED for better performance.
-  if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt();
-  
+  if (!sd.begin(chipSelect, SPI_HALF_SPEED)) {
+    sd.initErrorHalt();
+  }
+
   makeTestFile();
-  
+
   demoFgets();
-  
-  cout << pstr("\nDone\n");
+
+  cout << F("\nDone\n");
 }
 void loop(void) {}

+ 7 - 4
SdFat/examples/formatting/formatting.ino

@@ -2,6 +2,7 @@
  * Print a table with various formatting options
  * Format dates
  */
+#include <SPI.h>
 #include <SdFat.h>
 
 // create Serial stream
@@ -14,7 +15,7 @@ void example(void) {
 
   for (int row = 1; row <= max; row++) {
     for (int col = 1; col <= max; col++) {
-     cout << setw(width) << row * col << (col == max ? '\n' : ' ');
+      cout << setw(width) << row * col << (col == max ? '\n' : ' ');
     }
   }
   cout << endl;
@@ -24,7 +25,9 @@ void example(void) {
 // shows how to set and restore the fill character
 void showDate(int m, int d, int y) {
   // convert two digit year
-  if (y < 100) y += 2000;
+  if (y < 100) {
+    y += 2000;
+  }
 
   // set new fill to '0' save old fill character
   char old = cout.fill('0');
@@ -38,10 +41,10 @@ void showDate(int m, int d, int y) {
 //------------------------------------------------------------------------------
 void setup(void) {
   Serial.begin(9600);
-  
+
   while (!Serial) {}  // wait for Leonardo
   delay(2000);
-  
+
   cout << endl << "default formatting" << endl;
   example();
 

+ 13 - 10
SdFat/examples/getline/getline.ino

@@ -6,6 +6,7 @@
  * Note: This example is meant to demonstrate subtleties the standard and
  * may not the best way to read a file.
  */
+#include <SPI.h>
 #include <SdFat.h>
 
 // SD chip select pin
@@ -18,14 +19,14 @@ SdFat sd;
 ArduinoOutStream cout(Serial);
 //------------------------------------------------------------------------------
 void makeTestFile() {
-  ofstream sdout("GETLINE.TXT");
+  ofstream sdout("getline.txt");
   // use flash for text to save RAM
-  sdout << pstr(
-    "short line\n"
-    "\n"
-    "17 character line\n"
-    "too long for buffer\n"
-    "line with no nl");
+  sdout << F(
+          "short line\n"
+          "\n"
+          "17 character line\n"
+          "too long for buffer\n"
+          "line with no nl");
 
   sdout.close();
 }
@@ -33,7 +34,7 @@ void makeTestFile() {
 void testGetline() {
   const int line_buffer_size = 18;
   char buffer[line_buffer_size];
-  ifstream sdin("GETLINE.TXT");
+  ifstream sdin("getline.txt");
   int line_number = 0;
 
   while (sdin.getline(buffer, line_buffer_size, '\n') || sdin.gcount()) {
@@ -56,13 +57,15 @@ void setup(void) {
   while (!Serial) {}  // wait for Leonardo
 
   // pstr stores strings in flash to save RAM
-  cout << pstr("Type any character to start\n");
+  cout << F("Type any character to start\n");
   while (Serial.read() <= 0) {}
   delay(400);  // catch Due reset problem
 
   // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
   // breadboards.  use SPI_FULL_SPEED for better performance.
-  if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt();
+  if (!sd.begin(chipSelect, SPI_HALF_SPEED)) {
+    sd.initErrorHalt();
+  }
 
   // make the test file
   makeTestFile();

+ 47 - 32
SdFat/examples/readCSV/readCSV.ino

@@ -1,7 +1,8 @@
 /*
  *  This example reads a simple CSV, comma-separated values, file.
- *  Each line of the file has three values, a long and two floats.
+ *  Each line of the file has a label and three values, a long and two floats.
  */
+#include <SPI.h>
 #include <SdFat.h>
 
 // SD chip select pin
@@ -13,10 +14,10 @@ SdFat sd;
 // create Serial stream
 ArduinoOutStream cout(Serial);
 
-char fileName[] = "TESTFILE.CSV";
+char fileName[] = "testfile.csv";
 //------------------------------------------------------------------------------
 // store error strings in flash to save RAM
-#define error(s) sd.errorHalt_P(PSTR(s))
+#define error(s) sd.errorHalt(F(s))
 //------------------------------------------------------------------------------
 // read and print CSV test file
 void readFile() {
@@ -24,37 +25,47 @@ void readFile() {
   float f1, f2;
   char text[10];
   char c1, c2, c3;  // space for commas.
-  
+
   // open input file
   ifstream sdin(fileName);
-  
+
   // check for open error
-  if (!sdin.is_open()) error("open");
-  
+  if (!sdin.is_open()) {
+    error("open");
+  }
+
   // read until input fails
   while (1) {
     // Get text field.
     sdin.get(text, sizeof(text), ',');
-    
+
     // Assume EOF if fail.
-    if (sdin.fail()) break;
-    
+    if (sdin.fail()) {
+      break;
+    }
+
     // Get commas and numbers.
     sdin >> c1 >> lg >> c2 >> f1 >> c3 >> f2;
-    
+
     // Skip CR/LF.
     sdin.skipWhite();
-    
-    if (sdin.fail()) error("bad input");
-    
+
+    if (sdin.fail()) {
+      error("bad input");
+    }
+
     // error in line if not commas
-    if (c1 != ',' || c2 != ',' || c3 != ',') error("comma");
-    
+    if (c1 != ',' || c2 != ',' || c3 != ',') {
+      error("comma");
+    }
+
     // print in six character wide columns
     cout << text << setw(6) << lg << setw(6) << f1 << setw(6) << f2 << endl;
   }
   // Error in an input line if file is not at EOF.
-  if (!sdin.eof()) error("readFile");
+  if (!sdin.eof()) {
+    error("readFile");
+  }
 }
 //------------------------------------------------------------------------------
 // write test file
@@ -62,39 +73,43 @@ void writeFile() {
 
   // create or open and truncate output file
   ofstream sdout(fileName);
-  
+
   // write file from string stored in flash
-  sdout << pstr(
-    "Line 1,1,2.3,4.5\n"
-    "Line 2,6,7.8,9.0\n"
-    "Line 3,9,8.7,6.5\n"
-    "Line 4,-4,-3.2,-1\n") << flush;
+  sdout << F(
+          "Line 1,1,2.3,4.5\n"
+          "Line 2,6,7.8,9.0\n"
+          "Line 3,9,8.7,6.5\n"
+          "Line 4,-4,-3.2,-1\n") << flush;
 
   // check for any errors
-  if (!sdout) error("writeFile");
-  
+  if (!sdout) {
+    error("writeFile");
+  }
+
   sdout.close();
 }
 //------------------------------------------------------------------------------
 void setup() {
   Serial.begin(9600);
   while (!Serial) {} // wait for Leonardo
-  cout << pstr("Type any character to start\n");
+  cout << F("Type any character to start\n");
   while (Serial.read() <= 0) {}
   delay(400);  // catch Due reset problem
-  
+
   // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
   // breadboards.  use SPI_FULL_SPEED for better performance
-  if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt();
-  
+  if (!sd.begin(chipSelect, SPI_HALF_SPEED)) {
+    sd.initErrorHalt();
+  }
+
   // create test file
   writeFile();
-  
+
   cout << endl;
 
   // read and print test
-  readFile();  
-  
+  readFile();
+
   cout << "\nDone!" << endl;
 }
 void loop() {}

+ 51 - 26
SdFat/examples/rename/rename.ino

@@ -1,7 +1,8 @@
 /*
- * This sketch demonstrates use of SdFile::rename() 
+ * This program demonstrates use of SdFile::rename()
  * and SdFat::rename().
  */
+#include <SPI.h>
 #include <SdFat.h>
 
 // SD chip select pin
@@ -14,64 +15,88 @@ SdFat sd;
 ArduinoOutStream cout(Serial);
 //------------------------------------------------------------------------------
 // store error strings in flash to save RAM
-#define error(s) sd.errorHalt_P(PSTR(s))
+#define error(s) sd.errorHalt(F(s))
 //------------------------------------------------------------------------------
 void setup() {
   Serial.begin(9600);
   while (!Serial) {}  // wait for Leonardo
 
-  cout << pstr("Insert an empty SD.  Type any character to start.") << endl;
+  cout << F("Insert an empty SD.  Type any character to start.") << endl;
   while (Serial.read() <= 0) {}
   delay(400);  // catch Due reset problem
 
   // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
   // breadboards.  use SPI_FULL_SPEED for better performance.
-  if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt();
+  if (!sd.begin(chipSelect, SPI_HALF_SPEED)) {
+    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_WRITE | O_CREAT);
-  if (!file.isOpen()) error("NAME1");
-  file.println("A test line for NAME1.TXT");
+  SdFile file("Name1.txt", O_WRITE | 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.
+  // rename the file name2.txt and add a line.
   // sd.vwd() is the volume working directory, root.
-  if (!file.rename(sd.vwd(), "NAME2.TXT")) error("NAME2");
-  file.println("A test line for NAME2.TXT");
+  if (!file.rename(sd.vwd(), "name2.txt")) {
+    error("name2.txt");
+  }
+  file.println("A test line for name2.txt");
 
   // list files
-  cout << pstr("------") << endl;
+  cout << F("------") << endl;
   sd.ls(LS_R);
 
-  // make a new directory - "DIR1"
-  if (!sd.mkdir("DIR1")) error("DIR1");
+  // 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(sd.vwd(), "DIR1/NAME3.TXT")) error("NAME3");
-  file.println("A line for DIR1/NAME3.TXT");
+  // move file into Dir1, rename it NAME3.txt and add a line
+  if (!file.rename(sd.vwd(), "Dir1/NAME3.txt")) {
+    error("NAME3.txt");
+  }
+  file.println("A line for Dir1/NAME3.txt");
 
   // list files
-  cout << pstr("------") << endl;
+  cout << F("------") << endl;
   sd.ls(LS_R);
 
-  // make directory "DIR2"
-  if (!sd.mkdir("DIR2")) error("DIR2");
+  // 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");
+  // 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_WRITE | O_APPEND)) {
-    error("DIR2/DIR3/NAME3.TXT");
+  if (!file.open("dir2/DIR3/NAME3.txt", O_WRITE | O_APPEND)) {
+    error("dir2/DIR3/NAME3.txt");
   }
-  file.println("A line for DIR2/DIR3/NAME3.TXT");
+  file.println("A line for dir2/DIR3/NAME3.txt");
+  file.close();
 
   // list files
-  cout << pstr("------") << endl;
+  cout << F("------") << endl;
   sd.ls(LS_R);
 
-  cout << pstr("Done") << endl;
+  cout << F("Done") << endl;
 }
 void loop() {}

+ 248 - 0
SdFat/utility/ArduinoFiles.h

@@ -0,0 +1,248 @@
+/* Arduino SdFat Library
+ * Copyright (C) 2012 by William Greiman
+ *
+ * This file is part of the Arduino SdFat Library
+ *
+ * This Library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the Arduino SdFat Library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+/**
+ * \file
+ * \brief PrintFile class
+ */
+#ifndef ArduinoFiles_h
+#define ArduinoFiles_h
+#include "FatLibConfig.h"
+#if ENABLE_ARDUINO_FEATURES
+#include "FatFile.h"
+#include <limits.h>
+//------------------------------------------------------------------------------
+/** Arduino SD.h style flag for open for read. */
+#define FILE_READ O_READ
+/** Arduino SD.h style flag for open at EOF for read/write with create. */
+#define FILE_WRITE (O_RDWR | O_CREAT | O_AT_END)
+//==============================================================================
+/**
+ * \class PrintFile
+ * \brief FatFile with Print.
+ */
+class PrintFile : public FatFile, public Print {
+ public:
+  PrintFile() {}
+  /**  Create a file object and open it in the current working directory.
+   *
+   * \param[in] path A path for a file to be opened.
+   *
+   * \param[in] oflag Values for \a oflag are constructed by a
+   * bitwise-inclusive OR of open flags. see
+   * FatFile::open(FatFile*, const char*, uint8_t).
+   */
+  PrintFile(const char* path, uint8_t oflag) : FatFile(path, oflag) {}
+#if DESTRUCTOR_CLOSES_FILE
+  ~PrintFile() {}
+#endif  // DESTRUCTOR_CLOSES_FILE
+  using FatFile::clearWriteError;
+  using FatFile::getWriteError;
+  using FatFile::read;
+  using FatFile::write;
+  /** \return number of bytes available from the current position to EOF
+   *   or INT_MAX if more than INT_MAX bytes are available.
+   */
+  int available() {
+    uint32_t n = FatFile::available();
+    return n > INT_MAX ? INT_MAX : n;
+  }
+  /** Ensure that any bytes written to the file are saved to the SD card. */
+  void flush() {
+    FatFile::sync();
+  }
+  /** Return the next available byte without consuming it.
+   *
+   * \return The byte if no error and not at eof else -1;
+   */
+  int peek() {
+    return FatFile::peek();
+  }
+  /** Read the next byte from a file.
+   *
+   * \return For success return the next byte in the file as an int.
+   * If an error occurs or end of file is reached return -1.
+   */
+//  int read() {
+//    return FatFile::read();
+//  }
+  /** Write a byte to a file. Required by the Arduino Print class.
+   * \param[in] b the byte to be written.
+   * Use getWriteError to check for errors.
+   * \return 1 for success and 0 for failure.
+   */
+  size_t write(uint8_t b) {
+    return FatFile::write(b);
+  }
+  /** Write data to an open file.  Form required by Print.
+   *
+   * \note Data is moved to the cache but may not be written to the
+   * storage device until sync() is called.
+   *
+   * \param[in] buf Pointer to the location of the data to be written.
+   *
+   * \param[in] size Number of bytes to write.
+   *
+   * \return For success write() returns the number of bytes written, always
+   * \a nbyte.  If an error occurs, write() returns -1.  Possible errors
+   * include write() is called before a file has been opened, write is called
+   * for a read-only file, device is full, a corrupt file system or an
+   * I/O error.
+   */
+  size_t write(const uint8_t *buf, size_t size) {
+    return FatFile::write(buf, size);
+  }
+};
+//==============================================================================
+/**
+ * \class File
+ * \brief Arduino SD.h style File API
+ */
+#if ARDUINO_FILE_USES_STREAM
+class File : public FatFile, public Stream {
+#else  // ARDUINO_FILE_USES_STREAM
+class File : public FatFile, public Print {
+#endif  // ARDUINO_FILE_USES_STREAM
+ public:
+  File() {}
+  /**  Create a file object and open it in the current working directory.
+   *
+   * \param[in] path A path with a valid 8.3 DOS name for a file to be opened.
+   *
+   * \param[in] oflag Values for \a oflag are constructed by a
+   * bitwise-inclusive OR of open flags. see
+   * FatFile::open(FatFile*, const char*, uint8_t).
+   */
+  File(const char* path, uint8_t oflag) {
+    open(path, oflag);
+  }
+  using FatFile::clearWriteError;
+  using FatFile::getWriteError;
+  using FatFile::read;
+  using FatFile::write;
+  /** The parenthesis operator.
+    *
+    * \return true if a file is open.
+    */
+  operator bool() {
+    return isOpen();
+  }
+  /** \return number of bytes available from the current position to EOF
+   *   or INT_MAX if more than INT_MAX bytes are available.
+   */
+  int available() {
+    uint32_t n = FatFile::available();
+    return n > INT_MAX ? INT_MAX : n;
+  }
+  /** Ensure that any bytes written to the file are saved to the SD card. */
+  void flush() {
+    FatFile::sync();
+  }
+  /** This function reports if the current file is a directory or not.
+  * \return true if the file is a directory.
+  */
+  bool isDirectory() {
+    return isDir();
+  }
+  /** No longer implemented due to Long File Names.
+   *
+   * Use getName(char* name, size_t size).
+   * \return a pointer to replacement suggestion. 
+   */
+  const char* name() const {
+    return "use getName()";
+  }
+  /** Return the next available byte without consuming it.
+   *
+   * \return The byte if no error and not at eof else -1;
+   */
+  int peek() {
+    return FatFile::peek();
+  }
+  /** \return the current file position. */
+  uint32_t position() {
+    return curPosition();
+  }
+  /** Opens the next file or folder in a directory.
+   *
+   * \param[in] mode open mode flags.
+   * \return a File object.
+   */
+  File openNextFile(uint8_t mode = O_READ) {
+    File tmpFile;
+    tmpFile.openNext(this, mode);
+    return tmpFile;
+  }
+  /** Read the next byte from a file.
+   *
+   * \return For success return the next byte in the file as an int.
+   * If an error occurs or end of file is reached return -1.
+   */
+  int read() {
+    return FatFile::read();
+  }
+  /** Rewind a file if it is a directory */
+  void rewindDirectory() {
+    if (isDir()) {
+      rewind();
+    }
+  }
+  /**
+   * Seek to a new position in the file, which must be between
+   * 0 and the size of the file (inclusive).
+   *
+   * \param[in] pos the new file position.
+   * \return true for success else false.
+   */
+  bool seek(uint32_t pos) {
+    return seekSet(pos);
+  }
+  /** \return the file's size. */
+  uint32_t size() {
+    return fileSize();
+  }
+  /** Write a byte to a file. Required by the Arduino Print class.
+   * \param[in] b the byte to be written.
+   * Use getWriteError to check for errors.
+   * \return 1 for success and 0 for failure.
+   */
+  size_t write(uint8_t b) {
+    return FatFile::write(b);
+  }
+  /** Write data to an open file.  Form required by Print.
+   *
+   * \note Data is moved to the cache but may not be written to the
+   * storage device until sync() is called.
+   *
+   * \param[in] buf Pointer to the location of the data to be written.
+   *
+   * \param[in] size Number of bytes to write.
+   *
+   * \return For success write() returns the number of bytes written, always
+   * \a nbyte.  If an error occurs, write() returns -1.  Possible errors
+   * include write() is called before a file has been opened, write is called
+   * for a read-only file, device is full, a corrupt file system or an
+   * I/O error.
+   */
+  size_t write(const uint8_t *buf, size_t size) {
+    return FatFile::write(buf, size);
+  }
+};
+#endif  // ENABLE_ARDUINO_FEATURES
+#endif  // ArduinoFiles_h

+ 41 - 19
SdFat/ArduinoStream.h → SdFat/utility/ArduinoStream.h

@@ -1,7 +1,7 @@
-/* Arduino SdFat Library
- * Copyright (C) 2012 by William Greiman
+/* FatLib Library
+ * Copyright (C) 2013 by William Greiman
  *
- * This file is part of the Arduino SdFat Library
+ * This file is part of the FatLib Library
  *
  * This Library is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -14,7 +14,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with the Arduino SdFat Library.  If not, see
+ * along with the FatLib Library.  If not, see
  * <http://www.gnu.org/licenses/>.
  */
 #ifndef ArduinoStream_h
@@ -23,7 +23,10 @@
  * \file
  * \brief ArduinoInStream and ArduinoOutStream classes
  */
-#include <bufstream.h>
+#include "FatLibConfig.h"
+#if ENABLE_ARDUINO_FEATURES
+#include <Arduino.h>
+#include "bufstream.h"
 //==============================================================================
 /**
  * \class ArduinoInStream
@@ -52,7 +55,9 @@ class ArduinoInStream : public ibufstream {
     while (1) {
       t = millis();
       while (!m_hw->available()) {
-        if ((millis() - t) > 10) goto done;
+        if ((millis() - t) > 10) {
+          goto done;
+        }
       }
       if (i >= (m_size - 1)) {
         setstate(failbit);
@@ -61,7 +66,7 @@ class ArduinoInStream : public ibufstream {
       m_line[i++] = m_hw->read();
       m_line[i] = '\0';
     }
-  done:
+done:
     init(m_line);
   }
 
@@ -71,12 +76,16 @@ class ArduinoInStream : public ibufstream {
    * \param[in] way
    * \return true/false.
    */
-  bool seekoff(off_type off, seekdir way) {return false;}
- /** Internal - do not use.
-  * \param[in] pos
-  * \return true/false.
-  */
-  bool seekpos(pos_type pos) {return false;}
+  bool seekoff(off_type off, seekdir way) {
+    return false;
+  }
+  /** Internal - do not use.
+   * \param[in] pos
+   * \return true/false.
+   */
+  bool seekpos(pos_type pos) {
+    return false;
+  }
 
  private:
   char *m_line;
@@ -103,17 +112,30 @@ class ArduinoOutStream : public ostream {
    * \param[in] c
    */
   void putch(char c) {
-    if (c == '\n') m_pr->write('\r');
+    if (c == '\n') {
+      m_pr->write('\r');
+    }
     m_pr->write(c);
   }
-  void putstr(const char* str) {m_pr->write(str);}
-  bool seekoff(off_type off, seekdir way) {return false;}
-  bool seekpos(pos_type pos) {return false;}
-  bool sync() {return true;}
-  pos_type tellpos() {return 0;}
+  void putstr(const char* str) {
+    m_pr->write(str);
+  }
+  bool seekoff(off_type off, seekdir way) {
+    return false;
+  }
+  bool seekpos(pos_type pos) {
+    return false;
+  }
+  bool sync() {
+    return true;
+  }
+  pos_type tellpos() {
+    return 0;
+  }
   /// @endcond
  private:
   ArduinoOutStream() {}
   Print* m_pr;
 };
+#endif  // ENABLE_ARDUINO_FEATURES
 #endif  // ArduinoStream_h

+ 82 - 40
SdFat/utility/DigitalPin.h

@@ -46,11 +46,11 @@ bool fastDigitalRead(uint8_t pin) {
  */
 static inline __attribute__((always_inline))
 void fastDigitalWrite(uint8_t pin, bool value) {
-	if (value) {
-		*portSetRegister(pin) = 1;
-	} else {
-		*portClearRegister(pin) = 1;
-	}
+  if (value) {
+    *portSetRegister(pin) = 1;
+  } else {
+    *portClearRegister(pin) = 1;
+  }
 }
 #else  // CORE_TEENSY
 //------------------------------------------------------------------------------
@@ -59,7 +59,7 @@ void fastDigitalWrite(uint8_t pin, bool value) {
  * @return value read
  */
 static inline __attribute__((always_inline))
-bool fastDigitalRead(uint8_t pin){
+bool fastDigitalRead(uint8_t pin) {
   return g_APinDescription[pin].pPort->PIO_PDSR & g_APinDescription[pin].ulPin;
 }
 //------------------------------------------------------------------------------
@@ -68,8 +68,8 @@ bool fastDigitalRead(uint8_t pin){
  * @param[in] level value to write
  */
 static inline __attribute__((always_inline))
-void fastDigitalWrite(uint8_t pin, bool value){
-  if(value) {
+void fastDigitalWrite(uint8_t pin, bool value) {
+  if (value) {
     g_APinDescription[pin].pPort->PIO_SODR = g_APinDescription[pin].ulPin;
   } else {
     g_APinDescription[pin].pPort->PIO_CODR = g_APinDescription[pin].ulPin;
@@ -78,10 +78,12 @@ void fastDigitalWrite(uint8_t pin, bool value){
 #endif  // CORE_TEENSY
 //------------------------------------------------------------------------------
 inline void fastDigitalToggle(uint8_t pin) {
- fastDigitalWrite(pin, !fastDigitalRead(pin));
- }
+  fastDigitalWrite(pin, !fastDigitalRead(pin));
+}
 //------------------------------------------------------------------------------
-inline void fastPinMode(uint8_t pin, bool mode) {pinMode(pin, mode);}
+inline void fastPinMode(uint8_t pin, bool mode) {
+  pinMode(pin, mode);
+}
 #else  // __arm__
 #include <avr/io.h>
 #include <util/atomic.h>
@@ -101,7 +103,7 @@ struct pin_map_t {
 ||defined(__AVR_ATmega168P__)\
 ||defined(__AVR_ATmega328P__)
 // 168 and 328 Arduinos
-const static pin_map_t pinMap[] = {
+static const pin_map_t pinMap[] = {
   {&DDRD, &PIND, &PORTD, 0},  // D0  0
   {&DDRD, &PIND, &PORTD, 1},  // D1  1
   {&DDRD, &PIND, &PORTD, 2},  // D2  2
@@ -209,7 +211,43 @@ static const pin_map_t pinMap[] = {
 || defined(__AVR_ATmega324__)\
 || defined(__AVR_ATmega16__)
 
-#ifdef defined(VARIANT_MIGHTY)
+#ifdef PORT_D0
+// Newer version of 1284P
+ static const pin_map_t pinMap[] = { 
+  {PORT_TO_MODE(PORT_D0), PORT_TO_INPUT(PORT_D0), PORT_TO_OUTPUT(PORT_D0),  BIT_D0},
+  {PORT_TO_MODE(PORT_D1), PORT_TO_INPUT(PORT_D1), PORT_TO_OUTPUT(PORT_D1),  BIT_D1},
+  {PORT_TO_MODE(PORT_D2), PORT_TO_INPUT(PORT_D2), PORT_TO_OUTPUT(PORT_D2),  BIT_D2},
+  {PORT_TO_MODE(PORT_D3), PORT_TO_INPUT(PORT_D3), PORT_TO_OUTPUT(PORT_D3),  BIT_D3},
+  {PORT_TO_MODE(PORT_D4), PORT_TO_INPUT(PORT_D4), PORT_TO_OUTPUT(PORT_D4),  BIT_D4},
+  {PORT_TO_MODE(PORT_D5), PORT_TO_INPUT(PORT_D5), PORT_TO_OUTPUT(PORT_D5),  BIT_D5},
+  {PORT_TO_MODE(PORT_D6), PORT_TO_INPUT(PORT_D6), PORT_TO_OUTPUT(PORT_D6),  BIT_D6},
+  {PORT_TO_MODE(PORT_D7), PORT_TO_INPUT(PORT_D7), PORT_TO_OUTPUT(PORT_D7),  BIT_D7},
+  {PORT_TO_MODE(PORT_D8), PORT_TO_INPUT(PORT_D8), PORT_TO_OUTPUT(PORT_D8),  BIT_D8},
+  {PORT_TO_MODE(PORT_D9), PORT_TO_INPUT(PORT_D9), PORT_TO_OUTPUT(PORT_D9),  BIT_D9},
+  {PORT_TO_MODE(PORT_D10), PORT_TO_INPUT(PORT_D10), PORT_TO_OUTPUT(PORT_D10),  BIT_D10},
+  {PORT_TO_MODE(PORT_D11), PORT_TO_INPUT(PORT_D11), PORT_TO_OUTPUT(PORT_D11),  BIT_D11},
+  {PORT_TO_MODE(PORT_D12), PORT_TO_INPUT(PORT_D12), PORT_TO_OUTPUT(PORT_D12),  BIT_D12},
+  {PORT_TO_MODE(PORT_D13), PORT_TO_INPUT(PORT_D13), PORT_TO_OUTPUT(PORT_D13),  BIT_D13},
+  {PORT_TO_MODE(PORT_D14), PORT_TO_INPUT(PORT_D14), PORT_TO_OUTPUT(PORT_D14),  BIT_D14},
+  {PORT_TO_MODE(PORT_D15), PORT_TO_INPUT(PORT_D15), PORT_TO_OUTPUT(PORT_D15),  BIT_D15},
+  {PORT_TO_MODE(PORT_D16), PORT_TO_INPUT(PORT_D16), PORT_TO_OUTPUT(PORT_D16),  BIT_D16},
+  {PORT_TO_MODE(PORT_D17), PORT_TO_INPUT(PORT_D17), PORT_TO_OUTPUT(PORT_D17),  BIT_D17},
+  {PORT_TO_MODE(PORT_D18), PORT_TO_INPUT(PORT_D18), PORT_TO_OUTPUT(PORT_D18),  BIT_D18},
+  {PORT_TO_MODE(PORT_D19), PORT_TO_INPUT(PORT_D19), PORT_TO_OUTPUT(PORT_D19),  BIT_D19},
+  {PORT_TO_MODE(PORT_D20), PORT_TO_INPUT(PORT_D20), PORT_TO_OUTPUT(PORT_D20),  BIT_D20},
+  {PORT_TO_MODE(PORT_D21), PORT_TO_INPUT(PORT_D21), PORT_TO_OUTPUT(PORT_D21),  BIT_D21},
+  {PORT_TO_MODE(PORT_D22), PORT_TO_INPUT(PORT_D22), PORT_TO_OUTPUT(PORT_D22),  BIT_D22},
+  {PORT_TO_MODE(PORT_D23), PORT_TO_INPUT(PORT_D23), PORT_TO_OUTPUT(PORT_D23),  BIT_D23},
+  {PORT_TO_MODE(PORT_D24), PORT_TO_INPUT(PORT_D24), PORT_TO_OUTPUT(PORT_D24),  BIT_D24},
+  {PORT_TO_MODE(PORT_D25), PORT_TO_INPUT(PORT_D25), PORT_TO_OUTPUT(PORT_D25),  BIT_D25},
+  {PORT_TO_MODE(PORT_D26), PORT_TO_INPUT(PORT_D26), PORT_TO_OUTPUT(PORT_D26),  BIT_D26},
+  {PORT_TO_MODE(PORT_D27), PORT_TO_INPUT(PORT_D27), PORT_TO_OUTPUT(PORT_D27),  BIT_D27},
+  {PORT_TO_MODE(PORT_D28), PORT_TO_INPUT(PORT_D28), PORT_TO_OUTPUT(PORT_D28),  BIT_D28},
+  {PORT_TO_MODE(PORT_D29), PORT_TO_INPUT(PORT_D29), PORT_TO_OUTPUT(PORT_D29),  BIT_D29},
+  {PORT_TO_MODE(PORT_D30), PORT_TO_INPUT(PORT_D30), PORT_TO_OUTPUT(PORT_D30),  BIT_D30},
+  {PORT_TO_MODE(PORT_D31), PORT_TO_INPUT(PORT_D31), PORT_TO_OUTPUT(PORT_D31),  BIT_D31}
+};
+#elif analogInputToDigitalPin(0)==24
 // Mighty Layout
 static const pin_map_t pinMap[] = {
   {&DDRB, &PINB, &PORTB, 0},  // B0  0
@@ -245,7 +283,7 @@ static const pin_map_t pinMap[] = {
   {&DDRA, &PINA, &PORTA, 6},  // A6 30
   {&DDRA, &PINA, &PORTA, 7}   // A7 31
 };
-#elif defined(VARIANT_BOBUINO)
+#elif analogInputToDigitalPin(0)==21
 // Bobuino Layout
 static const pin_map_t pinMap[] = {
   {&DDRD, &PIND, &PORTD, 0},  // D0  0
@@ -281,7 +319,7 @@ static const pin_map_t pinMap[] = {
   {&DDRD, &PIND, &PORTD, 4},  // D4 30
   {&DDRD, &PIND, &PORTD, 7}   // D7 31
 };
-#elif defined(VARIANT_STANDARD)
+#elif analogInputToDigitalPin(0)==31
 // Standard Layout
 static const pin_map_t pinMap[] = {
   {&DDRB, &PINB, &PORTB, 0},  // B0  0
@@ -449,7 +487,7 @@ static const uint8_t digitalPinCount = sizeof(pinMap)/sizeof(pin_map_t);
 //==============================================================================
 /** generate bad pin number error */
 void badPinNumber(void)
-  __attribute__((error("Pin number is too large or not a constant")));
+__attribute__((error("Pin number is too large or not a constant")));
 //------------------------------------------------------------------------------
 /** Check for valid pin number
  * @param[in] pin Number of pin to be checked.
@@ -457,7 +495,7 @@ void badPinNumber(void)
 static inline __attribute__((always_inline))
 void badPinCheck(uint8_t pin) {
   if (!__builtin_constant_p(pin) || pin >= digitalPinCount) {
-     badPinNumber();
+    badPinNumber();
   }
 }
 //------------------------------------------------------------------------------
@@ -469,7 +507,7 @@ void badPinCheck(uint8_t pin) {
 static inline __attribute__((always_inline))
 void fastBitWriteSafe(volatile uint8_t* address, uint8_t bit, bool level) {
   uint8_t oldSREG;
-  if (address > (uint8_t*)0X5F) {
+  if (address > reinterpret_cast<uint8_t*>(0X5F)) {
     oldSREG = SREG;
     cli();
   }
@@ -478,7 +516,7 @@ void fastBitWriteSafe(volatile uint8_t* address, uint8_t bit, bool level) {
   } else {
     *address &= ~(1 << bit);
   }
-  if (address > (uint8_t*)0X5F) {
+  if (address > reinterpret_cast<uint8_t*>(0X5F)) {
     SREG = oldSREG;
   }
 }
@@ -497,18 +535,18 @@ bool fastDigitalRead(uint8_t pin) {
  * @param[in] pin Arduino pin number
  *
  * If the pin is in output mode toggle the pin level.
- * If the pin is in input mode toggle the state of the 20K pullup.
+ * If the pin is in input mode toggle the state of the 20K pull-up.
  */
 static inline __attribute__((always_inline))
 void fastDigitalToggle(uint8_t pin) {
   badPinCheck(pin);
-    if (pinMap[pin].pin > (uint8_t*)0X5F) {
-      // must write bit to high address port
-      *pinMap[pin].pin = 1 << pinMap[pin].bit;
-    } else {
-      // will compile to sbi and PIN register will not be read.
-      *pinMap[pin].pin |= 1 << pinMap[pin].bit;
-    }
+  if (pinMap[pin].pin > reinterpret_cast<uint8_t*>(0X5F)) {
+    // must write bit to high address port
+    *pinMap[pin].pin = 1 << pinMap[pin].bit;
+  } else {
+    // will compile to sbi and PIN register will not be read.
+    *pinMap[pin].pin |= 1 << pinMap[pin].bit;
+  }
 }
 //------------------------------------------------------------------------------
 /** Set pin value
@@ -525,7 +563,7 @@ void fastDigitalWrite(uint8_t pin, bool level) {
  * @param[in] pin Arduino pin number
  * @param[in] mode if true set output mode else input mode
  *
- * fastPinMode does not enable or disable the 20K pullup for input mode.
+ * fastPinMode does not enable or disable the 20K pull-up for input mode.
  */
 static inline __attribute__((always_inline))
 void fastPinMode(uint8_t pin, bool mode) {
@@ -539,7 +577,7 @@ void fastPinMode(uint8_t pin, bool mode) {
  * @param[in] pin Arduino pin number
  * @param[in] mode If true set output mode else input mode
  * @param[in] level If mode is output, set level high/low.
- *                  If mode is input, enable or disable the pin's 20K pullup.
+ *                  If mode is input, enable or disable the pin's 20K pull-up.
  */
 static inline __attribute__((always_inline))
 void fastPinConfig(uint8_t pin, bool mode, bool level) {
@@ -568,7 +606,7 @@ class DigitalPin {
   /** Constructor
    * @param[in] mode If true set output mode else input mode
    * @param[in] level If mode is output, set level high/low.
-   *                  If mode is input, enable or disable the pin's 20K pullup.
+   *                  If mode is input, enable or disable the pin's 20K pull-up.
    */
   DigitalPin(bool mode, bool level) {
     config(mode, level);
@@ -588,14 +626,14 @@ class DigitalPin {
   /** Parenthesis operator
    * @return Pin's level
    */
-	inline operator bool () const __attribute__((always_inline)) {
+  inline operator bool() const __attribute__((always_inline)) {
     return read();
   }
   //----------------------------------------------------------------------------
   /** set pin configuration
    * @param[in] mode If true set output mode else input mode
-   * @param[in] level If mode is output, set level high/low.
-   *                  If mode is input, enable or disable the pin's 20K pullup.
+   * @param[in] level If mode is output, set level high/low.  If mode
+   *                  is input, enable or disable the pin's 20K pull-up.
    */
   inline __attribute__((always_inline))
   void config(bool mode, bool level) {
@@ -603,22 +641,26 @@ class DigitalPin {
   }
   //----------------------------------------------------------------------------
   /**
-   * Set pin level high if output mode or enable 20K pullup if input mode.
+   * Set pin level high if output mode or enable 20K pull-up if input mode.
    */
   inline __attribute__((always_inline))
-  void high() {write(true);}
+  void high() {
+    write(true);
+  }
   //----------------------------------------------------------------------------
   /**
-   * Set pin level low if output mode or disable 20K pullup if input mode.
+   * Set pin level low if output mode or disable 20K pull-up if input mode.
    */
   inline __attribute__((always_inline))
-  void low() {write(false);}
+  void low() {
+    write(false);
+  }
   //----------------------------------------------------------------------------
   /**
    * Set pin mode
    * @param[in] pinMode if true set output mode else input mode.
    *
-   * mode() does not enable or disable the 20K pullup for input mode.
+   * mode() does not enable or disable the 20K pull-up for input mode.
    */
   inline __attribute__((always_inline))
   void mode(bool pinMode) {
@@ -634,7 +676,7 @@ class DigitalPin {
   /** toggle a pin
    *
    * If the pin is in output mode toggle the pin's level.
-   * If the pin is in input mode toggle the state of the 20K pullup.
+   * If the pin is in input mode toggle the state of the 20K pull-up.
    */
   inline __attribute__((always_inline))
   void toggle() {
@@ -651,4 +693,4 @@ class DigitalPin {
   }
 };
 #endif  // DigitalPin_h
-/** @} */
+/** @} */

+ 8 - 6
SdFat/utility/FatApiConstants.h

@@ -46,14 +46,16 @@ uint8_t const O_CREAT = 0X40;
 /** If O_CREAT and O_EXCL are set, open() shall fail if the file exists */
 uint8_t const O_EXCL = 0X80;
 
-// SdBaseFile class static and const definitions
+// FatFile class static and const definitions
 // flags for ls()
-/** ls() flag to print modify date */
-uint8_t const LS_DATE = 1;
-/** ls() flag to print file size */
-uint8_t const LS_SIZE = 2;
+/** ls() flag for list all files including hidden. */
+uint8_t const LS_A = 1;
+/** ls() flag to print modify. date */
+uint8_t const LS_DATE = 2;
+/** ls() flag to print file size. */
+uint8_t const LS_SIZE = 4;
 /** ls() flag for recursive list of subdirectories */
-uint8_t const LS_R = 4;
+uint8_t const LS_R = 8;
 
 // flags for timestamp
 /** set the file's last access date */

+ 1494 - 0
SdFat/utility/FatFile.cpp

@@ -0,0 +1,1494 @@
+/* FatLib Library
+ * Copyright (C) 2012 by William Greiman
+ *
+ * This file is part of the FatLib Library
+ *
+ * This Library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the FatLib Library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+#include "FatFile.h"
+#include "FatFileSystem.h"
+//------------------------------------------------------------------------------
+// Pointer to cwd directory.
+FatFile* FatFile::m_cwd = 0;
+// Callback function for date/time.
+void (*FatFile::m_dateTime)(uint16_t* date, uint16_t* time) = 0;
+//------------------------------------------------------------------------------
+// Add a cluster to a file.
+bool FatFile::addCluster() {
+  m_flags |= F_FILE_DIR_DIRTY;
+  return m_vol->allocateCluster(m_curCluster, &m_curCluster);
+}
+//------------------------------------------------------------------------------
+// Add a cluster to a directory file and zero the cluster.
+// Return with first block of cluster in the cache.
+bool FatFile::addDirCluster() {
+  uint32_t block;
+  cache_t* pc;
+
+  if (isRootFixed()) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // max folder size
+  if (m_curPosition >= 512UL*4095) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  if (!addCluster()) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  block = m_vol->clusterStartBlock(m_curCluster);
+  pc = m_vol->cacheFetchData(block, FatCache::CACHE_RESERVE_FOR_WRITE);
+  if (!pc) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  memset(pc, 0, 512);
+  // zero rest of clusters
+  for (uint8_t i = 1; i < m_vol->blocksPerCluster(); i++) {
+    if (!m_vol->writeBlock(block + i, pc->data)) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+  }
+  // Set position to EOF to avoid inconsistent curCluster/curPosition.
+  m_curPosition += 512UL*m_vol->blocksPerCluster();
+  return true;
+
+fail:
+  return false;
+}
+//------------------------------------------------------------------------------
+// cache a file's directory entry
+// return pointer to cached entry or null for failure
+dir_t* FatFile::cacheDirEntry(uint8_t action) {
+  cache_t* pc;
+  pc = m_vol->cacheFetchData(m_dirBlock, action);
+  if (!pc) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  return pc->dir + (m_dirIndex & 0XF);
+
+fail:
+  return 0;
+}
+//------------------------------------------------------------------------------
+bool FatFile::close() {
+  bool rtn = sync();
+  m_attr = FILE_ATTR_CLOSED;
+  return rtn;
+}
+//------------------------------------------------------------------------------
+bool FatFile::contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock) {
+  // error if no blocks
+  if (m_firstCluster == 0) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  for (uint32_t c = m_firstCluster; ; c++) {
+    uint32_t next;
+    int8_t fg = m_vol->fatGet(c, &next);
+    if (fg < 0) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    // check for contiguous
+    if (fg == 0 || next != (c + 1)) {
+      // error if not end of chain
+      if (fg) {
+        DBG_FAIL_MACRO;
+        goto fail;
+      }
+      *bgnBlock = m_vol->clusterStartBlock(m_firstCluster);
+      *endBlock = m_vol->clusterStartBlock(c)
+                  + m_vol->blocksPerCluster() - 1;
+      return true;
+    }
+  }
+
+fail:
+  return false;
+}
+//------------------------------------------------------------------------------
+bool FatFile::createContiguous(FatFile* dirFile,
+                               const char* path, uint32_t size) {
+  uint32_t count;
+  // don't allow zero length file
+  if (size == 0) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  if (!open(dirFile, path, O_CREAT | O_EXCL | O_RDWR)) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // calculate number of clusters needed
+  count = ((size - 1) >> (m_vol->clusterSizeShift() + 9)) + 1;
+
+  // allocate clusters
+  if (!m_vol->allocContiguous(count, &m_firstCluster)) {
+    remove();
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  m_fileSize = size;
+
+  // insure sync() will update dir entry
+  m_flags |= F_FILE_DIR_DIRTY;
+
+  return sync();
+
+fail:
+  return false;
+}
+//------------------------------------------------------------------------------
+bool FatFile::dirEntry(dir_t* dst) {
+  dir_t* dir;
+  // Make sure fields on device are correct.
+  if (!sync()) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // read entry
+  dir = cacheDirEntry(FatCache::CACHE_FOR_READ);
+  if (!dir) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // copy to caller's struct
+  memcpy(dst, dir, sizeof(dir_t));
+  return true;
+
+fail:
+  return false;
+}
+//------------------------------------------------------------------------------
+uint8_t FatFile::dirName(const dir_t* dir, char* name) {
+  uint8_t j = 0;
+  uint8_t lcBit = DIR_NT_LC_BASE;
+  for (uint8_t i = 0; i < 11; i++) {
+    if (dir->name[i] == ' ') {
+      continue;
+    }
+    if (i == 8) {
+      // Position bit for extension.
+      lcBit = DIR_NT_LC_EXT;
+      name[j++] = '.';
+    }
+    char c = dir->name[i];
+    if ('A' <= c && c <= 'Z' && (lcBit & dir->reservedNT)) {
+      c += 'a' - 'A';
+    }
+    name[j++] = c;
+  }
+  name[j] = 0;
+  return j;
+}
+
+//------------------------------------------------------------------------------
+uint32_t FatFile::dirSize() {
+  int8_t fg;
+  if (!isDir()) {
+    return 0;
+  }
+  if (isRootFixed()) {
+    return 32*m_vol->rootDirEntryCount();
+  }
+  uint16_t n = 0;
+  uint32_t c = isRoot32() ? m_vol->rootDirStart() : m_firstCluster;
+  do {
+    fg = m_vol->fatGet(c, &c);
+    if (fg < 0 || n > 4095) {
+      return 0;
+    }
+    n += m_vol->blocksPerCluster();
+  } while (fg);
+  return 512UL*n;
+}
+//------------------------------------------------------------------------------
+int16_t FatFile::fgets(char* str, int16_t num, char* delim) {
+  char ch;
+  int16_t n = 0;
+  int16_t r = -1;
+  while ((n + 1) < num && (r = read(&ch, 1)) == 1) {
+    // delete CR
+    if (ch == '\r') {
+      continue;
+    }
+    str[n++] = ch;
+    if (!delim) {
+      if (ch == '\n') {
+        break;
+      }
+    } else {
+      if (strchr(delim, ch)) {
+        break;
+      }
+    }
+  }
+  if (r < 0) {
+    // read error
+    return -1;
+  }
+  str[n] = '\0';
+  return n;
+}
+//------------------------------------------------------------------------------
+void FatFile::getpos(FatPos_t* pos) {
+  pos->position = m_curPosition;
+  pos->cluster = m_curCluster;
+}
+//------------------------------------------------------------------------------
+bool FatFile::mkdir(FatFile* parent, const char* path, bool pFlag) {
+  fname_t fname;
+  FatFile tmpDir;
+
+  if (isOpen() || !parent->isDir()) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  if (isDirSeparator(*path)) {
+    while (isDirSeparator(*path)) {
+      path++;
+    }
+    if (!tmpDir.openRoot(parent->m_vol)) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    parent = &tmpDir;
+  }
+  while (1) {
+    if (!parsePathName(path, &fname, &path)) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    if (!*path) {
+      break;
+    }
+    if (!open(parent, &fname, O_READ)) {
+      if (!pFlag || !mkdir(parent, &fname)) {
+        DBG_FAIL_MACRO;
+        goto fail;
+      }
+    }
+    tmpDir = *this;
+    parent = &tmpDir;
+    close();
+  }
+  return mkdir(parent, &fname);
+
+fail:
+  return false;
+}
+//------------------------------------------------------------------------------
+bool FatFile::mkdir(FatFile* parent, fname_t* fname) {
+  uint32_t block;
+  dir_t dot;
+  dir_t* dir;
+  cache_t* pc;
+
+  if (!parent->isDir()) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // create a normal file
+  if (!open(parent, fname, O_CREAT | O_EXCL | O_RDWR)) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // convert file to directory
+  m_flags = O_READ;
+  m_attr = FILE_ATTR_SUBDIR;
+
+  // allocate and zero first cluster
+  if (!addDirCluster()) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  m_firstCluster = m_curCluster;
+  // Set to start of dir
+  rewind();
+  // force entry to device
+  if (!sync()) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // cache entry - should already be in cache due to sync() call
+  dir = cacheDirEntry(FatCache::CACHE_FOR_WRITE);
+  if (!dir) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // change directory entry  attribute
+  dir->attributes = DIR_ATT_DIRECTORY;
+
+  // make entry for '.'
+  memcpy(&dot, dir, sizeof(dot));
+  dot.name[0] = '.';
+  for (uint8_t i = 1; i < 11; i++) {
+    dot.name[i] = ' ';
+  }
+
+  // cache block for '.'  and '..'
+  block = m_vol->clusterStartBlock(m_firstCluster);
+  pc = m_vol->cacheFetchData(block, FatCache::CACHE_FOR_WRITE);
+  if (!pc) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // copy '.' to block
+  memcpy(&pc->dir[0], &dot, sizeof(dot));
+  // make entry for '..'
+  dot.name[1] = '.';
+  dot.firstClusterLow = parent->m_firstCluster & 0XFFFF;
+  dot.firstClusterHigh = parent->m_firstCluster >> 16;
+  // copy '..' to block
+  memcpy(&pc->dir[1], &dot, sizeof(dot));
+  // write first block
+  return m_vol->cacheSync();
+
+fail:
+  return false;
+}
+//------------------------------------------------------------------------------
+bool FatFile::open(FatFileSystem* fs, const char* path, uint8_t oflag) {
+  return open(fs->vwd(), path, oflag);
+}
+//------------------------------------------------------------------------------
+bool FatFile::open(FatFile* dirFile, const char* path, uint8_t oflag) {
+  FatFile tmpDir;
+  fname_t fname;
+
+  // error if already open
+  if (isOpen() || !dirFile->isDir()) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  if (isDirSeparator(*path)) {
+    while (isDirSeparator(*path)) {
+      path++;
+    }
+    if (*path == 0) {
+      return openRoot(dirFile->m_vol);
+    }
+    if (!tmpDir.openRoot(dirFile->m_vol)) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    dirFile = &tmpDir;
+  }
+  while (1) {
+    if (!parsePathName(path, &fname, &path)) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    if (*path == 0) {
+      break;
+    }
+    if (!open(dirFile, &fname, O_READ)) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    tmpDir = *this;
+    dirFile = &tmpDir;
+    close();
+  }
+  return open(dirFile, &fname, oflag);
+
+fail:
+  return false;
+}
+//------------------------------------------------------------------------------
+bool FatFile::open(FatFile* dirFile, uint16_t index, uint8_t oflag) {
+  uint8_t chksum = 0;
+  uint8_t lfnOrd = 0;
+  dir_t* dir;
+  ldir_t*ldir;
+
+  // Error if already open.
+  if (isOpen() || !dirFile->isDir()) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // Don't open existing file if O_EXCL - user call error.
+  if (oflag & O_EXCL) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  if (index) {
+    // Check for LFN.
+    if (!dirFile->seekSet(32UL*(index -1))) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    ldir = reinterpret_cast<ldir_t*>(dirFile->readDirCache());
+    if (!ldir) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    if (ldir->attr == DIR_ATT_LONG_NAME) {
+      if (1 == (ldir->ord & 0X1F)) {
+        chksum = ldir->chksum;
+        // Use largest possible number.
+        lfnOrd = index > 20 ? 20 : index;
+      }
+    }
+  } else {
+    dirFile->rewind();
+  }
+  // read entry into cache
+  dir = dirFile->readDirCache();
+  if (!dir) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // error if empty slot or '.' or '..'
+  if (dir->name[0] == DIR_NAME_DELETED ||
+      dir->name[0] == DIR_NAME_FREE ||
+      dir->name[0] == '.') {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  if (lfnOrd && chksum != lfnChecksum(dir->name)) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // open cached entry
+  if (!openCachedEntry(dirFile, index, oflag, lfnOrd)) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  return true;
+
+fail:
+  return false;
+}
+//------------------------------------------------------------------------------
+// open a cached directory entry.
+
+bool FatFile::openCachedEntry(FatFile* dirFile, uint16_t dirIndex,
+                              uint8_t oflag, uint8_t lfnOrd) {
+  uint32_t firstCluster;
+  memset(this, 0, sizeof(FatFile));
+  // location of entry in cache
+  m_vol = dirFile->m_vol;
+  m_dirIndex = dirIndex;
+  m_dirCluster = dirFile->m_firstCluster;
+  dir_t* dir = &m_vol->cacheAddress()->dir[0XF & dirIndex];
+
+  // Must be file or subdirectory.
+  if (!DIR_IS_FILE_OR_SUBDIR(dir)) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  m_attr = dir->attributes & FILE_ATTR_COPY;
+  if (DIR_IS_FILE(dir)) {
+    m_attr |= FILE_ATTR_FILE;
+  }
+  m_lfnOrd = lfnOrd;
+  // Write, truncate, or at end is an error for a directory or read-only file.
+  if (oflag & (O_WRITE | O_TRUNC | O_AT_END)) {
+    if (isSubDir() || isReadOnly()) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+  }
+  // save open flags for read/write
+  m_flags = oflag & F_OFLAG;
+
+  m_dirBlock = m_vol->cacheBlockNumber();
+
+  // copy first cluster number for directory fields
+  firstCluster = ((uint32_t)dir->firstClusterHigh << 16)
+                 | dir->firstClusterLow;
+
+  if (oflag & O_TRUNC) {
+    if (firstCluster && !m_vol->freeChain(firstCluster)) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    // need to update directory entry
+    m_flags |= F_FILE_DIR_DIRTY;
+  } else {
+    m_firstCluster = firstCluster;
+    m_fileSize = dir->fileSize;
+  }
+  if ((oflag & O_AT_END) && !seekSet(m_fileSize)) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  return true;
+
+fail:
+  m_attr = FILE_ATTR_CLOSED;
+  return false;
+}
+//------------------------------------------------------------------------------
+bool FatFile::openNext(FatFile* dirFile, uint8_t oflag) {
+  uint8_t chksum = 0;
+  ldir_t* ldir;
+  uint8_t lfnOrd = 0;
+  uint16_t index;
+
+  // Check for not open and valid directory..
+  if (isOpen() || !dirFile->isDir() || (dirFile->curPosition() & 0X1F)) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  while (1) {
+    // read entry into cache
+    index = dirFile->curPosition()/32;
+    dir_t* dir = dirFile->readDirCache();
+    if (!dir) {
+      if (dirFile->getError()) {
+        DBG_FAIL_MACRO;
+      }
+      goto fail;
+    }
+    // done if last entry
+    if (dir->name[0] == DIR_NAME_FREE) {
+      goto fail;
+    }
+    // skip empty slot or '.' or '..'
+    if (dir->name[0] == '.' || dir->name[0] == DIR_NAME_DELETED) {
+      lfnOrd = 0;
+    } else if (DIR_IS_FILE_OR_SUBDIR(dir)) {
+      if (lfnOrd && chksum != lfnChecksum(dir->name)) {
+        DBG_FAIL_MACRO;
+        goto fail;
+      }
+      if (!openCachedEntry(dirFile, index, oflag, lfnOrd)) {
+        DBG_FAIL_MACRO;
+        goto fail;
+      }
+      return true;
+    } else if (DIR_IS_LONG_NAME(dir)) {
+      ldir = reinterpret_cast<ldir_t*>(dir);
+      if (ldir->ord & LDIR_ORD_LAST_LONG_ENTRY) {
+        lfnOrd = ldir->ord & 0X1F;
+        chksum = ldir->chksum;
+      }
+    } else {
+      lfnOrd = 0;
+    }
+  }
+
+fail:
+  return false;
+}
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+//------------------------------------------------------------------------------
+/** Open a file's parent directory.
+ *
+ * \param[in] file Parent of this directory will be opened.  Must not be root.
+ *
+ * \return The value true is returned for success and
+ * the value false is returned for failure.
+ */
+bool FatFile::openParent(FatFile* dirFile) {
+  FatFile dotdot;
+  uint32_t lbn;
+  dir_t* dir;
+  uint32_t ddc;
+  cache_t* cb;
+
+  if (isOpen() || !dirFile->isOpen()) {
+    goto fail;
+  }
+  if (dirFile->m_dirCluster == 0) {
+    return openRoot(dirFile->m_vol);
+  }
+  lbn = dirFile->m_vol->clusterStartBlock(dirFile->m_dirCluster);
+  cb = dirFile->m_vol->cacheFetchData(lbn, FatCache::CACHE_FOR_READ);
+  if (!cb) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // Point to dir entery for ..
+  dir = cb->dir + 1;
+  ddc = dir->firstClusterLow | ((uint32_t)dir->firstClusterHigh << 16);
+  if (ddc == 0) {
+    if (!dotdot.openRoot(dirFile->m_vol)) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+  } else {
+    memset(&dotdot, 0, sizeof(FatFile));
+    dotdot.m_attr = FILE_ATTR_SUBDIR;
+    dotdot.m_flags = O_READ;
+    dotdot.m_vol = dirFile->m_vol;
+    dotdot.m_firstCluster = ddc;
+  }
+  uint32_t di;
+  do {
+    di = dotdot.curPosition()/32;
+    dir = dotdot.readDirCache();
+    if (!dir) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    ddc = dir->firstClusterLow | ((uint32_t)dir->firstClusterHigh << 16);
+  } while (ddc != dirFile->m_dirCluster);
+  return open(&dotdot, di, O_READ);
+
+fail:
+  return false;
+}
+#endif  // DOXYGEN_SHOULD_SKIP_THIS
+//------------------------------------------------------------------------------
+bool FatFile::openRoot(FatVolume* vol) {
+  // error if file is already open
+  if (isOpen()) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  memset(this, 0, sizeof(FatFile));
+
+  m_vol = vol;
+  switch (vol->fatType()) {
+#if FAT12_SUPPORT
+  case 12:
+#endif  // FAT12_SUPPORT
+  case 16:
+    m_attr = FILE_ATTR_ROOT_FIXED;
+    break;
+
+  case 32:
+    m_attr = FILE_ATTR_ROOT32;
+    break;
+
+  default:
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // read only
+  m_flags = O_READ;
+  return true;
+
+fail:
+  return false;
+}
+//------------------------------------------------------------------------------
+int FatFile::peek() {
+  FatPos_t pos;
+  getpos(&pos);
+  int c = read();
+  if (c >= 0) {
+    setpos(&pos);
+  }
+  return c;
+}
+//------------------------------------------------------------------------------
+int FatFile::read(void* buf, size_t nbyte) {
+  int8_t fg;
+  uint8_t blockOfCluster = 0;
+  uint8_t* dst = reinterpret_cast<uint8_t*>(buf);
+  uint16_t offset;
+  size_t toRead;
+  uint32_t block;  // raw device block number
+  cache_t* pc;
+
+  // error if not open for read
+  if (!isOpen() || !(m_flags & O_READ)) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+
+  if (isFile()) {
+    uint32_t tmp32 = m_fileSize - m_curPosition;
+    if (nbyte >= tmp32) {
+      nbyte = tmp32;
+    }
+  } else if (isRootFixed()) {
+    uint16_t tmp16 = 32*m_vol->m_rootDirEntryCount - (uint16_t)m_curPosition;
+    if (nbyte > tmp16) {
+      nbyte = tmp16;
+    }
+  }
+  toRead = nbyte;
+  while (toRead) {
+    size_t n;
+    offset = m_curPosition & 0X1FF;  // offset in block
+    if (isRootFixed()) {
+      block = m_vol->rootDirStart() + (m_curPosition >> 9);
+    } else {
+      blockOfCluster = m_vol->blockOfCluster(m_curPosition);
+      if (offset == 0 && blockOfCluster == 0) {
+        // start of new cluster
+        if (m_curPosition == 0) {
+          // use first cluster in file
+          m_curCluster = isRoot32() ? m_vol->rootDirStart() : m_firstCluster;
+        } else {
+          // get next cluster from FAT
+          fg = m_vol->fatGet(m_curCluster, &m_curCluster);
+          if (fg < 0) {
+            DBG_FAIL_MACRO;
+            goto fail;
+          }
+          if (fg == 0) {
+            if (isDir()) {
+              break;
+            }
+            DBG_FAIL_MACRO;
+            goto fail;
+          }
+        }
+      }
+      block = m_vol->clusterStartBlock(m_curCluster) + blockOfCluster;
+    }
+    if (offset != 0 || toRead < 512 || block == m_vol->cacheBlockNumber()) {
+      // amount to be read from current block
+      n = 512 - offset;
+      if (n > toRead) {
+        n = toRead;
+      }
+      // read block to cache and copy data to caller
+      pc = m_vol->cacheFetchData(block, FatCache::CACHE_FOR_READ);
+      if (!pc) {
+        DBG_FAIL_MACRO;
+        goto fail;
+      }
+      uint8_t* src = pc->data + offset;
+      memcpy(dst, src, n);
+#if USE_MULTI_BLOCK_IO
+    } else if (toRead >= 1024) {
+      uint8_t nb = toRead >> 9;
+      if (!isRootFixed()) {
+        uint8_t mb = m_vol->blocksPerCluster() - blockOfCluster;
+        if (mb < nb) {
+          nb = mb;
+        }
+      }
+      n = 512*nb;
+      if (m_vol->cacheBlockNumber() <= block
+          && block < (m_vol->cacheBlockNumber() + nb)) {
+        // flush cache if a block is in the cache
+        if (!m_vol->cacheSync()) {
+          DBG_FAIL_MACRO;
+          goto fail;
+        }
+      }
+      if (!m_vol->readBlocks(block, dst, nb)) {
+        DBG_FAIL_MACRO;
+        goto fail;
+      }
+#endif  // USE_MULTI_BLOCK_IO
+    } else {
+      // read single block
+      n = 512;
+      if (!m_vol->readBlock(block, dst)) {
+        DBG_FAIL_MACRO;
+        goto fail;
+      }
+    }
+    dst += n;
+    m_curPosition += n;
+    toRead -= n;
+  }
+  return nbyte - toRead;
+
+fail:
+  m_error |= READ_ERROR;
+  return -1;
+}
+//------------------------------------------------------------------------------
+int8_t FatFile::readDir(dir_t* dir) {
+  int16_t n;
+  // if not a directory file or miss-positioned return an error
+  if (!isDir() || (0X1F & m_curPosition)) {
+    return -1;
+  }
+
+  while (1) {
+    n = read(dir, sizeof(dir_t));
+    if (n != sizeof(dir_t)) {
+      return n == 0 ? 0 : -1;
+    }
+    // last entry if DIR_NAME_FREE
+    if (dir->name[0] == DIR_NAME_FREE) {
+      return 0;
+    }
+    // skip empty entries and entry for .  and ..
+    if (dir->name[0] == DIR_NAME_DELETED || dir->name[0] == '.') {
+      continue;
+    }
+    // return if normal file or subdirectory
+    if (DIR_IS_FILE_OR_SUBDIR(dir)) {
+      return n;
+    }
+  }
+}
+//------------------------------------------------------------------------------
+// Read next directory entry into the cache
+// Assumes file is correctly positioned
+dir_t* FatFile::readDirCache(bool skipReadOk) {
+//  uint8_t b;
+  uint8_t i = (m_curPosition >> 5) & 0XF;
+
+  if (i == 0 || !skipReadOk) {
+    int8_t n = read(&n, 1);
+    if  (n != 1) {
+      if (n != 0) {
+        DBG_FAIL_MACRO;
+      }
+      goto fail;
+    }
+//   if (read(&b, 1) != 1) {
+//     DBG_FAIL_MACRO;
+//     goto fail;
+//    }
+    m_curPosition += 31;
+  } else {
+    m_curPosition += 32;
+  }
+  // return pointer to entry
+  return m_vol->cacheAddress()->dir + i;
+
+fail:
+  return 0;
+}
+//------------------------------------------------------------------------------
+bool FatFile::remove(FatFile* dirFile, const char* path) {
+  FatFile file;
+  if (!file.open(dirFile, path, O_WRITE)) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  return file.remove();
+
+fail:
+  return false;
+}
+//------------------------------------------------------------------------------
+bool FatFile::rename(FatFile* dirFile, const char* newPath) {
+  dir_t entry;
+  uint32_t dirCluster = 0;
+  FatFile file;
+  FatFile oldFile;
+  cache_t* pc;
+  dir_t* dir;
+
+  // Must be an open file or subdirectory.
+  if (!(isFile() || isSubDir())) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // Can't rename LFN in 8.3 mode.
+  if (!USE_LONG_FILE_NAMES && isLFN()) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // Can't move file to new volume.
+  if (m_vol != dirFile->m_vol) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // sync() and cache directory entry
+  sync();
+  oldFile = *this;
+  dir = cacheDirEntry(FatCache::CACHE_FOR_READ);
+  if (!dir) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // save directory entry
+  memcpy(&entry, dir, sizeof(entry));
+  // make directory entry for new path
+  if (isFile()) {
+    if (!file.open(dirFile, newPath, O_CREAT | O_EXCL | O_WRITE)) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+  } else {
+    // don't create missing path prefix components
+    if (!file.mkdir(dirFile, newPath, false)) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    // save cluster containing new dot dot
+    dirCluster = file.m_firstCluster;
+  }
+  // change to new directory entry
+
+  m_dirBlock = file.m_dirBlock;
+  m_dirIndex = file.m_dirIndex;
+  m_lfnOrd = file.m_lfnOrd;
+  m_dirCluster = file.m_dirCluster;
+  // mark closed to avoid possible destructor close call
+  file.m_attr = FILE_ATTR_CLOSED;
+
+  // cache new directory entry
+  dir = cacheDirEntry(FatCache::CACHE_FOR_WRITE);
+  if (!dir) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // copy all but name and name flags to new directory entry
+  memcpy(&dir->creationTimeTenths, &entry.creationTimeTenths,
+         sizeof(entry) - sizeof(dir->name) - 2);
+  dir->attributes = entry.attributes;
+
+  // update dot dot if directory
+  if (dirCluster) {
+    // get new dot dot
+    uint32_t block = m_vol->clusterStartBlock(dirCluster);
+    pc = m_vol->cacheFetchData(block, FatCache::CACHE_FOR_READ);
+    if (!pc) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    memcpy(&entry, &pc->dir[1], sizeof(entry));
+
+    // free unused cluster
+    if (!m_vol->freeChain(dirCluster)) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    // store new dot dot
+    block = m_vol->clusterStartBlock(m_firstCluster);
+    pc = m_vol->cacheFetchData(block, FatCache::CACHE_FOR_WRITE);
+    if (!pc) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    memcpy(&pc->dir[1], &entry, sizeof(entry));
+  }
+  // Remove old directory entry;
+  oldFile.m_firstCluster = 0;
+  oldFile.m_flags = O_WRITE;
+  oldFile.m_attr = FILE_ATTR_FILE;
+  if (!oldFile.remove()) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  return m_vol->cacheSync();
+
+fail:
+  return false;
+}
+//------------------------------------------------------------------------------
+bool FatFile::rmdir() {
+  // must be open subdirectory
+  if (!isSubDir() || (!USE_LONG_FILE_NAMES && isLFN())) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  rewind();
+
+  // make sure directory is empty
+  while (1) {
+    dir_t* dir = readDirCache(true);
+    if (!dir) {
+      // EOF if no error.
+      if (!getError()) {
+        break;
+      }
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    // done if past last used entry
+    if (dir->name[0] == DIR_NAME_FREE) {
+      break;
+    }
+    // skip empty slot, '.' or '..'
+    if (dir->name[0] == DIR_NAME_DELETED || dir->name[0] == '.') {
+      continue;
+    }
+    // error not empty
+    if (DIR_IS_FILE_OR_SUBDIR(dir)) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+  }
+  // convert empty directory to normal file for remove
+  m_attr = FILE_ATTR_FILE;
+  m_flags |= O_WRITE;
+  return remove();
+
+fail:
+  return false;
+}
+//------------------------------------------------------------------------------
+bool FatFile::rmRfStar() {
+  uint16_t index;
+  FatFile f;
+  if (!isDir()) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  rewind();
+  while (1) {
+    // remember position
+    index = m_curPosition/32;
+
+    dir_t* dir = readDirCache();
+    if (!dir) {
+      // At EOF if no error.
+      if (!getError()) {
+        break;
+      }
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    // done if past last entry
+    if (dir->name[0] == DIR_NAME_FREE) {
+      break;
+    }
+
+    // skip empty slot or '.' or '..'
+    if (dir->name[0] == DIR_NAME_DELETED || dir->name[0] == '.') {
+      continue;
+    }
+
+    // skip if part of long file name or volume label in root
+    if (!DIR_IS_FILE_OR_SUBDIR(dir)) {
+      continue;
+    }
+
+    if (!f.open(this, index, O_READ)) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    if (f.isSubDir()) {
+      // recursively delete
+      if (!f.rmRfStar()) {
+        DBG_FAIL_MACRO;
+        goto fail;
+      }
+    } else {
+      // ignore read-only
+      f.m_flags |= O_WRITE;
+      if (!f.remove()) {
+        DBG_FAIL_MACRO;
+        goto fail;
+      }
+    }
+    // position to next entry if required
+    if (m_curPosition != (32UL*(index + 1))) {
+      if (!seekSet(32UL*(index + 1))) {
+        DBG_FAIL_MACRO;
+        goto fail;
+      }
+    }
+  }
+  // don't try to delete root
+  if (!isRoot()) {
+    if (!rmdir()) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+  }
+  return true;
+
+fail:
+  return false;
+}
+//------------------------------------------------------------------------------
+bool FatFile::seekSet(uint32_t pos) {
+  uint32_t nCur;
+  uint32_t nNew;
+  uint32_t tmp = m_curCluster;
+  // error if file not open
+  if (!isOpen()) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  if (pos == 0) {
+    // set position to start of file
+    m_curCluster = 0;
+    goto done;
+  }
+  if (isFile()) {
+    if (pos > m_fileSize) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+  } else if (isRootFixed()) {
+    if (pos <= 32*m_vol->rootDirEntryCount()) {
+      goto done;
+    }
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // calculate cluster index for cur and new position
+  nCur = (m_curPosition - 1) >> (m_vol->clusterSizeShift() + 9);
+  nNew = (pos - 1) >> (m_vol->clusterSizeShift() + 9);
+
+  if (nNew < nCur || m_curPosition == 0) {
+    // must follow chain from first cluster
+    m_curCluster = isRoot32() ? m_vol->rootDirStart() : m_firstCluster;
+  } else {
+    // advance from curPosition
+    nNew -= nCur;
+  }
+  while (nNew--) {
+    if (m_vol->fatGet(m_curCluster, &m_curCluster) <= 0) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+  }
+
+done:
+  m_curPosition = pos;
+  return true;
+
+fail:
+  m_curCluster = tmp;
+  return false;
+}
+//------------------------------------------------------------------------------
+void FatFile::setpos(FatPos_t* pos) {
+  m_curPosition = pos->position;
+  m_curCluster = pos->cluster;
+}
+//------------------------------------------------------------------------------
+bool FatFile::sync() {
+  if (!isOpen()) {
+    return true;
+  }
+
+  if (m_flags & F_FILE_DIR_DIRTY) {
+    dir_t* dir = cacheDirEntry(FatCache::CACHE_FOR_WRITE);
+    // check for deleted by another open file object
+    if (!dir || dir->name[0] == DIR_NAME_DELETED) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    // do not set filesize for dir files
+    if (isFile()) {
+      dir->fileSize = m_fileSize;
+    }
+
+    // update first cluster fields
+    dir->firstClusterLow = m_firstCluster & 0XFFFF;
+    dir->firstClusterHigh = m_firstCluster >> 16;
+
+    // set modify time if user supplied a callback date/time function
+    if (m_dateTime) {
+      m_dateTime(&dir->lastWriteDate, &dir->lastWriteTime);
+      dir->lastAccessDate = dir->lastWriteDate;
+    }
+    // clear directory dirty
+    m_flags &= ~F_FILE_DIR_DIRTY;
+  }
+  if (m_vol->cacheSync()) {
+    return true;
+  }
+  DBG_FAIL_MACRO;
+
+fail:
+  m_error |= WRITE_ERROR;
+  return false;
+}
+//------------------------------------------------------------------------------
+bool FatFile::timestamp(FatFile* file) {
+  dir_t* dir;
+  dir_t srcDir;
+
+  // most be files get timestamps
+  if (!isFile() || !file->isFile() || !file->dirEntry(&srcDir)) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // update directory fields
+  if (!sync()) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  dir = cacheDirEntry(FatCache::CACHE_FOR_WRITE);
+  if (!dir) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // copy timestamps
+  dir->lastAccessDate = srcDir.lastAccessDate;
+  dir->creationDate = srcDir.creationDate;
+  dir->creationTime = srcDir.creationTime;
+  dir->creationTimeTenths = srcDir.creationTimeTenths;
+  dir->lastWriteDate = srcDir.lastWriteDate;
+  dir->lastWriteTime = srcDir.lastWriteTime;
+
+  // write back entry
+  return m_vol->cacheSync();
+
+fail:
+  return false;
+}
+//------------------------------------------------------------------------------
+bool FatFile::timestamp(uint8_t flags, uint16_t year, uint8_t month,
+                   uint8_t day, uint8_t hour, uint8_t minute, uint8_t second) {
+  uint16_t dirDate;
+  uint16_t dirTime;
+  dir_t* dir;
+
+  if (!isFile()
+      || year < 1980
+      || year > 2107
+      || month < 1
+      || month > 12
+      || day < 1
+      || day > 31
+      || hour > 23
+      || minute > 59
+      || second > 59) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // update directory entry
+  if (!sync()) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  dir = cacheDirEntry(FatCache::CACHE_FOR_WRITE);
+  if (!dir) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  dirDate = FAT_DATE(year, month, day);
+  dirTime = FAT_TIME(hour, minute, second);
+  if (flags & T_ACCESS) {
+    dir->lastAccessDate = dirDate;
+  }
+  if (flags & T_CREATE) {
+    dir->creationDate = dirDate;
+    dir->creationTime = dirTime;
+    // seems to be units of 1/100 second not 1/10 as Microsoft states
+    dir->creationTimeTenths = second & 1 ? 100 : 0;
+  }
+  if (flags & T_WRITE) {
+    dir->lastWriteDate = dirDate;
+    dir->lastWriteTime = dirTime;
+  }
+  return m_vol->cacheSync();
+
+fail:
+  return false;
+}
+//------------------------------------------------------------------------------
+bool FatFile::truncate(uint32_t length) {
+  uint32_t newPos;
+  // error if not a normal file or read-only
+  if (!isFile() || !(m_flags & O_WRITE)) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // error if length is greater than current size
+  if (length > m_fileSize) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // fileSize and length are zero - nothing to do
+  if (m_fileSize == 0) {
+    return true;
+  }
+
+  // remember position for seek after truncation
+  newPos = m_curPosition > length ? length : m_curPosition;
+
+  // position to last cluster in truncated file
+  if (!seekSet(length)) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  if (length == 0) {
+    // free all clusters
+    if (!m_vol->freeChain(m_firstCluster)) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    m_firstCluster = 0;
+  } else {
+    uint32_t toFree;
+    int8_t fg = m_vol->fatGet(m_curCluster, &toFree);
+    if (fg < 0) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    if (fg) {
+      // free extra clusters
+      if (!m_vol->freeChain(toFree)) {
+        DBG_FAIL_MACRO;
+        goto fail;
+      }
+      // current cluster is end of chain
+      if (!m_vol->fatPutEOC(m_curCluster)) {
+        DBG_FAIL_MACRO;
+        goto fail;
+      }
+    }
+  }
+  m_fileSize = length;
+
+  // need to update directory entry
+  m_flags |= F_FILE_DIR_DIRTY;
+
+  if (!sync()) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // set file to correct position
+  return seekSet(newPos);
+
+fail:
+  return false;
+}
+//------------------------------------------------------------------------------
+int FatFile::write(const void* buf, size_t nbyte) {
+  // convert void* to uint8_t*  -  must be before goto statements
+  const uint8_t* src = reinterpret_cast<const uint8_t*>(buf);
+  cache_t* pc;
+  uint8_t cacheOption;
+  // number of bytes left to write  -  must be before goto statements
+  size_t nToWrite = nbyte;
+  size_t n;
+  // error if not a normal file or is read-only
+  if (!isFile() || !(m_flags & O_WRITE)) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // seek to end of file if append flag
+  if ((m_flags & O_APPEND)) {
+    if (!seekSet(m_fileSize)) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+  }
+  // Don't exceed max fileSize.
+  if (nbyte > (0XFFFFFFFF - m_curPosition)) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  while (nToWrite) {
+    uint8_t blockOfCluster = m_vol->blockOfCluster(m_curPosition);
+    uint16_t blockOffset = m_curPosition & 0X1FF;
+    if (blockOfCluster == 0 && blockOffset == 0) {
+      // start of new cluster
+      if (m_curCluster != 0) {
+        int8_t fg = m_vol->fatGet(m_curCluster, &m_curCluster);
+        if (fg < 0) {
+          DBG_FAIL_MACRO;
+          goto fail;
+        }
+        if (fg == 0) {
+          // add cluster if at end of chain
+          if (!addCluster()) {
+            DBG_FAIL_MACRO;
+            goto fail;
+          }
+        }
+      } else {
+        if (m_firstCluster == 0) {
+          // allocate first cluster of file
+          if (!addCluster()) {
+            DBG_FAIL_MACRO;
+            goto fail;
+          }
+          m_firstCluster = m_curCluster;
+        } else {
+          m_curCluster = m_firstCluster;
+        }
+      }
+    }
+    // block for data write
+    uint32_t block = m_vol->clusterStartBlock(m_curCluster) + blockOfCluster;
+
+    if (blockOffset != 0 || nToWrite < 512) {
+      // partial block - must use cache
+      // max space in block
+      n = 512 - blockOffset;
+      // lesser of space and amount to write
+      if (n > nToWrite) {
+        n = nToWrite;
+      }
+
+      if (blockOffset == 0 && m_curPosition >= m_fileSize) {
+        // start of new block don't need to read into cache
+        cacheOption = FatCache::CACHE_RESERVE_FOR_WRITE;
+      } else {
+        // rewrite part of block
+        cacheOption = FatCache::CACHE_FOR_WRITE;
+      }
+      pc = m_vol->cacheFetchData(block, cacheOption);
+      if (!pc) {
+        DBG_FAIL_MACRO;
+        goto fail;
+      }
+      uint8_t* dst = pc->data + blockOffset;
+      memcpy(dst, src, n);
+      if (512 == (n + blockOffset)) {
+        // Force write if block is full - improves large writes.
+        if (!m_vol->cacheSyncData()) {
+          DBG_FAIL_MACRO;
+          goto fail;
+        }
+      }
+#if USE_MULTI_BLOCK_IO
+    } else if (nToWrite >= 1024) {
+      // use multiple block write command
+      uint8_t maxBlocks = m_vol->blocksPerCluster() - blockOfCluster;
+      uint8_t nBlock = nToWrite >> 9;
+      if (nBlock > maxBlocks) {
+        nBlock = maxBlocks;
+      }
+      n = 512*nBlock;
+      if (m_vol->cacheBlockNumber() <= block
+          && block < (m_vol->cacheBlockNumber() + nBlock)) {
+        // invalidate cache if block is in cache
+        m_vol->cacheInvalidate();
+      }
+      if (!m_vol->writeBlocks(block, src, nBlock)) {
+        DBG_FAIL_MACRO;
+        goto fail;
+      }
+#endif  // USE_MULTI_BLOCK_IO
+    } else {
+      // use single block write command
+      n = 512;
+      if (m_vol->cacheBlockNumber() == block) {
+        m_vol->cacheInvalidate();
+      }
+      if (!m_vol->writeBlock(block, src)) {
+        DBG_FAIL_MACRO;
+        goto fail;
+      }
+    }
+    m_curPosition += n;
+    src += n;
+    nToWrite -= n;
+  }
+  if (m_curPosition > m_fileSize) {
+    // update fileSize and insure sync will update dir entry
+    m_fileSize = m_curPosition;
+    m_flags |= F_FILE_DIR_DIRTY;
+  } else if (m_dateTime) {
+    // insure sync will update modified date and time
+    m_flags |= F_FILE_DIR_DIRTY;
+  }
+
+  if (m_flags & O_SYNC) {
+    if (!sync()) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+  }
+  return nbyte;
+
+fail:
+  // return for write error
+  m_error |= WRITE_ERROR;
+  return -1;
+}

+ 990 - 0
SdFat/utility/FatFile.h

@@ -0,0 +1,990 @@
+/* FatLib Library
+ * Copyright (C) 2012 by William Greiman
+ *
+ * This file is part of the FatLib Library
+ *
+ * This Library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the FatLib Library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+#ifndef FatFile_h
+#define FatFile_h
+/**
+ * \file
+ * \brief FatFile class
+ */
+// #include <ctype.h>
+#include <string.h>
+#include <stddef.h>
+#include <limits.h>
+#include "FatLibConfig.h"
+#include "FatApiConstants.h"
+#include "FatStructs.h"
+#include "FatVolume.h"
+class FatFileSystem;
+//------------------------------------------------------------------------------
+// Stuff to store strings in AVR flash.
+#ifdef __AVR__
+#include <avr/pgmspace.h>
+#else  // __AVR__
+#ifndef PGM_P
+/** pointer to flash for ARM */
+#define PGM_P const char*
+#endif  // PGM_P
+#ifndef PSTR
+/** store literal string in flash for ARM */
+#define PSTR(x) (x)
+#endif  // PSTR
+#ifndef pgm_read_byte
+/** read 8-bits from flash for ARM */
+#define pgm_read_byte(addr) (*(const unsigned char*)(addr))
+#endif  // pgm_read_byte
+#ifndef pgm_read_word
+/** read 16-bits from flash for ARM */
+#define pgm_read_word(addr) (*(const uint16_t*)(addr))
+#endif  // pgm_read_word
+#ifndef PROGMEM
+/** store in flash for ARM */
+#define PROGMEM const
+#endif  // PROGMEM
+#endif  // __AVR__
+//------------------------------------------------------------------------------
+/**
+ * \struct FatPos_t
+ * \brief Internal type for file position - do not use in user apps.
+ */
+struct FatPos_t {
+  /** stream position */
+  uint32_t position;
+  /** cluster for position */
+  uint32_t cluster;
+  FatPos_t() : position(0), cluster(0) {}
+};
+//------------------------------------------------------------------------------
+/** Expression for path name separator. */
+#define isDirSeparator(c) ((c) == '/')
+//------------------------------------------------------------------------------
+/**
+ * \struct fname_t
+ * \brief Internal type for Short File Name - do not use in user apps.
+ */
+struct fname_t {
+  /** Flags for base and extension character case and LFN. */
+  uint8_t flags;
+  /** length of Long File Name */
+  size_t len;
+  /** Long File Name start. */
+  const char* lfn;
+  /** position for sequence number */
+  uint8_t seqPos;
+  /** Short File Name */
+  uint8_t sfn[11];
+};
+/** Derived from a LFN with loss or conversion of characters. */
+const uint8_t FNAME_FLAG_LOST_CHARS = 0X01;
+/** Base-name or extension has mixed case. */
+const uint8_t FNAME_FLAG_MIXED_CASE = 0X02;
+/** LFN entries are required for file name. */
+const uint8_t FNAME_FLAG_NEED_LFN =
+  FNAME_FLAG_LOST_CHARS | FNAME_FLAG_MIXED_CASE;
+/** Filename base-name is all lower case */
+const uint8_t FNAME_FLAG_LC_BASE = DIR_NT_LC_BASE;
+/** Filename extension is all lower case. */
+const uint8_t FNAME_FLAG_LC_EXT = DIR_NT_LC_EXT;
+//==============================================================================
+/**
+ * \class FatFile
+ * \brief Basic file class.
+ */
+class FatFile {
+ public:
+  /** Create an instance. */
+  FatFile() : m_attr(FILE_ATTR_CLOSED), m_error(0) {}
+  /**  Create a file object and open it in the current working directory.
+   *
+   * \param[in] path A path with a valid 8.3 DOS name for a file to be opened.
+   *
+   * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive
+   * OR of open flags. see FatFile::open(FatFile*, const char*, uint8_t).
+   */
+  FatFile(const char* path, uint8_t oflag) {
+    m_attr = FILE_ATTR_CLOSED;
+    m_error = 0;
+    open(path, oflag);
+  }
+#if DESTRUCTOR_CLOSES_FILE
+  ~FatFile() {
+    if (isOpen()) {
+      close();
+    }
+  }
+#endif  // DESTRUCTOR_CLOSES_FILE
+
+#if ENABLE_ARDUINO_FEATURES
+  /** List directory contents.
+   *
+   * \param[in] flags The inclusive OR of
+   *
+   * LS_DATE - %Print file modification date
+   *
+   * LS_SIZE - %Print file size.
+   *
+   * LS_R - Recursive list of subdirectories.
+   */
+  void ls(uint8_t flags = 0) {
+    ls(&Serial, flags);
+  }
+  /** %Print a directory date field.
+   *
+   *  Format is yyyy-mm-dd.
+   *
+   * \param[in] fatDate The date field from a directory entry.
+   */
+  static void printFatDate(uint16_t fatDate) {
+    printFatDate(&Serial, fatDate);
+  }
+  /** %Print a directory time field.
+   *
+   * Format is hh:mm:ss.
+   *
+   * \param[in] fatTime The time field from a directory entry.
+   */
+  static void printFatTime(uint16_t fatTime) {
+    printFatTime(&Serial, fatTime);
+  }
+  /** Print a file's name.
+   *
+   * \return The value true is returned for success and
+   * the value false is returned for failure.
+   */
+  size_t printName() {
+    return FatFile::printName(&Serial);
+  }
+#endif  // ENABLE_ARDUINO_FEATURES
+
+  /** \return value of writeError */
+  bool getWriteError() {
+    return m_error & WRITE_ERROR;
+  }
+  /** Set writeError to zero */
+  void clearWriteError() {
+    m_error &= ~WRITE_ERROR;
+  }
+  /** Clear all error bits. */
+  void clearError() {
+    m_error = 0;
+  }
+  /** \return All error bits. */
+  uint8_t getError() {
+    return m_error;
+  }
+  /** get position for streams
+   * \param[out] pos struct to receive position
+   */
+  void getpos(FatPos_t* pos);
+  /** set position for streams
+   * \param[out] pos struct with value for new position
+   */
+  void setpos(FatPos_t* pos);
+  /** \return The number of bytes available from the current position
+   * to EOF for normal files.  Zero is returned for directory files.
+   */
+  uint32_t available() {
+    return isFile() ? fileSize() - curPosition() : 0;
+  }
+  /** Close a file and force cached data and directory information
+   *  to be written to the storage device.
+   *
+   * \return The value true is returned for success and
+   * the value false is returned for failure.
+   */
+  bool close();
+  /** Check for contiguous file and return its raw block range.
+   *
+   * \param[out] bgnBlock the first block address for the file.
+   * \param[out] endBlock the last  block address for the file.
+   *
+   * \return The value true is returned for success and
+   * the value false is returned for failure.
+   */
+  bool contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock);
+  /** Create and open a new contiguous file of a specified size.
+   *
+   * \note This function only supports short DOS 8.3 names.
+   * See open() for more information.
+   *
+   * \param[in] dirFile The directory where the file will be created.
+   * \param[in] path A path with a valid DOS 8.3 file name.
+   * \param[in] size The desired file size.
+   *
+   * \return The value true is returned for success and
+   * the value false, is returned for failure.
+   */
+  bool createContiguous(FatFile* dirFile,
+                        const char* path, uint32_t size);
+  /** \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. */
+  uint32_t curPosition() const {
+    return m_curPosition;
+  }
+  /** \return Current working directory */
+  static FatFile* cwd() {
+    return m_cwd;
+  }
+  /** Set the date/time callback function
+   *
+   * \param[in] dateTime The user's call back function.  The callback
+   * function is of the form:
+   *
+   * \code
+   * void dateTime(uint16_t* date, uint16_t* time) {
+   *   uint16_t year;
+   *   uint8_t month, day, hour, minute, second;
+   *
+   *   // User gets date and time from GPS or real-time clock here
+   *
+   *   // return date using FAT_DATE macro to format fields
+   *   *date = FAT_DATE(year, month, day);
+   *
+   *   // return time using FAT_TIME macro to format fields
+   *   *time = FAT_TIME(hour, minute, second);
+   * }
+   * \endcode
+   *
+   * Sets the function that is called when a file is created or when
+   * a file's directory entry is modified by sync(). All timestamps,
+   * access, creation, and modify, are set when a file is created.
+   * sync() maintains the last access date and last modify date/time.
+   *
+   * See the timestamp() function.
+   */
+  static void dateTimeCallback(
+    void (*dateTime)(uint16_t* date, uint16_t* time)) {
+    m_dateTime = dateTime;
+  }
+  /**  Cancel the date/time callback function. */
+  static void dateTimeCallbackCancel() {
+    m_dateTime = 0;
+  }
+  /** Return a file's directory entry.
+   *
+   * \param[out] dir Location for return of the file's directory entry.
+   *
+   * \return The value true is returned for success and
+   * the value false is returned for failure.
+   */
+  bool dirEntry(dir_t* dir);
+  /**
+   * \return The index of this file in it's directory.
+   */
+  uint16_t dirIndex() {
+    return m_dirIndex;
+  }
+  /** Format the name field of \a dir into the 13 byte array
+   * \a name in standard 8.3 short name format.
+   *
+   * \param[in] dir The directory structure containing the name.
+   * \param[out] name A 13 byte char array for the formatted name.
+   * \return length of the name.
+   */
+  static uint8_t dirName(const dir_t* dir, char* name);
+  /** \return The number of bytes allocated to a directory or zero
+   *         if an error occurs.
+   */
+  uint32_t dirSize();
+  /** Dump file in Hex
+   * \param[in] pr Print stream for list.
+   * \param[in] pos Start position in file.
+   * \param[in] n number of locations to dump.
+   */
+  void dmpFile(print_t* pr, uint32_t pos, size_t n);
+  /** Test for the existence of a file in a directory
+   *
+   * \param[in] path Path of the file to be tested for.
+   *
+   * The calling instance must be an open directory file.
+   *
+   * dirFile.exists("TOFIND.TXT") searches for "TOFIND.TXT" in  the directory
+   * dirFile.
+   *
+   * \return true if the file exists else false.
+   */
+  bool exists(const char* path) {
+    FatFile file;
+    return file.open(this, path, O_READ);
+  }
+  /**
+   * Get a string from a file.
+   *
+   * fgets() reads bytes from a file into the array pointed to by \a str, until
+   * \a num - 1 bytes are read, or a delimiter is read and transferred to \a str,
+   * or end-of-file is encountered. The string is then terminated
+   * with a null byte.
+   *
+   * fgets() deletes CR, '\\r', from the string.  This insures only a '\\n'
+   * terminates the string for Windows text files which use CRLF for newline.
+   *
+   * \param[out] str Pointer to the array where the string is stored.
+   * \param[in] num Maximum number of characters to be read
+   * (including the final null byte). Usually the length
+   * of the array \a str is used.
+   * \param[in] delim Optional set of delimiters. The default is "\n".
+   *
+   * \return For success fgets() returns the length of the string in \a str.
+   * If no data is read, fgets() returns zero for EOF or -1 if an error occurred.
+   */
+  int16_t fgets(char* str, int16_t num, char* delim = 0);
+  /** \return The total number of bytes in a file. */
+  uint32_t fileSize() const {
+    return m_fileSize;
+  }
+  /** \return The first cluster number for a file or directory. */
+  uint32_t firstCluster() const {
+    return m_firstCluster;
+  }
+  /**
+   * Get a file's name followed by a zero byte.
+   *
+   * \param[out] name An array of characters for the file's name.
+   * \param[in] size The size of the array in bytes. The array
+   *             must be at least 13 bytes long.  The file's name will be
+   *             truncated if the file's name is too long.
+   * \return The value true, is returned for success and
+   * the value false, is returned for failure.
+   */
+  bool getName(char* name, size_t size);
+  /**
+   * Get a file's Short File Name followed by a zero byte.
+   *
+   * \param[out] name An array of characters for the file's name.
+   *                  The array must be at least 13 bytes long.
+   * \return The value true, is returned for success and
+   * the value false, is returned for failure.
+   */  
+  bool getSFN(char* name);
+  /** \return True if this is a directory else false. */
+  bool isDir() const {
+    return m_attr & FILE_ATTR_DIR;
+  }
+  /** \return True if this is a normal file else false. */
+  bool isFile() const {
+    return m_attr & FILE_ATTR_FILE;
+  }
+  /** \return True if this is a hidden file else false. */
+  bool isHidden() const {
+    return m_attr & FILE_ATTR_HIDDEN;
+  }
+  /** \return true if this file has a Long File Name. */
+  bool isLFN() const {
+    return m_lfnOrd;
+  }
+  /** \return True if this is an open file/directory else false. */
+  bool isOpen() const {
+    return m_attr;
+  }
+  /** \return True if this is the root directory. */
+  bool isRoot() const {
+    return m_attr & FILE_ATTR_ROOT;
+  }
+  /** \return True if this is the FAT32 root directory. */
+  bool isRoot32() const {
+    return m_attr & FILE_ATTR_ROOT32;
+  }
+  /** \return True if this is the FAT12 of FAT16 root directory. */
+  bool isRootFixed() const {
+    return m_attr & FILE_ATTR_ROOT_FIXED;
+  }
+  /** \return True if file is read-only */
+  bool isReadOnly() const {
+    return m_attr & FILE_ATTR_READ_ONLY;
+  }
+  /** \return True if this is a subdirectory else false. */
+  bool isSubDir() const {
+    return m_attr & FILE_ATTR_SUBDIR;
+  }
+  /** \return True if this is a system file else false. */
+  bool isSystem() const {
+    return m_attr & FILE_ATTR_SYSTEM;
+  }
+  /** Check for a legal 8.3 character.
+   * \param[in] c Character to be checked.
+   * \return true for a legal 8.3 character else false.
+   */
+  static bool legal83Char(uint8_t c) {
+    if (c == '"' || c == '|') {
+      return false;
+    }
+    // *+,./
+    if (0X2A <= c && c <= 0X2F && c != 0X2D) {
+      return false;
+    }
+    // :;<=>?
+    if (0X3A <= c && c <= 0X3F) {
+      return false;
+    }
+    // [\]
+    if (0X5B <= c && c <= 0X5D) {
+      return false;
+    }
+    return 0X20 < c && c < 0X7F;
+  }
+  /** List directory contents.
+   *
+   * \param[in] pr Print stream for list.
+   *
+   * \param[in] flags The inclusive OR of
+   *
+   * LS_DATE - %Print file modification date
+   *
+   * LS_SIZE - %Print file size.
+   *
+   * LS_R - Recursive list of subdirectories.
+   *
+   * \param[in] indent Amount of space before file name. Used for recursive
+   * list to indicate subdirectory level.
+   */
+  void ls(print_t* pr, uint8_t flags = 0, uint8_t indent = 0);
+  /** Make a new directory.
+   *
+   * \param[in] dir An open FatFile instance for the directory that will
+   *                   contain the new directory.
+   *
+   * \param[in] path A path with a valid 8.3 DOS name for the new directory.
+   *
+   * \param[in] pFlag Create missing parent directories if true.
+   *
+   * \return The value true is returned for success and
+   * the value false is returned for failure.
+   */
+  bool mkdir(FatFile* dir, const char* path, bool pFlag = true);
+  /** Open a file in the volume working directory of a FatFileSystem.
+   *
+   * \param[in] fs File System where the file is located.
+   *
+   * \param[in] path with a valid 8.3 DOS name for a file to be opened.
+   *
+   * \param[in] oflag bitwise-inclusive OR of open mode flags.
+   *                  See see FatFile::open(FatFile*, const char*, uint8_t).
+   *
+   * \return The value true is returned for success and
+   * the value false is returned for failure.
+   */
+  bool open(FatFileSystem* fs, const char* path, uint8_t oflag);
+  /** Open a file by index.
+   *
+   * \param[in] dirFile An open FatFile instance for the 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 mode 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(FatFile* dirFile, uint16_t index, uint8_t oflag);
+  /** Open a file or directory by name.
+   *
+   * \param[in] dirFile An open FatFile instance for the directory containing
+   *                    the file to be opened.
+   *
+   * \param[in] path A path with a valid 8.3 DOS name for a file to be opened.
+   *
+   * \param[in] oflag Values for \a oflag are constructed by a
+   *                  bitwise-inclusive OR of flags from the following list
+   *
+   * O_READ - Open for reading.
+   *
+   * O_RDONLY - Same as O_READ.
+   *
+   * O_WRITE - Open for writing.
+   *
+   * O_WRONLY - Same as O_WRITE.
+   *
+   * O_RDWR - Open for reading and writing.
+   *
+   * O_APPEND - If set, the file offset shall be set to the end of the
+   * file prior to each write.
+   *
+   * O_AT_END - Set the initial position at the end of the file.
+   *
+   * O_CREAT - If the file exists, this flag has no effect except as noted
+   * under O_EXCL below. Otherwise, the file shall be created
+   *
+   * O_EXCL - If O_CREAT and O_EXCL are set, open() shall fail if the file exists.
+   *
+   * O_SYNC - Call sync() after each write.  This flag should not be used with
+   * write(uint8_t) or any functions do character at a time writes since sync()
+   * will be called after each byte.
+   *
+   * O_TRUNC - If the file exists and is a regular file, and the file is
+   * successfully opened and is not read only, its length shall be truncated to 0.
+   *
+   * WARNING: A given file must not be opened by more than one FatFile object
+   * or file corruption may occur.
+   *
+   * \note Directory files must be opened read only.  Write and truncation is
+   * not allowed for directory files.
+   *
+   * \return The value true is returned for success and
+   * the value false is returned for failure.
+   */
+  bool open(FatFile* dirFile, const char* path, uint8_t oflag);
+  /** Open a file in the current working directory.
+   *
+   * \param[in] path A path with a valid 8.3 DOS name for a file to be opened.
+   *
+   * \param[in] oflag bitwise-inclusive OR of open mode flags.
+   *                  See see FatFile::open(FatFile*, const char*, uint8_t).
+   *
+   * \return The value true is returned for success and
+   * the value false is returned for failure.
+   */
+  bool open(const char* path, uint8_t oflag = O_READ) {
+    return open(m_cwd, path, oflag);
+  }
+  /** Open the next file or subdirectory in a directory.
+   *
+   * \param[in] dirFile An open FatFile instance for the directory
+   *                    containing the file to be opened.
+   *
+   * \param[in] oflag bitwise-inclusive OR of open mode flags.
+   *                  See see FatFile::open(FatFile*, const char*, uint8_t).
+   *
+   * \return true for success or false for failure.
+   */
+  bool openNext(FatFile* dirFile, uint8_t oflag = O_READ);
+  /** Open a volume's root directory.
+   *
+   * \param[in] vol The FAT volume containing the root directory to be opened.
+   *
+   * \return The value true is returned for success and
+   * the value false is returned for failure.
+   */
+  bool openRoot(FatVolume* vol);
+  /** Return the next available byte without consuming it.
+   *
+   * \return The byte if no error and not at eof else -1;
+   */
+  int peek();
+  /** Print a file's creation date and time
+   *
+   * \param[in] pr Print stream for output.
+   *
+   * \return The value true is returned for success and
+   * the value false is returned for failure.
+   */
+  bool printCreateDateTime(print_t* pr);
+  /** %Print a directory date field.
+   *
+   *  Format is yyyy-mm-dd.
+   *
+   * \param[in] pr Print stream for output.
+   * \param[in] fatDate The date field from a directory entry.
+   */
+  static void printFatDate(print_t* pr, uint16_t fatDate);
+  /** %Print a directory time field.
+   *
+   * Format is hh:mm:ss.
+   *
+   * \param[in] pr Print stream for output.
+   * \param[in] fatTime The time field from a directory entry.
+   */
+  static void printFatTime(print_t* pr, uint16_t fatTime);
+  /** Print a number followed by a field terminator.
+   * \param[in] value The number to be printed.
+   * \param[in] term The field terminator.  Use '\\n' for CR LF.
+   * \param[in] prec Number of digits after decimal point.
+   * \return The number of bytes written or -1 if an error occurs.
+   */
+  int printField(float value, char term, uint8_t prec = 2);
+  /** Print a number followed by a field terminator.
+   * \param[in] value The number to be printed.
+   * \param[in] term The field terminator.  Use '\\n' for CR LF.
+   * \return The number of bytes written or -1 if an error occurs.
+   */
+  int printField(int16_t value, char term);
+  /** Print a number followed by a field terminator.
+   * \param[in] value The number to be printed.
+   * \param[in] term The field terminator.  Use '\\n' for CR LF.
+   * \return The number of bytes written or -1 if an error occurs.
+   */
+  int printField(uint16_t value, char term);
+  /** Print a number followed by a field terminator.
+   * \param[in] value The number to be printed.
+   * \param[in] term The field terminator.  Use '\\n' for CR LF.
+   * \return The number of bytes written or -1 if an error occurs.
+   */
+  int printField(int32_t value, char term);
+  /** Print a number followed by a field terminator.
+   * \param[in] value The number to be printed.
+   * \param[in] term The field terminator.  Use '\\n' for CR LF.
+   * \return The number of bytes written or -1 if an error occurs.
+   */
+  int printField(uint32_t value, char term);
+  /** Print a file's modify date and time
+   *
+   * \param[in] pr Print stream for output.
+   *
+   * \return The value true is returned for success and
+   * the value false is returned for failure.
+   */
+  bool printModifyDateTime(print_t* pr);
+  /** Print a file's name
+   *
+   * \param[in] pr Print stream for output.
+   *
+   * \return The value true is returned for success and
+   * the value false is returned for failure.
+   */
+  size_t printName(print_t* pr);
+  /** Print a file's size.
+   *
+   * \param[in] pr Print stream for output.
+   *
+   * \return The number of characters printed is returned
+   *         for success and zero is returned for failure.
+   */
+  size_t printFileSize(print_t* pr);
+  /** Print a file's Short File Name.
+   *
+   * \param[in] pr Print stream for output.
+   *
+   * \return The number of characters printed is returned
+   *         for success and zero is returned for failure.
+   */  
+  size_t printSFN(print_t* pr);
+  /** Read the next byte from a file.
+   *
+   * \return For success read returns the next byte in the file as an int.
+   * If an error occurs or end of file is reached -1 is returned.
+   */
+  int read() {
+    uint8_t b;
+    return read(&b, 1) == 1 ? b : -1;
+  }
+  /** Read data from a file starting at the current position.
+   *
+   * \param[out] buf Pointer to the location that will receive the data.
+   *
+   * \param[in] nbyte Maximum number of bytes to read.
+   *
+   * \return For success read() returns the number of bytes read.
+   * A value less than \a nbyte, including zero, will be returned
+   * if end of file is reached.
+   * If an error occurs, read() returns -1.  Possible errors include
+   * read() called before a file has been opened, corrupt file system
+   * or an I/O error occurred.
+   */
+  int read(void* buf, size_t nbyte);
+  /** Read the next directory entry from a directory file.
+   *
+   * \param[out] dir The dir_t struct that will receive the data.
+   *
+   * \return For success readDir() returns the number of bytes read.
+   * A value of zero will be returned if end of file is reached.
+   * If an error occurs, readDir() returns -1.  Possible errors include
+   * readDir() called before a directory has been opened, this is not
+   * a directory file or an I/O error occurred.
+   */
+  int8_t readDir(dir_t* dir);
+  /** Remove a file.
+   *
+   * The directory entry and all data for the file are deleted.
+   *
+   * \note This function should not be used to delete the 8.3 version of a
+   * file that has a long name. For example if a file has the long name
+   * "New Text Document.txt" you should not delete the 8.3 name "NEWTEX~1.TXT".
+   *
+   * \return The value true is returned for success and
+   * the value false is returned for failure.
+   */
+  bool remove();
+  /** Remove a file.
+   *
+   * The directory entry and all data for the file are deleted.
+   *
+   * \param[in] dirFile The directory that contains the file.
+   * \param[in] path Path for the file to be removed.
+   *
+   * \note This function should not be used to delete the 8.3 version of a
+   * file that has a long name. For example if a file has the long name
+   * "New Text Document.txt" you should not delete the 8.3 name "NEWTEX~1.TXT".
+   *
+   * \return The value true is returned for success and
+   * the value false is returned for failure.
+   */
+  static bool remove(FatFile* dirFile, const char* path);
+  /** Set the file's current position to zero. */
+  void rewind() {
+    seekSet(0);
+  }
+  /** Rename a file or subdirectory.
+   *
+   * \param[in] dirFile Directory for the new path.
+   * \param[in] newPath New path name for the file/directory.
+   *
+   * \return The value true is returned for success and
+   * the value false is returned for failure.
+   */
+  bool rename(FatFile* dirFile, const char* newPath);
+  /** Remove a directory file.
+   *
+   * The directory file will be removed only if it is empty and is not the
+   * root directory.  rmdir() follows DOS and Windows and ignores the
+   * read-only attribute for the directory.
+   *
+   * \note This function should not be used to delete the 8.3 version of a
+   * directory that has a long name. For example if a directory has the
+   * long name "New folder" you should not delete the 8.3 name "NEWFOL~1".
+   *
+   * \return The value true is returned for success and
+   * the value false is returned for failure.
+   */
+  bool rmdir();
+  /** Recursively delete a directory and all contained files.
+   *
+   * This is like the Unix/Linux 'rm -rf *' if called with the root directory
+   * hence the name.
+   *
+   * Warning - This will remove all contents of the directory including
+   * subdirectories.  The directory will then be removed if it is not root.
+   * The read-only attribute for files will be ignored.
+   *
+   * \note This function should not be used to delete the 8.3 version of
+   * a directory that has a long name.  See remove() and rmdir().
+   *
+   * \return The value true is returned for success and
+   * the value false is returned for failure.
+   */
+  bool rmRfStar();
+  /** Set the files position to current position + \a pos. See seekSet().
+   * \param[in] offset The new position in bytes from the current position.
+   * \return true for success or false for failure.
+   */
+  bool seekCur(int32_t offset) {
+    return seekSet(m_curPosition + offset);
+  }
+  /** Set the files position to end-of-file + \a offset. See seekSet().
+   * Can't be used for directory files since file size is not defined.
+   * \param[in] offset The new position in bytes from end-of-file.
+   * \return true for success or false for failure.
+   */
+  bool seekEnd(int32_t offset = 0) {
+    return isFile() ? seekSet(m_fileSize + offset) : false;
+  }
+  /** Sets a file's position.
+   *
+   * \param[in] pos The new position in bytes from the beginning of the file.
+   *
+   * \return The value true is returned for success and
+   * the value false is returned for failure.
+   */
+  bool seekSet(uint32_t pos);
+  /** Set the current working directory.
+   *
+   * \param[in] dir New current working directory.
+   *
+   * \return true for success else false.
+   */
+  static bool setCwd(FatFile* dir) {
+    if (!dir->isDir()) {
+      return false;
+    }
+    m_cwd = dir;
+    return true;
+  }
+  /** The sync() call causes all modified data and directory fields
+   * to be written to the storage device.
+   *
+   * \return The value true is returned for success and
+   * the value false is returned for failure.
+   */
+  bool sync();
+  /** Copy a file's timestamps
+   *
+   * \param[in] file File to copy timestamps from.
+   *
+   * \note
+   * Modify and access timestamps may be overwritten if a date time callback
+   * function has been set by dateTimeCallback().
+   *
+   * \return The value true is returned for success and
+   * the value false is returned for failure.
+   */
+  bool timestamp(FatFile* file);
+  /** Set a file's timestamps in its directory entry.
+   *
+   * \param[in] flags Values for \a flags are constructed by a bitwise-inclusive
+   * OR of flags from the following list
+   *
+   * T_ACCESS - Set the file's last access date.
+   *
+   * T_CREATE - Set the file's creation date and time.
+   *
+   * T_WRITE - Set the file's last write/modification date and time.
+   *
+   * \param[in] year Valid range 1980 - 2107 inclusive.
+   *
+   * \param[in] month Valid range 1 - 12 inclusive.
+   *
+   * \param[in] day Valid range 1 - 31 inclusive.
+   *
+   * \param[in] hour Valid range 0 - 23 inclusive.
+   *
+   * \param[in] minute Valid range 0 - 59 inclusive.
+   *
+   * \param[in] second Valid range 0 - 59 inclusive
+   *
+   * \note It is possible to set an invalid date since there is no check for
+   * the number of days in a month.
+   *
+   * \note
+   * Modify and access timestamps may be overwritten if a date time callback
+   * function has been set by dateTimeCallback().
+   *
+   * \return The value true is returned for success and
+   * the value false is returned for failure.
+   */
+  bool timestamp(uint8_t flags, uint16_t year, uint8_t month, uint8_t day,
+                 uint8_t hour, uint8_t minute, uint8_t second);
+  /** Type of file.  You should use isFile() or isDir() instead of fileType()
+   * if possible.
+   *
+   * \return The file or directory type.
+   */
+  uint8_t fileAttr() const {
+    return m_attr;
+  }
+  /** Truncate a file to a specified length.  The current file position
+   * will be maintained if it is less than or equal to \a length otherwise
+   * it will be set to end of file.
+   *
+   * \param[in] length The desired length for the file.
+   *
+   * \return The value true is returned for success and
+   * the value false is returned for failure.
+   */
+  bool truncate(uint32_t length);
+  /** \return FatVolume that contains this file. */
+  FatVolume* volume() const {
+    return m_vol;
+  }
+  /** Write a string to a file. Used by the Arduino Print class.
+   * \param[in] str Pointer to the string.
+   * Use getWriteError to check for errors.
+   * \return count of characters written for success or -1 for failure.
+   */
+  int write(const char* str) {
+    return write(str, strlen(str));
+  }
+  /** Write a single byte.
+   * \param[in] b The byte to be written.
+   * \return +1 for success or -1 for failure.
+   */
+  int write(uint8_t b) {
+    return write(&b, 1);
+  }
+  /** Write data to an open file.
+   *
+   * \note Data is moved to the cache but may not be written to the
+   * storage device until sync() is called.
+   *
+   * \param[in] buf Pointer to the location of the data to be written.
+   *
+   * \param[in] nbyte Number of bytes to write.
+   *
+   * \return For success write() returns the number of bytes written, always
+   * \a nbyte.  If an error occurs, write() returns -1.  Possible errors
+   * include write() is called before a file has been opened, write is called
+   * for a read-only file, device is full, a corrupt file system or an I/O error.
+   *
+   */
+  int write(const void* buf, size_t nbyte);
+//------------------------------------------------------------------------------
+ private:
+  /** This file has not been opened. */
+  static const uint8_t FILE_ATTR_CLOSED = 0;
+  /** File is read-only. */
+  static const uint8_t FILE_ATTR_READ_ONLY = DIR_ATT_READ_ONLY;
+  /** File should be hidden in directory listings. */
+  static const uint8_t FILE_ATTR_HIDDEN = DIR_ATT_HIDDEN;
+  /** Entry is for a system file. */
+  static const uint8_t FILE_ATTR_SYSTEM = DIR_ATT_SYSTEM;
+  /** Entry for normal data file */
+  static const uint8_t FILE_ATTR_FILE = 0X08;
+  /** Entry is for a subdirectory */
+  static const uint8_t FILE_ATTR_SUBDIR = DIR_ATT_DIRECTORY;
+  /** A FAT12 or FAT16 root directory */
+  static const uint8_t FILE_ATTR_ROOT_FIXED = 0X20;
+  /** A FAT32 root directory */
+  static const uint8_t FILE_ATTR_ROOT32 = 0X40;
+  /** Entry is for root. */
+  static const uint8_t FILE_ATTR_ROOT = FILE_ATTR_ROOT_FIXED | FILE_ATTR_ROOT32;
+  /** Directory type bits */
+  static const uint8_t FILE_ATTR_DIR = FILE_ATTR_SUBDIR | FILE_ATTR_ROOT;
+  /** Attributes to copy from directory entry */
+  static const uint8_t FILE_ATTR_COPY = DIR_ATT_READ_ONLY | DIR_ATT_HIDDEN |
+                                        DIR_ATT_SYSTEM | DIR_ATT_DIRECTORY;
+
+  /** experimental don't use */
+
+  bool openParent(FatFile* dir);
+
+  // private functions
+  bool addCluster();
+  bool addDirCluster();
+  dir_t* cacheDirEntry(uint8_t action);
+  static uint8_t lfnChecksum(uint8_t* name);
+  bool lfnUniqueSfn(fname_t* fname);
+  bool openCluster(FatFile* file);
+  static bool parsePathName(const char* str, fname_t* fname, const char** ptr);
+  bool mkdir(FatFile* parent, fname_t* fname);
+  bool open(FatFile* dirFile, fname_t* fname, uint8_t oflag);
+  bool openCachedEntry(FatFile* dirFile, uint16_t cacheIndex, uint8_t oflag,
+                       uint8_t lfnOrd);
+  bool readLBN(uint32_t* lbn);
+  dir_t* readDirCache(bool skipReadOk = false);
+  bool setDirSize();
+
+  // bits defined in m_flags
+  // should be 0X0F
+  static uint8_t const F_OFLAG = (O_ACCMODE | O_APPEND | O_SYNC);
+  // sync of directory entry required
+  static uint8_t const F_FILE_DIR_DIRTY = 0X80;
+
+  // global pointer to cwd dir
+  static FatFile* m_cwd;
+  // data time callback function
+  static void (*m_dateTime)(uint16_t* date, uint16_t* time);
+  // private data
+  static const uint8_t WRITE_ERROR = 0X1;
+  static const uint8_t READ_ERROR  = 0X2;
+  uint8_t    m_attr;             // File attributes
+  uint8_t    m_error;            // Error bits.
+  uint8_t    m_flags;            // See above for definition of m_flags bits
+  uint8_t    m_lfnOrd;
+  uint16_t   m_dirIndex;         // index of directory entry in dir file
+  FatVolume* m_vol;              // volume where file is located
+  uint32_t   m_dirCluster;
+  uint32_t   m_curCluster;       // cluster for current file position
+  uint32_t   m_curPosition;      // current file position
+  uint32_t   m_dirBlock;         // block for this files directory entry
+  uint32_t   m_fileSize;         // file size in bytes
+  uint32_t   m_firstCluster;     // first cluster of file
+};
+#endif  // FatFile_h

+ 683 - 0
SdFat/utility/FatFileLFN.cpp

@@ -0,0 +1,683 @@
+/* FatLib Library
+ * Copyright (C) 2012 by William Greiman
+ *
+ * This file is part of the FatLib Library
+ *
+ * This Library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the FatLib Library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+#include "FatFile.h"
+//------------------------------------------------------------------------------
+//
+uint8_t FatFile::lfnChecksum(uint8_t* name) {
+  uint8_t sum = 0;
+  for (uint8_t i = 0; i < 11; i++) {
+    sum = (((sum & 1) << 7) | ((sum & 0xfe) >> 1)) + name[i];
+  }
+  return sum;
+}
+#if USE_LONG_FILE_NAMES
+//------------------------------------------------------------------------------
+// Saves about 90 bytes of flash on 328 over tolower().
+inline char lfnToLower(char c) {
+  return 'A' <= c && c <= 'Z' ? c + 'a' - 'A' : c;
+}
+//------------------------------------------------------------------------------
+// Daniel Bernstein University of Illinois at Chicago.
+// Original had + instead of ^
+static uint16_t Bernstein(uint16_t hash, const char *str, size_t len) {
+  for (size_t i = 0; i < len; i++) {
+    // hash = hash * 33 ^ str[i];
+    hash = ((hash << 5) + hash) ^ str[i];
+  }
+  return hash;
+}
+//------------------------------------------------------------------------------
+/**
+ * Fetch a 16-bit long file name character.
+ *
+ * \param[in] ldir Pointer to long file name directory entry.
+ * \param[in] i Index of character.
+ * \return The 16-bit character.
+ */
+static uint16_t lfnGetChar(ldir_t *ldir, uint8_t i) {
+  if (i < LDIR_NAME1_DIM) {
+    return ldir->name1[i];
+  } else if (i < (LDIR_NAME1_DIM + LDIR_NAME2_DIM)) {
+    return ldir->name2[i - LDIR_NAME1_DIM];
+  } else if (i < (LDIR_NAME1_DIM + LDIR_NAME2_DIM + LDIR_NAME2_DIM)) {
+    return ldir->name3[i - LDIR_NAME1_DIM - LDIR_NAME2_DIM];
+  }
+  return 0;
+}
+//------------------------------------------------------------------------------
+static bool lfnGetName(ldir_t *ldir, char* name, size_t n) {
+  uint8_t i;
+  size_t k = 13*((ldir->ord & 0X1F) - 1);
+  for (i = 0; i < 13; i++) {
+    uint16_t c = lfnGetChar(ldir, i);
+    if (c == 0 || k >= n) {
+      break;
+    }
+    name[k++] = c >= 0X7F ? '?' : c;
+  }
+  // Terminate with zero byte if name fits.
+  if (k < n && (ldir->ord & LDIR_ORD_LAST_LONG_ENTRY)) {
+    name[k] = 0;
+  }
+  // Truncate if name is too long.
+  name[n - 1] = 0;
+  return true;
+}
+//------------------------------------------------------------------------------
+inline bool lfnLegalChar(char c) {
+  if (c == '/' || c == '\\' || c == '"' || c == '*' ||
+      c == ':' || c == '<' || c == '>' || c == '?' || c == '|') {
+    return false;
+  }
+  return 0X1F < c && c < 0X7F;
+}
+//------------------------------------------------------------------------------
+/**
+ * Store a 16-bit long file name character.
+ *
+ * \param[in] ldir Pointer to long file name directory entry.
+ * \param[in] i Index of character.
+ * \param[in] c  The 16-bit character.
+ */
+static void lfnPutChar(ldir_t *ldir, uint8_t i, uint16_t c) {
+  if (i < LDIR_NAME1_DIM) {
+    ldir->name1[i] = c;
+  } else if (i < (LDIR_NAME1_DIM + LDIR_NAME2_DIM)) {
+    ldir->name2[i - LDIR_NAME1_DIM] = c;
+  } else if (i < (LDIR_NAME1_DIM + LDIR_NAME2_DIM + LDIR_NAME2_DIM)) {
+    ldir->name3[i - LDIR_NAME1_DIM - LDIR_NAME2_DIM] = c;
+  }
+}
+//------------------------------------------------------------------------------
+static void lfnPutName(ldir_t *ldir, const char* name, size_t n) {
+  size_t k = 13*((ldir->ord & 0X1F) - 1);
+  for (uint8_t i = 0; i < 13; i++, k++) {
+    uint16_t c = k < n ? name[k] : k == n ? 0 : 0XFFFF;
+    lfnPutChar(ldir, i, c);
+  }
+}
+//==============================================================================
+bool FatFile::getName(char* name, size_t size) {
+  FatFile dirFile;
+  ldir_t* ldir;
+  if (!isOpen() || size < 13) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  if (!isLFN()) {
+    return getSFN(name);
+  }
+  if (!dirFile.openCluster(this)) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  for (uint8_t ord = 1; ord <= m_lfnOrd; ord++) {
+    if (!dirFile.seekSet(32UL*(m_dirIndex - ord))) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    ldir = reinterpret_cast<ldir_t*>(dirFile.readDirCache());
+    if (!ldir) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    if (ldir->attr != DIR_ATT_LONG_NAME) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    if (ord != (ldir->ord & 0X1F)) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    if (!lfnGetName(ldir, name, size)) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    if (ldir->ord & LDIR_ORD_LAST_LONG_ENTRY) {
+      return true;
+    }
+  }
+  // Fall into fail.
+  DBG_FAIL_MACRO;
+
+fail:
+  name[0] = 0;
+  return false;
+}
+//------------------------------------------------------------------------------
+bool FatFile::openCluster(FatFile* file) {
+  if (file->m_dirCluster == 0) {
+    return openRoot(file->m_vol);
+  }
+  memset(this, 0, sizeof(FatFile));
+  m_attr = FILE_ATTR_SUBDIR;
+  m_flags = O_READ;
+  m_vol = file->m_vol;
+  m_firstCluster = file->m_dirCluster;
+  return true;
+}
+//------------------------------------------------------------------------------
+bool FatFile::parsePathName(const char* path,
+                            fname_t* fname, const char** ptr) {
+  char c;
+  bool is83;
+  uint8_t bit = DIR_NT_LC_BASE;
+  uint8_t lc = 0;
+  uint8_t uc = 0;
+  uint8_t i = 0;
+  uint8_t in = 7;
+  int end;
+  int len = 0;
+  int si;
+  int dot;
+
+  // Skip leading spaces.
+  while (*path == ' ') {
+    path++;
+  }
+  fname->lfn = path;
+
+  for (len = 0; ; len++) {
+    c = path[len];
+    if (c == 0 || isDirSeparator(c)) {
+      break;
+    }
+    if (!lfnLegalChar(c)) {
+      return false;
+    }
+  }
+  // Advance to next path component.
+  for (end = len; path[end] ==  ' ' || isDirSeparator(path[end]); end++) {}
+  *ptr = &path[end];
+
+  // Back over spaces and dots.
+  while (len) {
+    c = path[len - 1];
+    if (c != '.' && c != ' ') {
+      break;
+    }
+    len--;
+  }
+  // Max length of LFN is 255.
+  if (len > 255) {
+    return false;
+  }
+  fname->len = len;
+  // Blank file short name.
+  for (uint8_t k = 0; k < 11; k++) {
+    fname->sfn[k] = ' ';
+  }
+  // skip leading spaces and dots.
+  for (si = 0; path[si] == '.' || path[si] == ' '; si++) {}
+  // Not 8.3 if leading dot or space.
+  is83 = !si;
+
+  // find last dot.
+  for (dot = len - 1; dot >= 0 && path[dot] != '.'; dot--) {}
+  for (; si < len; si++) {
+    c = path[si];
+    if (c == ' ' || (c == '.' && dot != si)) {
+      is83 = false;
+      continue;
+    }
+    if (!legal83Char(c) && si != dot) {
+      is83 = false;
+      c = '_';
+    }
+    if (si == dot || i > in) {
+      if (in == 10) {
+        // Done - extension longer than three characters.
+        is83 = false;
+        break;
+      }
+      if (si != dot) {
+        is83 = false;
+      }
+      // Break if no dot and base-name is longer than eight characters.
+      if (si > dot) {
+        break;
+      }
+      si = dot;
+      in = 10;  // Max index for full 8.3 name.
+      i = 8;    // Place for extension.
+      bit = DIR_NT_LC_EXT;  // bit for extension.
+    } else {
+      if ('a' <= c && c <= 'z') {
+        c += 'A' - 'a';
+        lc |= bit;
+      } else if ('A' <= c && c <= 'Z') {
+        uc |= bit;
+      }
+      fname->sfn[i++] = c;
+      if (i < 7) {
+        fname->seqPos = i;
+      }
+    }
+  }
+  if (fname->sfn[0] == ' ') {
+    return false;
+  }
+
+  if (is83) {
+    fname->flags = lc & uc ? FNAME_FLAG_MIXED_CASE : lc;
+  } else {
+    fname->flags = FNAME_FLAG_LOST_CHARS;
+    fname->sfn[fname->seqPos] = '~';
+    fname->sfn[fname->seqPos + 1] = '1';
+  }
+  return true;
+}
+//------------------------------------------------------------------------------
+bool FatFile::open(FatFile* dirFile, fname_t* fname, uint8_t oflag) {
+  bool fnameFound = false;
+  uint8_t lfnOrd = 0;
+  uint8_t freeNeed;
+  uint8_t freeFound = 0;
+  uint8_t ord = 0;
+  uint8_t chksum = 0;
+  uint16_t freeIndex = 0;
+  uint16_t curIndex;
+  dir_t* dir;
+  ldir_t* ldir;
+  size_t len = fname->len;
+
+  if (!dirFile->isDir() || isOpen()) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // Number of directory entries needed.
+  freeNeed = fname->flags & FNAME_FLAG_NEED_LFN ? 1 + (len + 12)/13 : 1;
+
+  dirFile->rewind();
+  while (1) {
+    curIndex = dirFile->m_curPosition/32;
+    dir = dirFile->readDirCache(true);
+    if (!dir) {
+      if (dirFile->getError()) {
+        DBG_FAIL_MACRO;
+        goto fail;
+      }
+      // At EOF
+      goto create;
+    }
+    if (dir->name[0] == DIR_NAME_DELETED || dir->name[0] == DIR_NAME_FREE) {
+      if (freeFound == 0) {
+        freeIndex = curIndex;
+      }
+      if (freeFound < freeNeed) {
+        freeFound++;
+      }
+      if (dir->name[0] == DIR_NAME_FREE) {
+        goto create;
+      }
+    } else {
+      if (freeFound < freeNeed) {
+        freeFound = 0;
+      }
+    }
+    // skip empty slot or '.' or '..'
+    if (dir->name[0] == DIR_NAME_DELETED || dir->name[0] == '.') {
+      lfnOrd = 0;
+    } else if (DIR_IS_LONG_NAME(dir)) {
+      ldir_t *ldir = reinterpret_cast<ldir_t*>(dir);
+      if (!lfnOrd) {
+        if ((ldir->ord & LDIR_ORD_LAST_LONG_ENTRY) == 0) {
+          continue;
+        }
+        lfnOrd = ord = ldir->ord & 0X1F;
+        chksum = ldir->chksum;
+      } else if (ldir->ord != --ord || chksum != ldir->chksum) {
+        lfnOrd = 0;
+        continue;
+      }
+      size_t k = 13*(ord - 1);
+      if (k >= len) {
+        // Not found.
+        lfnOrd = 0;
+        continue;
+      }
+      for (uint8_t i = 0; i < 13; i++) {
+        uint16_t u = lfnGetChar(ldir, i);
+        if (k == len) {
+          if (u != 0) {
+            // Not found.
+            lfnOrd = 0;
+          }
+          break;
+        }
+        if (u > 255 || lfnToLower(u) != lfnToLower(fname->lfn[k++])) {
+          // Not found.
+          lfnOrd = 0;
+          break;
+        }
+      }
+    } else if (DIR_IS_FILE_OR_SUBDIR(dir)) {
+      if (lfnOrd) {
+        if (1 == ord && lfnChecksum(dir->name) == chksum) {
+          goto found;
+        }
+        DBG_FAIL_MACRO;
+        goto fail;
+      }
+      if (!memcmp(dir->name, fname->sfn, sizeof(fname->sfn))) {
+        if (!(fname->flags & FNAME_FLAG_LOST_CHARS)) {
+          goto found;
+        }
+        fnameFound = true;
+      }
+    } else {
+      lfnOrd = 0;
+    }
+  }
+
+found:
+  // Don't open if create only.
+  if (oflag & O_EXCL) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  goto open;
+
+create:
+  // don't create unless O_CREAT and O_WRITE
+  if (!(oflag & O_CREAT) || !(oflag & O_WRITE)) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // If at EOF start in next cluster.
+  if (freeFound == 0) {
+    freeIndex = curIndex;
+  }
+
+  while (freeFound < freeNeed) {
+    dir = dirFile->readDirCache();
+    if (!dir) {
+      if (dirFile->getError()) {
+        DBG_FAIL_MACRO;
+        goto fail;
+      }
+      // EOF if no error.
+      break;
+    }
+    freeFound++;
+  }
+  while (freeFound < freeNeed) {
+    // Will fail if FAT16 root.
+    if (!dirFile->addDirCluster()) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    // Done if more than one block per cluster.  Max freeNeed is 21.
+    if (dirFile->m_vol->blocksPerCluster() > 1) {
+      break;
+    }
+    freeFound += 16;
+  }
+  if (fnameFound) {
+    if (!dirFile->lfnUniqueSfn(fname)) {
+      goto fail;
+    }
+  }
+  if (!dirFile->seekSet(32UL*freeIndex)) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  lfnOrd = freeNeed - 1;
+  for (uint8_t ord = lfnOrd ; ord ; ord--) {
+    ldir = reinterpret_cast<ldir_t*>(dirFile->readDirCache());
+    if (!ldir) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    dirFile->m_vol->cacheDirty();
+    ldir->ord = ord == lfnOrd ? LDIR_ORD_LAST_LONG_ENTRY | ord : ord;
+    ldir->attr = DIR_ATT_LONG_NAME;
+    ldir->type = 0;
+    ldir->chksum = lfnChecksum(fname->sfn);
+    ldir->mustBeZero = 0;
+    lfnPutName(ldir, fname->lfn, len);
+  }
+  curIndex = dirFile->m_curPosition/32;
+  dir = dirFile->readDirCache();
+  if (!dir) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // initialize as empty file
+  memset(dir, 0, sizeof(dir_t));
+  memcpy(dir->name, fname->sfn, 11);
+
+  // Set base-name and extension lower case bits.
+  dir->reservedNT =  (DIR_NT_LC_BASE | DIR_NT_LC_EXT) & fname->flags;
+
+  // set timestamps
+  if (m_dateTime) {
+    // call user date/time function
+    m_dateTime(&dir->creationDate, &dir->creationTime);
+  } else {
+    // use default date/time
+    dir->creationDate = FAT_DEFAULT_DATE;
+    dir->creationTime = FAT_DEFAULT_TIME;
+  }
+  dir->lastAccessDate = dir->creationDate;
+  dir->lastWriteDate = dir->creationDate;
+  dir->lastWriteTime = dir->creationTime;
+
+  // Force write of entry to device.
+  dirFile->m_vol->cacheDirty();
+
+open:
+  // open entry in cache.
+  if (!openCachedEntry(dirFile, curIndex, oflag, lfnOrd)) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  return true;
+
+fail:
+  return false;
+}
+//------------------------------------------------------------------------------
+size_t FatFile::printName(print_t* pr) {
+  FatFile dirFile;
+  uint16_t u;
+  size_t n = 0;
+  ldir_t* ldir;
+
+  if (!isLFN()) {
+    return printSFN(pr);
+  }
+  if (!dirFile.openCluster(this)) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  for (uint8_t ord = 1; ord <= m_lfnOrd; ord++) {
+    if (!dirFile.seekSet(32UL*(m_dirIndex - ord))) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    ldir = reinterpret_cast<ldir_t*>(dirFile.readDirCache());
+    if (!ldir) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    if (ldir->attr != DIR_ATT_LONG_NAME ||
+        ord != (ldir->ord & 0X1F)) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    for (uint8_t i = 0; i < 13; i++) {
+      u = lfnGetChar(ldir, i);
+      if (u == 0) {
+        // End of name.
+        break;
+      }
+      if (u > 0X7E) {
+        u = '?';
+      }
+      pr->write(static_cast<char>(u));
+      n++;
+    }
+    if (ldir->ord & LDIR_ORD_LAST_LONG_ENTRY) {
+      return n;
+    }
+  }
+  // Fall into fail;
+  DBG_FAIL_MACRO;
+
+fail:
+  return 0;
+}
+//------------------------------------------------------------------------------
+bool FatFile::remove() {
+  bool last;
+  uint8_t chksum;
+  uint8_t ord;
+  FatFile dirFile;
+  dir_t* dir;
+  ldir_t* ldir;
+
+  // Cant' remove not open for write.
+  if (!isFile() || !(m_flags & O_WRITE)) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // Free any clusters.
+  if (m_firstCluster && !m_vol->freeChain(m_firstCluster)) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // Cache directory entry.
+  dir = cacheDirEntry(FatCache::CACHE_FOR_WRITE);
+  if (!dir) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  chksum = lfnChecksum(dir->name);
+
+  // Mark entry deleted.
+  dir->name[0] = DIR_NAME_DELETED;
+
+  // Set this file closed.
+  m_attr = FILE_ATTR_CLOSED;
+
+  // Write entry to device.
+  if (!m_vol->cacheSync()) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  if (!isLFN()) {
+    // Done, no LFN entries.
+    return true;
+  }
+  if (!dirFile.openCluster(this)) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  for (ord = 1; ord <= m_lfnOrd; ord++) {
+    if (!dirFile.seekSet(32UL*(m_dirIndex - ord))) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    ldir = reinterpret_cast<ldir_t*>(dirFile.readDirCache());
+    if (!ldir) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    if (ldir->attr != DIR_ATT_LONG_NAME ||
+        ord != (ldir->ord & 0X1F) ||
+        chksum != ldir->chksum) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    last = ldir->ord & LDIR_ORD_LAST_LONG_ENTRY;
+    ldir->ord = DIR_NAME_DELETED;
+    m_vol->cacheDirty();
+    if (last) {
+      if (!m_vol->cacheSync()) {
+        DBG_FAIL_MACRO;
+        goto fail;
+      }
+      return true;
+    }
+  }
+  // Fall into fail.
+  DBG_FAIL_MACRO;
+
+fail:
+  return false;
+}
+//------------------------------------------------------------------------------
+bool FatFile::lfnUniqueSfn(fname_t* fname) {
+  const uint8_t FIRST_HASH_SEQ = 2;  // min value is 2
+  uint8_t pos = fname->seqPos;;
+  dir_t *dir;
+  uint16_t hex;
+
+  DBG_HALT_IF(!(fname->flags & FNAME_FLAG_LOST_CHARS));
+  DBG_HALT_IF(fname->sfn[pos] != '~' && fname->sfn[pos + 1] != '1');
+
+  for (uint8_t seq = 2; seq < 100; seq++) {
+    if (seq < FIRST_HASH_SEQ) {
+      fname->sfn[pos + 1] = '0' + seq;
+    } else {
+      DBG_PRINT_IF(seq > FIRST_HASH_SEQ);
+      hex = Bernstein(seq + fname->len, fname->lfn, fname->len);
+      if (pos > 3) {
+        // Make space in name for ~HHHH.
+        pos = 3;
+      }
+      for (uint8_t i = pos + 4 ; i > pos; i--) {
+        uint8_t h = hex & 0XF;
+        fname->sfn[i] = h < 10 ? h + '0' : h + 'A' - 10;
+        hex >>= 4;
+      }
+    }
+    fname->sfn[pos] = '~';
+    rewind();
+    while (1) {
+      dir = readDirCache(true);
+      if (!dir) {
+        if (!getError()) {
+          // At EOF and name not found if no error.
+          goto done;
+        }
+        DBG_FAIL_MACRO;
+        goto fail;
+      }
+      if (dir->name[0] == DIR_NAME_FREE) {
+        goto done;
+      }
+      if (DIR_IS_FILE_OR_SUBDIR(dir) && !memcmp(fname->sfn, dir->name, 11)) {
+        // Name found - try another.
+        break;
+      }
+    }
+  }
+  // fall inti fail - too many tries.
+  DBG_FAIL_MACRO;
+
+fail:
+  return false;
+
+done:
+  return true;
+}
+#endif  // #if USE_LONG_FILE_NAMES

+ 248 - 0
SdFat/utility/FatFilePrint.cpp

@@ -0,0 +1,248 @@
+/* FatLib Library
+ * Copyright (C) 2012 by William Greiman
+ *
+ * This file is part of the FatLib Library
+ *
+ * This Library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the FatLib Library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+#include <math.h>
+#include "FatFile.h"
+#include "FmtNumber.h"
+//------------------------------------------------------------------------------
+// print uint8_t with width 2
+static void print2u(print_t* pr, uint8_t v) {
+  char c0 = '?';
+  char c1 = '?';
+  if (v < 100) {
+    c1 = v/10;
+    c0 = v - 10*c1 + '0';
+    c1 += '0';
+  }
+  pr->write(c1);
+  pr->write(c0);
+}
+//------------------------------------------------------------------------------
+static void printU32(print_t* pr, uint32_t v) {
+  char buf[11];
+  char* ptr = buf + sizeof(buf);
+  *--ptr = 0;
+  pr->write(fmtDec(v, ptr));
+}
+//------------------------------------------------------------------------------
+static void printHex(print_t* pr, uint8_t w, uint16_t h) {
+  char buf[5];
+  char* ptr = buf + sizeof(buf);
+  *--ptr = 0;
+  for (uint8_t i = 0; i < w; i++) {
+    char c = h & 0XF;
+    *--ptr = c < 10 ? c + '0' : c + 'A' - 10;
+    h >>= 4;
+  }
+  pr->write(ptr);
+}
+//------------------------------------------------------------------------------
+void FatFile::dmpFile(print_t* pr, uint32_t pos, size_t n) {
+  char text[17];
+  text[16] = 0;
+  if (n >= 0XFFF0) {
+    n = 0XFFF0;
+  }
+  if (!seekSet(pos)) {
+    return;
+  }
+  for (size_t i = 0; i <= n; i++) {
+    if ((i & 15) == 0) {
+      if (i) {
+        pr->write(' ');
+        pr->write(text);
+        if (i == n) {
+          break;
+        }
+      }
+      pr->write('\r');
+      pr->write('\n');
+      if (i >= n) {
+        break;
+      }
+      printHex(pr, 4, i);
+      pr->write(' ');
+    }
+    int16_t h = read();
+    if (h < 0) {
+      break;
+    }
+    pr->write(' ');
+    printHex(pr, 2, h);
+    text[i&15] = ' ' <= h && h < 0X7F ? h : '.';
+  }
+  pr->write('\r');
+  pr->write('\n');
+}
+//------------------------------------------------------------------------------
+void FatFile::ls(print_t* pr, uint8_t flags, uint8_t indent) {
+  FatFile file;
+  rewind();
+  while (file.openNext(this, O_READ)) {
+    // indent for dir level
+    if (!file.isHidden() || (flags & LS_A)) {
+      for (uint8_t i = 0; i < indent; i++) {
+        pr->write(' ');
+      }
+      if (flags & LS_DATE) {
+        file.printModifyDateTime(pr);
+        pr->write(' ');
+      }
+      if (flags & LS_SIZE) {
+        file.printFileSize(pr);
+        pr->write(' ');
+      }
+      file.printName(pr);
+      if (file.isDir()) {
+        pr->write('/');
+      }
+      pr->write('\r');
+      pr->write('\n');
+      if ((flags & LS_R) && file.isDir()) {
+        file.ls(pr, flags, indent + 2);
+      }
+    }
+    file.close();
+  }
+}
+//------------------------------------------------------------------------------
+bool FatFile::printCreateDateTime(print_t* pr) {
+  dir_t dir;
+  if (!dirEntry(&dir)) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  printFatDate(pr, dir.creationDate);
+  pr->write(' ');
+  printFatTime(pr, dir.creationTime);
+  return true;
+
+fail:
+  return false;
+}
+//------------------------------------------------------------------------------
+void FatFile::printFatDate(print_t* pr, uint16_t fatDate) {
+  printU32(pr, FAT_YEAR(fatDate));
+  pr->write('-');
+  print2u(pr, FAT_MONTH(fatDate));
+  pr->write('-');
+  print2u(pr, FAT_DAY(fatDate));
+}
+//------------------------------------------------------------------------------
+void FatFile::printFatTime(print_t* pr, uint16_t fatTime) {
+  print2u(pr, FAT_HOUR(fatTime));
+  pr->write(':');
+  print2u(pr, FAT_MINUTE(fatTime));
+  pr->write(':');
+  print2u(pr, FAT_SECOND(fatTime));
+}
+//------------------------------------------------------------------------------
+/** Template for FatFile::printField() */
+template <typename Type>
+static int printFieldT(FatFile* file, char sign, Type value, char term) {
+  char buf[3*sizeof(Type) + 3];
+  char* str = &buf[sizeof(buf)];
+
+  if (term) {
+    *--str = term;
+    if (term == '\n') {
+      *--str = '\r';
+    }
+  }
+#ifdef OLD_FMT
+  do {
+    Type m = value;
+    value /= 10;
+    *--str = '0' + m - 10*value;
+  } while (value);
+#else  // OLD_FMT
+  str = fmtDec(value, str);
+#endif  // OLD_FMT
+  if (sign) {
+    *--str = sign;
+  }
+  return file->write(str, &buf[sizeof(buf)] - str);
+}
+//------------------------------------------------------------------------------
+
+int FatFile::printField(float value, char term, uint8_t prec) {
+  char buf[24];
+  char* str = &buf[sizeof(buf)];
+  if (term) {
+    *--str = term;
+    if (term == '\n') {
+      *--str = '\r';
+    }
+  }
+  str = fmtFloat(value, str, prec);
+  return write(str, buf + sizeof(buf) - str);
+}
+//------------------------------------------------------------------------------
+int FatFile::printField(uint16_t value, char term) {
+  return printFieldT(this, 0, value, term);
+}
+//------------------------------------------------------------------------------
+int FatFile::printField(int16_t value, char term) {
+  char sign = 0;
+  if (value < 0) {
+    sign = '-';
+    value = -value;
+  }
+  return printFieldT(this, sign, (uint16_t)value, term);
+}
+//------------------------------------------------------------------------------
+int FatFile::printField(uint32_t value, char term) {
+  return printFieldT(this, 0, value, term);
+}
+//------------------------------------------------------------------------------
+int FatFile::printField(int32_t value, char term) {
+  char sign = 0;
+  if (value < 0) {
+    sign = '-';
+    value = -value;
+  }
+  return printFieldT(this, sign, (uint32_t)value, term);
+}
+//------------------------------------------------------------------------------
+bool FatFile::printModifyDateTime(print_t* pr) {
+  dir_t dir;
+  if (!dirEntry(&dir)) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  printFatDate(pr, dir.lastWriteDate);
+  pr->write(' ');
+  printFatTime(pr, dir.lastWriteTime);
+  return true;
+
+fail:
+  return false;
+}
+
+//------------------------------------------------------------------------------
+size_t FatFile::printFileSize(print_t* pr) {
+  char buf[11];
+  char *ptr = buf + sizeof(buf);
+  *--ptr = 0;
+  ptr = fmtDec(fileSize(), ptr);
+  while (ptr > buf) {
+    *--ptr = ' ';
+  }
+  return pr->write(buf);
+}

+ 273 - 0
SdFat/utility/FatFileSFN.cpp

@@ -0,0 +1,273 @@
+/* FatLib Library
+ * Copyright (C) 2012 by William Greiman
+ *
+ * This file is part of the FatLib Library
+ *
+ * This Library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the FatLib Library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+#include "FatFile.h"
+#include "FatFileSystem.h"
+//------------------------------------------------------------------------------
+bool FatFile::getSFN(char* name) {
+  dir_t* dir;
+  if (!isOpen()) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  if (isRoot()) {
+    name[0] = '/';
+    name[1] = '\0';
+    return true;
+  }
+  // cache entry
+  dir = cacheDirEntry(FatCache::CACHE_FOR_READ);
+  if (!dir) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // format name
+  dirName(dir, name);
+  return true;
+
+fail:
+  return false;
+}
+//------------------------------------------------------------------------------
+size_t FatFile::printSFN(print_t* pr) {
+  char name[13];
+  if (!getSFN(name)) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  return pr->write(name);
+
+fail:
+  return 0;
+}
+#if !USE_LONG_FILE_NAMES
+//------------------------------------------------------------------------------
+bool FatFile::getName(char* name, size_t size) {
+  return size < 13 ? 0 : getSFN(name);
+}
+//------------------------------------------------------------------------------
+// format directory name field from a 8.3 name string
+bool FatFile::parsePathName(const char* path, fname_t* fname,
+                            const char** ptr) {
+  uint8_t uc = 0;
+  uint8_t lc = 0;
+  uint8_t bit = FNAME_FLAG_LC_BASE;
+  // blank fill name and extension
+  for (uint8_t i = 0; i < 11; i++) {
+    fname->sfn[i] = ' ';
+  }
+
+  for (uint8_t i = 0, n = 7;; path++) {
+    uint8_t c = *path;
+    if (c == 0 || isDirSeparator(c)) {
+      // Done.
+      break;
+    }
+    if (c == '.' && n == 7) {
+      n = 10;  // max index for full 8.3 name
+      i = 8;   // place for extension
+
+      // bit for extension.
+      bit = FNAME_FLAG_LC_EXT;
+    } else {
+      if (!legal83Char(c) || i > n) {
+        DBG_FAIL_MACRO;
+        goto fail;
+      }
+      if ('a' <= c && c <= 'z') {
+        c += 'A' - 'a';
+        lc |= bit;
+      } else if ('A' <= c && c <= 'Z') {
+        uc |= bit;
+      }
+      fname->sfn[i++] = c;
+    }
+  }
+  // must have a file name, extension is optional
+  if (fname->sfn[0] == ' ') {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // Set base-name and extension bits.
+  fname->flags = lc & uc ? 0 : lc;
+  while (isDirSeparator(*path)) {
+    path++;
+  }
+  *ptr = path;
+  return true;
+
+fail:
+  return false;
+}
+//------------------------------------------------------------------------------
+// open with filename in fname
+#define SFN_OPEN_USES_CHKSUM 0
+bool FatFile::open(FatFile* dirFile, fname_t* fname, uint8_t oflag) {
+  bool emptyFound = false;
+#if SFN_OPEN_USES_CHKSUM
+  uint8_t chksum;
+#endif
+  uint8_t lfnOrd = 0;
+  uint16_t emptyIndex;
+  uint16_t index = 0;
+  dir_t* dir;
+  ldir_t* ldir;
+
+  dirFile->rewind();
+  while (1) {
+    if (!emptyFound) {
+      emptyIndex = index;
+    }
+    dir = dirFile->readDirCache(true);
+    if (!dir) {
+      if (dirFile->getError())  {
+        DBG_FAIL_MACRO;
+        goto fail;
+      }
+      // At EOF if no error.
+      break;
+    }
+    if (dir->name[0] == DIR_NAME_FREE) {
+      emptyFound = true;
+      break;
+    }
+    if (dir->name[0] == DIR_NAME_DELETED) {
+      lfnOrd = 0;
+      emptyFound = true;
+    } else if (DIR_IS_FILE_OR_SUBDIR(dir)) {
+      if (!memcmp(fname->sfn, dir->name, 11)) {
+        // don't open existing file if O_EXCL
+        if (oflag & O_EXCL) {
+          DBG_FAIL_MACRO;
+          goto fail;
+        }
+#if SFN_OPEN_USES_CHKSUM
+        if (lfnOrd && chksum != lfnChecksum(dir->name)) {
+          DBG_FAIL_MACRO;
+          goto fail;
+        }
+#endif  // SFN_OPEN_USES_CHKSUM
+        if (!openCachedEntry(dirFile, index, oflag, lfnOrd)) {
+          DBG_FAIL_MACRO;
+          goto fail;
+        }
+        return true;
+      } else {
+        lfnOrd = 0;
+      }
+    } else if (DIR_IS_LONG_NAME(dir)) {
+      ldir = reinterpret_cast<ldir_t*>(dir);
+      if (ldir->ord & LDIR_ORD_LAST_LONG_ENTRY) {
+        lfnOrd = ldir->ord & 0X1F;
+#if SFN_OPEN_USES_CHKSUM
+        chksum = ldir->chksum;
+#endif  // SFN_OPEN_USES_CHKSUM
+      }
+    } else {
+      lfnOrd = 0;
+    }
+    index++;
+  }
+  // don't create unless O_CREAT and O_WRITE
+  if (!(oflag & O_CREAT) || !(oflag & O_WRITE)) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  if (emptyFound) {
+    index = emptyIndex;
+  } else {
+    if (!dirFile->addDirCluster()) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+  }
+  if (!dirFile->seekSet(32UL*index)) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  dir = dirFile->readDirCache();
+  if (!dir) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // initialize as empty file
+  memset(dir, 0, sizeof(dir_t));
+  memcpy(dir->name, fname->sfn, 11);
+
+  // Set base-name and extension lower case bits.
+  dir->reservedNT =  (DIR_NT_LC_BASE | DIR_NT_LC_EXT) & fname->flags;
+
+  // set timestamps
+  if (m_dateTime) {
+    // call user date/time function
+    m_dateTime(&dir->creationDate, &dir->creationTime);
+  } else {
+    // use default date/time
+    dir->creationDate = FAT_DEFAULT_DATE;
+    dir->creationTime = FAT_DEFAULT_TIME;
+  }
+  dir->lastAccessDate = dir->creationDate;
+  dir->lastWriteDate = dir->creationDate;
+  dir->lastWriteTime = dir->creationTime;
+
+  // Force write of entry to device.
+  dirFile->m_vol->cacheDirty();
+
+  // open entry in cache.
+  return openCachedEntry(dirFile, index, oflag, 0);
+
+fail:
+  return false;
+}
+//------------------------------------------------------------------------------
+size_t FatFile::printName(print_t* pr) {
+  return printSFN(pr);
+}
+//------------------------------------------------------------------------------
+bool FatFile::remove() {
+  dir_t* dir;
+  // Can't remove if LFN or not open for write.
+  if (!isFile() || isLFN() || !(m_flags & O_WRITE)) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // Free any clusters.
+  if (m_firstCluster && !m_vol->freeChain(m_firstCluster)) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // Cache directory entry.
+  dir = cacheDirEntry(FatCache::CACHE_FOR_WRITE);
+  if (!dir) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // Mark entry deleted.
+  dir->name[0] = DIR_NAME_DELETED;
+
+  // Set this file closed.
+  m_attr = FILE_ATTR_CLOSED;
+
+  // Write entry to device.
+  return m_vol->cacheSync();
+
+fail:
+  return false;
+}
+#endif  // !USE_LONG_FILE_NAMES

+ 310 - 0
SdFat/utility/FatFileSystem.h

@@ -0,0 +1,310 @@
+/* FatLib Library
+ * Copyright (C) 2013 by William Greiman
+ *
+ * This file is part of the FatLib Library
+ *
+ * This Library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the FatLib Library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+#ifndef FatFileSystem_h
+#define FatFileSystem_h
+#include "FatVolume.h"
+#include "FatFile.h"
+#include "ArduinoFiles.h"
+/**
+ * \file
+ * \brief FatFileSystem class
+ */
+//------------------------------------------------------------------------------
+/**
+ * \class FatFileSystem
+ * \brief Integration class for the FatLib library.
+ */
+class FatFileSystem : public  FatVolume {
+ public:
+  /**
+   * Initialize an FatFileSystem object.
+   * \param[in] part partition to initialize.
+   * \return The value true is returned for success and
+   * the value false is returned for failure.
+   */
+  bool begin(uint8_t part = 0) {
+    vwd()->close();
+    return (part ? init(part) : init(1) || init(0))
+            && vwd()->openRoot(this) && FatFile::setCwd(vwd());
+  }
+#if ENABLE_ARDUINO_FEATURES
+   /** List the directory contents of the volume working directory to Serial.
+   *
+   * \param[in] flags The inclusive OR of
+   *
+   * LS_DATE - %Print file modification date
+   *
+   * LS_SIZE - %Print file size.
+   *
+   * LS_R - Recursive list of subdirectories.
+   */
+  void ls(uint8_t flags = 0) {
+    ls(&Serial, flags);
+  }
+  /** List the directory contents of a directory to Serial.
+   *
+   * \param[in] path directory to list.
+   *
+   * \param[in] flags The inclusive OR of
+   *
+   * LS_DATE - %Print file modification date
+   *
+   * LS_SIZE - %Print file size.
+   *
+   * LS_R - Recursive list of subdirectories.
+   */
+  void ls(const char* path, uint8_t flags = 0) {
+    ls(&Serial, path, flags);
+  }
+  /** open a file
+   *
+   * \param[in] path location of file to be opened.
+   * \param[in] mode open mode flags.
+   * \return a File object.
+   */
+  File open(const char *path, uint8_t mode = FILE_READ) {
+    File tmpFile;
+    tmpFile.open(vwd(), path, mode);
+    return tmpFile;
+  }
+#endif  // ENABLE_ARDUINO_FEATURES
+  /** Change a volume's working directory to root
+   *
+   * Changes the volume's working directory to the SD's root directory.
+   * Optionally set the current working directory to the volume's
+   * working directory.
+   *
+   * \param[in] set_cwd Set the current working directory to this volume's
+   *  working directory if true.
+   *
+   * \return The value true is returned for success and
+   * the value false is returned for failure.
+   */
+  bool chdir(bool set_cwd = false) {
+    vwd()->close();
+    return vwd()->openRoot(this) && (set_cwd ? FatFile::setCwd(vwd()) : true);
+  }
+  /** Change a volume's working directory
+   *
+   * Changes the volume working directory to the \a path subdirectory.
+   * Optionally set the current working directory to the volume's
+   * working directory.
+   *
+   * Example: If the volume's working directory is "/DIR", chdir("SUB")
+   * will change the volume's working directory from "/DIR" to "/DIR/SUB".
+   *
+   * If path is "/", the volume's working directory will be changed to the
+   * root directory
+   *
+   * \param[in] path The name of the subdirectory.
+   *
+   * \param[in] set_cwd Set the current working directory to this volume's
+   *  working directory if true.
+   *
+   * \return The value true is returned for success and
+   * the value false is returned for failure.
+   */
+  //----------------------------------------------------------------------------
+  bool chdir(const char *path, bool set_cwd = false) {
+    FatFile dir;
+    if (path[0] == '/' && path[1] == '\0') {
+      return chdir(set_cwd);
+    }
+    if (!dir.open(vwd(), path, O_READ)) {
+      goto fail;
+    }
+    if (!dir.isDir()) {
+      goto fail;
+    }
+//    *m_vwd = dir;
+    m_vwd = dir;
+    if (set_cwd) {
+      FatFile::setCwd(vwd());
+    }
+    return true;
+
+fail:
+    return false;
+  }
+  //----------------------------------------------------------------------------
+  /** Set the current working directory to a volume's working directory.
+   *
+   * This is useful with multiple SD cards.
+   *
+   * The current working directory is changed to this
+   * volume's working directory.
+   *
+   * This is like the Windows/DOS \<drive letter>: command.
+   */
+  void chvol() {
+    FatFile::setCwd(vwd());
+  }
+  //----------------------------------------------------------------------------
+  /**
+   * Test for the existence of a file.
+   *
+   * \param[in] path Path of the file to be tested for.
+   *
+   * \return true if the file exists else false.
+   */
+  bool exists(const char* path) {
+    return vwd()->exists(path);
+  }
+  //----------------------------------------------------------------------------
+  /** List the directory contents of the volume working directory.
+   *
+   * \param[in] pr Print stream for list.
+   *
+   * \param[in] flags The inclusive OR of
+   *
+   * LS_DATE - %Print file modification date
+   *
+   * LS_SIZE - %Print file size.
+   *
+   * LS_R - Recursive list of subdirectories.
+   */
+  void ls(print_t* pr, uint8_t flags) {
+    vwd()->ls(pr, flags);
+  }
+  //----------------------------------------------------------------------------
+  /** List the directory contents of a directory.
+   *
+   * \param[in] pr Print stream for list.
+   *
+   * \param[in] path directory to list.
+   *
+   * \param[in] flags The inclusive OR of
+   *
+   * LS_DATE - %Print file modification date
+   *
+   * LS_SIZE - %Print file size.
+   *
+   * LS_R - Recursive list of subdirectories.
+   */
+  void ls(print_t* pr, const char* path, uint8_t flags) {
+    FatFile dir;
+    dir.open(vwd(), path, O_READ);
+    dir.ls(pr, flags);
+  }
+  //----------------------------------------------------------------------------
+  /** Make a subdirectory in the volume working directory.
+   *
+   * \param[in] path A path with a valid 8.3 DOS name for the subdirectory.
+   *
+   * \param[in] pFlag Create missing parent directories if true.
+   *
+   * \return The value true is returned for success and
+   * the value false is returned for failure.
+   */
+  bool mkdir(const char* path, bool pFlag = true) {
+    FatFile sub;
+    return sub.mkdir(vwd(), path, pFlag);
+  }
+  //----------------------------------------------------------------------------
+  /** Remove a file from the volume working directory.
+  *
+  * \param[in] path A path with a valid 8.3 DOS name for the file.
+  *
+  * \return The value true is returned for success and
+  * the value false is returned for failure.
+  */
+  bool remove(const char* path) {
+    return FatFile::remove(vwd(), path);
+  }
+  //----------------------------------------------------------------------------
+  /** Rename a file or subdirectory.
+   *
+   * \param[in] oldPath Path name to the file or subdirectory to be renamed.
+   *
+   * \param[in] newPath New path name of the file or subdirectory.
+   *
+   * The \a newPath object must not exist before the rename call.
+   *
+   * The file to be renamed must not be open.  The directory entry may be
+   * moved and file system corruption could occur if the file is accessed by
+   * a file object that was opened before the rename() call.
+   *
+   * \return The value true is returned for success and
+   * the value false is returned for failure.
+   */
+  bool rename(const char *oldPath, const char *newPath) {
+    FatFile file;
+    if (!file.open(vwd(), oldPath, O_READ)) {
+      return false;
+    }
+    return file.rename(vwd(), newPath);
+  }
+  //----------------------------------------------------------------------------
+  /** Remove a subdirectory from the volume's working directory.
+   *
+   * \param[in] path A path with a valid 8.3 DOS name for the subdirectory.
+   *
+   * The subdirectory file will be removed only if it is empty.
+   *
+   * \return The value true is returned for success and
+   * the value false is returned for failure.
+   */
+  bool rmdir(const char* path) {
+    FatFile sub;
+    if (!sub.open(vwd(), path, O_READ)) {
+      return false;
+    }
+    return sub.rmdir();
+  }
+  //----------------------------------------------------------------------------
+  /** Truncate a file to a specified length.  The current file position
+   * will be maintained if it is less than or equal to \a length otherwise
+   * it will be set to end of file.
+   *
+   * \param[in] path A path with a valid 8.3 DOS name for the file.
+   * \param[in] length The desired length for the file.
+   *
+   * \return The value true is returned for success and
+   * the value false is returned for failure.
+   */
+  bool truncate(const char* path, uint32_t length) {
+    FatFile file;
+    if (!file.open(vwd(), path, O_WRITE)) {
+      return false;
+    }
+    return file.truncate(length);
+  }
+  /** \return a pointer to the FatVolume object. */
+  FatVolume* vol() {
+    return this;
+  }
+  /** \return a pointer to the volume working directory. */
+  FatFile* vwd() {
+    return &m_vwd;
+  }
+  /** Wipe all data from the volume. You must reinitialize the volume before
+   *  accessing it again.
+   * \param[in] pr print stream for status dots.
+   * \return true for success else false.
+   */
+  bool wipe(print_t* pr = 0) {
+    vwd()->close();
+    return FatVolume::wipe(pr);
+  }
+
+ private:
+  FatFile m_vwd;
+};
+#endif  // FatFileSystem_h

+ 33 - 0
SdFat/utility/FatLib.h

@@ -0,0 +1,33 @@
+/* FatLib Library
+ * Copyright (C) 2013 by William Greiman
+ *
+ * This file is part of the FatLib Library
+ *
+ * This Library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the FatLib Library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+#ifndef FatLib_h
+#define FatLib_h
+#include "ArduinoFiles.h"
+#include "ArduinoStream.h"
+#include "FatFileSystem.h"
+#include "FatLibConfig.h"
+#include "FatVolume.h"
+#include "FatFile.h"
+#include "StdioStream.h"
+#include "fstream.h"
+//------------------------------------------------------------------------------
+/** FatFileSystem version YYYYMMDD */
+#define FAT_LIB_VERSION 20150131
+#endif  // FatLib_h

+ 143 - 0
SdFat/utility/FatLibConfig.h

@@ -0,0 +1,143 @@
+/* FatLib Library
+ * Copyright (C) 2013 by William Greiman
+ *
+ * This file is part of the FatLib Library
+ *
+ * This Library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the FatLib Library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+/**
+ * \file
+ * \brief configuration definitions
+ */
+#ifndef FatLibConfig_h
+#define FatLibConfig_h
+#include <stdint.h>
+// Allow this file to override defaults.
+#include "../SdFatConfig.h"
+
+#ifdef __AVR__
+#include <avr/io.h>
+#endif  // __AVR__
+//------------------------------------------------------------------------------
+/**
+ * Set USE_LONG_FILE_NAMES nonzero to use long file names (LFN).
+ * Long File Name are limited to a maximum length of 255 characters.
+ *
+ * This implementation allows 7-bit characters in the range
+ * 0X20 to 0X7E.  The following characters are not allowed:
+ *
+ *  < (less than)
+ *  > (greater than)
+ *  : (colon)
+ *  " (double quote)
+ *  / (forward slash)
+ *  \ (backslash)
+ *  | (vertical bar or pipe)
+ *  ? (question mark)
+ *  * (asterisk)
+ *
+ */
+#ifndef USE_LONG_FILE_NAMES
+#define USE_LONG_FILE_NAMES 1
+#endif  // USE_LONG_FILE_NAMES
+//------------------------------------------------------------------------------
+/** 
+ * Set ARDUINO_FILE_USES_STREAM nonzero to use Stream as the base class
+ * for the Arduino File class.  If ARDUINO_FILE_USES_STREAM is zero, Print
+ * will be used as the base class for the Arduino File class.
+ *
+ * You can save some flash if you do not use Stream input functions such as
+ * find(), findUntil(), readBytesUntil(), readString(), readStringUntil(), 
+ * parseInt(), and parsefloat().
+ */
+#ifndef ARDUINO_FILE_USES_STREAM
+#define ARDUINO_FILE_USES_STREAM 1
+#endif  // ARDUINO_FILE_USES_STREAM
+//------------------------------------------------------------------------------
+/**
+ * Set USE_SEPARATE_FAT_CACHE non-zero to use a second 512 byte cache
+ * for FAT table entries.  Improves performance for large writes that
+ * are not a multiple of 512 bytes.
+ */
+#ifndef USE_SEPARATE_FAT_CACHE
+#ifdef __arm__
+#define USE_SEPARATE_FAT_CACHE 1
+#else  // __arm__
+#define USE_SEPARATE_FAT_CACHE 0
+#endif  // __arm__
+#endif  // USE_SEPARATE_FAT_CACHE
+//------------------------------------------------------------------------------
+/**
+ * Set USE_MULTI_BLOCK_IO non-zero to use multi-block SD read/write.
+ *
+ * Don't use mult-block read/write on small AVR boards.
+ */
+#ifndef USE_MULTI_BLOCK_IO
+#if defined(RAMEND) && RAMEND < 3000
+#define USE_MULTI_BLOCK_IO 0
+#else  // RAMEND
+#define USE_MULTI_BLOCK_IO 1
+#endif  // RAMEND
+#endif  // USE_MULTI_BLOCK_IO
+//------------------------------------------------------------------------------
+/**
+ * Set DESTRUCTOR_CLOSES_FILE non-zero to close a file in its destructor.
+ *
+ * Causes use of lots of heap in ARM.
+ */
+#ifndef DESTRUCTOR_CLOSES_FILE
+#define DESTRUCTOR_CLOSES_FILE 0
+#endif  // DESTRUCTOR_CLOSES_FILE
+//------------------------------------------------------------------------------
+/**
+ * Call flush for endl if ENDL_CALLS_FLUSH is non-zero
+ *
+ * The standard for iostreams is to call flush.  This is very costly for
+ * SdFat.  Each call to flush causes 2048 bytes of I/O to the SD.
+ *
+ * SdFat has a single 512 byte buffer for I/O so it must write the current
+ * data block to the SD, read the directory block from the SD, update the
+ * directory entry, write the directory block to the SD and read the data
+ * block back into the buffer.
+ *
+ * The SD flash memory controller is not designed for this many rewrites
+ * so performance may be reduced by more than a factor of 100.
+ *
+ * If ENDL_CALLS_FLUSH is zero, you must call flush and/or close to force
+ * all data to be written to the SD.
+ */
+#ifndef  ENDL_CALLS_FLUSH
+#define ENDL_CALLS_FLUSH 0
+#endif  // ENDL_CALLS_FLUSH
+//------------------------------------------------------------------------------
+/**
+ * Allow FAT12 volumes if FAT12_SUPPORT is non-zero.
+ * FAT12 has not been well tested.
+ */
+#ifndef FAT12_SUPPORT
+#define FAT12_SUPPORT 0
+#endif  // FAT12_SUPPORT
+//------------------------------------------------------------------------------
+/**
+ *  Enable Extra features for Arduino.
+ */
+#ifndef ENABLE_ARDUINO_FEATURES
+#if defined(ARDUINO) || defined(DOXYGEN)
+#define ENABLE_ARDUINO_FEATURES 1
+#else  //  #if defined(ARDUINO) || defined(DOXYGEN)
+#define ENABLE_ARDUINO_FEATURES 0
+#endif  //  defined(ARDUINO) || defined(DOXYGEN)
+#endif  // ENABLE_ARDUINO_FEATURES
+#endif  // FatLibConfig_h

+ 412 - 335
SdFat/utility/FatStructs.h

@@ -43,56 +43,56 @@ uint8_t const EXTENDED_BOOT_SIG = 0X29;
  * The MBR partition table has four entries.
  */
 struct partitionTable {
-          /**
-           * Boot Indicator . Indicates whether the volume is the active
-           * partition.  Legal values include: 0X00. Do not use for booting.
-           * 0X80 Active partition.
-           */
+  /**
+   * Boot Indicator . Indicates whether the volume is the active
+   * partition.  Legal values include: 0X00. Do not use for booting.
+   * 0X80 Active partition.
+   */
   uint8_t  boot;
-          /**
-            * Head part of Cylinder-head-sector address of the first block in
-            * the partition. Legal values are 0-255. Only used in old PC BIOS.
-            */
+  /**
+    * Head part of Cylinder-head-sector address of the first block in
+    * the partition. Legal values are 0-255. Only used in old PC BIOS.
+    */
   uint8_t  beginHead;
-          /**
-           * Sector part of Cylinder-head-sector address of the first block in
-           * the partition. Legal values are 1-63. Only used in old PC BIOS.
-           */
+  /**
+   * Sector part of Cylinder-head-sector address of the first block in
+   * the partition. Legal values are 1-63. Only used in old PC BIOS.
+   */
   unsigned beginSector : 6;
-           /** High bits cylinder for first block in partition. */
+  /** High bits cylinder for first block in partition. */
   unsigned beginCylinderHigh : 2;
-          /**
-           * Combine beginCylinderLow with beginCylinderHigh. Legal values
-           * are 0-1023.  Only used in old PC BIOS.
-           */
+  /**
+   * Combine beginCylinderLow with beginCylinderHigh. Legal values
+   * are 0-1023.  Only used in old PC BIOS.
+   */
   uint8_t  beginCylinderLow;
-          /**
-           * Partition type. See defines that begin with PART_TYPE_ for
-           * some Microsoft partition types.
-           */
+  /**
+   * Partition type. See defines that begin with PART_TYPE_ for
+   * some Microsoft partition types.
+   */
   uint8_t  type;
-          /**
-           * head part of cylinder-head-sector address of the last sector in the
-           * partition.  Legal values are 0-255. Only used in old PC BIOS.
-           */
+  /**
+   * head part of cylinder-head-sector address of the last sector in the
+   * partition.  Legal values are 0-255. Only used in old PC BIOS.
+   */
   uint8_t  endHead;
-          /**
-           * Sector part of cylinder-head-sector address of the last sector in
-           * the partition.  Legal values are 1-63. Only used in old PC BIOS.
-           */
+  /**
+   * Sector part of cylinder-head-sector address of the last sector in
+   * the partition.  Legal values are 1-63. Only used in old PC BIOS.
+   */
   unsigned endSector : 6;
-           /** High bits of end cylinder */
+  /** High bits of end cylinder */
   unsigned endCylinderHigh : 2;
-          /**
-           * Combine endCylinderLow with endCylinderHigh. Legal values
-           * are 0-1023.  Only used in old PC BIOS.
-           */
+  /**
+   * Combine endCylinderLow with endCylinderHigh. Legal values
+   * are 0-1023.  Only used in old PC BIOS.
+   */
   uint8_t  endCylinderLow;
-           /** Logical block address of the first block in the partition. */
+  /** Logical block address of the first block in the partition. */
   uint32_t firstSector;
-           /** Length of the partition, in blocks. */
+  /** Length of the partition, in blocks. */
   uint32_t totalSectors;
-}__attribute__((packed));
+} __attribute__((packed));
 /** Type name for partitionTable */
 typedef struct partitionTable part_t;
 //------------------------------------------------------------------------------
@@ -104,19 +104,19 @@ typedef struct partitionTable part_t;
  * The first block of a storage device that is formatted with a MBR.
  */
 struct masterBootRecord {
-           /** Code Area for master boot program. */
+  /** Code Area for master boot program. */
   uint8_t  codeArea[440];
-           /** Optional Windows NT disk signature. May contain boot code. */
+  /** Optional Windows NT disk signature. May contain boot code. */
   uint32_t diskSignature;
-           /** Usually zero but may be more boot code. */
+  /** Usually zero but may be more boot code. */
   uint16_t usuallyZero;
-           /** Partition tables. */
+  /** Partition tables. */
   part_t   part[4];
-           /** First MBR signature byte. Must be 0X55 */
+  /** First MBR signature byte. Must be 0X55 */
   uint8_t  mbrSig0;
-           /** Second MBR signature byte. Must be 0XAA */
+  /** Second MBR signature byte. Must be 0XAA */
   uint8_t  mbrSig1;
-}__attribute__((packed));
+} __attribute__((packed));
 /** Type name for masterBootRecord */
 typedef struct masterBootRecord mbr_t;
 //------------------------------------------------------------------------------
@@ -127,124 +127,124 @@ typedef struct masterBootRecord mbr_t;
  *
  */
 struct fat_boot {
-         /**
-          * The first three bytes of the boot sector must be valid,
-          * executable x 86-based CPU instructions. This includes a
-          * jump instruction that skips the next non-executable bytes.
-          */
+  /**
+   * The first three bytes of the boot sector must be valid,
+   * executable x 86-based CPU instructions. This includes a
+   * jump instruction that skips the next non-executable bytes.
+   */
   uint8_t jump[3];
-         /**
-          * This is typically a string of characters that identifies
-          * the operating system that formatted the volume.
-          */
+  /**
+   * This is typically a string of characters that identifies
+   * the operating system that formatted the volume.
+   */
   char    oemId[8];
-          /**
-           * The size of a hardware sector. Valid decimal values for this
-           * field are 512, 1024, 2048, and 4096. For most disks used in
-           * the United States, the value of this field is 512.
-           */
+  /**
+   * The size of a hardware sector. Valid decimal values for this
+   * field are 512, 1024, 2048, and 4096. For most disks used in
+   * the United States, the value of this field is 512.
+   */
   uint16_t bytesPerSector;
-          /**
-           * Number of sectors per allocation unit. This value must be a
-           * power of 2 that is greater than 0. The legal values are
-           * 1, 2, 4, 8, 16, 32, 64, and 128.  128 should be avoided.
-           */
+  /**
+   * Number of sectors per allocation unit. This value must be a
+   * power of 2 that is greater than 0. The legal values are
+   * 1, 2, 4, 8, 16, 32, 64, and 128.  128 should be avoided.
+   */
   uint8_t  sectorsPerCluster;
-          /**
-           * The number of sectors preceding the start of the first FAT,
-           * including the boot sector. The value of this field is always 1.
-           */
+  /**
+   * The number of sectors preceding the start of the first FAT,
+   * including the boot sector. The value of this field is always 1.
+   */
   uint16_t reservedSectorCount;
-          /**
-           * The number of copies of the FAT on the volume.
-           * The value of this field is always 2.
-           */
+  /**
+   * The number of copies of the FAT on the volume.
+   * The value of this field is always 2.
+   */
   uint8_t  fatCount;
-          /**
-           * For FAT12 and FAT16 volumes, this field contains the count of
-           * 32-byte directory entries in the root directory. For FAT32 volumes,
-           * this field must be set to 0. For FAT12 and FAT16 volumes, this
-           * value should always specify a count that when multiplied by 32
-           * results in a multiple of bytesPerSector.  FAT16 volumes should
-           * use the value 512.
-           */
+  /**
+   * For FAT12 and FAT16 volumes, this field contains the count of
+   * 32-byte directory entries in the root directory. For FAT32 volumes,
+   * this field must be set to 0. For FAT12 and FAT16 volumes, this
+   * value should always specify a count that when multiplied by 32
+   * results in a multiple of bytesPerSector.  FAT16 volumes should
+   * use the value 512.
+   */
   uint16_t rootDirEntryCount;
-          /**
-           * This field is the old 16-bit total count of sectors on the volume.
-           * This count includes the count of all sectors in all four regions
-           * of the volume. This field can be 0; if it is 0, then totalSectors32
-           * must be non-zero.  For FAT32 volumes, this field must be 0. For
-           * FAT12 and FAT16 volumes, this field contains the sector count, and
-           * totalSectors32 is 0 if the total sector count fits
-           * (is less than 0x10000).
-           */
+  /**
+   * This field is the old 16-bit total count of sectors on the volume.
+   * This count includes the count of all sectors in all four regions
+   * of the volume. This field can be 0; if it is 0, then totalSectors32
+   * must be non-zero.  For FAT32 volumes, this field must be 0. For
+   * FAT12 and FAT16 volumes, this field contains the sector count, and
+   * totalSectors32 is 0 if the total sector count fits
+   * (is less than 0x10000).
+   */
   uint16_t totalSectors16;
-          /**
-           * This dates back to the old MS-DOS 1.x media determination and is
-           * no longer usually used for anything.  0xF8 is the standard value
-           * for fixed (nonremovable) media. For removable media, 0xF0 is
-           * frequently used. Legal values are 0xF0 or 0xF8-0xFF.
-           */
+  /**
+   * This dates back to the old MS-DOS 1.x media determination and is
+   * no longer usually used for anything.  0xF8 is the standard value
+   * for fixed (non-removable) media. For removable media, 0xF0 is
+   * frequently used. Legal values are 0xF0 or 0xF8-0xFF.
+   */
   uint8_t  mediaType;
-          /**
-           * Count of sectors occupied by one FAT on FAT12/FAT16 volumes.
-           * On FAT32 volumes this field must be 0, and sectorsPerFat32
-           * contains the FAT size count.
-           */
+  /**
+   * Count of sectors occupied by one FAT on FAT12/FAT16 volumes.
+   * On FAT32 volumes this field must be 0, and sectorsPerFat32
+   * contains the FAT size count.
+   */
   uint16_t sectorsPerFat16;
-           /** Sectors per track for interrupt 0x13. Not used otherwise. */
+  /** Sectors per track for interrupt 0x13. Not used otherwise. */
   uint16_t sectorsPerTrack;
-           /** Number of heads for interrupt 0x13.  Not used otherwise. */
+  /** Number of heads for interrupt 0x13.  Not used otherwise. */
   uint16_t headCount;
-          /**
-           * Count of hidden sectors preceding the partition that contains this
-           * FAT volume. This field is generally only relevant for media
-           * visible on interrupt 0x13.
-           */
+  /**
+   * Count of hidden sectors preceding the partition that contains this
+   * FAT volume. This field is generally only relevant for media
+   * visible on interrupt 0x13.
+   */
   uint32_t hidddenSectors;
-          /**
-           * This field is the new 32-bit total count of sectors on the volume.
-           * This count includes the count of all sectors in all four regions
-           * of the volume.  This field can be 0; if it is 0, then
-           * totalSectors16 must be non-zero.
-           */
+  /**
+   * This field is the new 32-bit total count of sectors on the volume.
+   * This count includes the count of all sectors in all four regions
+   * of the volume.  This field can be 0; if it is 0, then
+   * totalSectors16 must be non-zero.
+   */
   uint32_t totalSectors32;
-           /**
-            * Related to the BIOS physical drive number. Floppy drives are
-            * identified as 0x00 and physical hard disks are identified as
-            * 0x80, regardless of the number of physical disk drives.
-            * Typically, this value is set prior to issuing an INT 13h BIOS
-            * call to specify the device to access. The value is only
-            * relevant if the device is a boot device.
-            */
+  /**
+   * Related to the BIOS physical drive number. Floppy drives are
+   * identified as 0x00 and physical hard disks are identified as
+   * 0x80, regardless of the number of physical disk drives.
+   * Typically, this value is set prior to issuing an INT 13h BIOS
+   * call to specify the device to access. The value is only
+   * relevant if the device is a boot device.
+   */
   uint8_t  driveNumber;
-           /** used by Windows NT - should be zero for FAT */
+  /** used by Windows NT - should be zero for FAT */
   uint8_t  reserved1;
-           /** 0X29 if next three fields are valid */
+  /** 0X29 if next three fields are valid */
   uint8_t  bootSignature;
-           /**
-            * A random serial number created when formatting a disk,
-            * which helps to distinguish between disks.
-            * Usually generated by combining date and time.
-            */
+  /**
+   * A random serial number created when formatting a disk,
+   * which helps to distinguish between disks.
+   * Usually generated by combining date and time.
+   */
   uint32_t volumeSerialNumber;
-           /**
-            * A field once used to store the volume label. The volume label
-            * is now stored as a special file in the root directory.
-            */
+  /**
+   * A field once used to store the volume label. The volume label
+   * is now stored as a special file in the root directory.
+   */
   char     volumeLabel[11];
-           /**
-            * A field with a value of either FAT, FAT12 or FAT16,
-            * depending on the disk format.
-            */
+  /**
+   * A field with a value of either FAT, FAT12 or FAT16,
+   * depending on the disk format.
+   */
   char     fileSystemType[8];
-           /** X86 boot code */
+  /** X86 boot code */
   uint8_t  bootCode[448];
-           /** must be 0X55 */
+  /** must be 0X55 */
   uint8_t  bootSectorSig0;
-           /** must be 0XAA */
+  /** must be 0XAA */
   uint8_t  bootSectorSig1;
-}__attribute__((packed));
+} __attribute__((packed));
 /** Type name for FAT Boot Sector */
 typedef struct fat_boot fat_boot_t;
 //------------------------------------------------------------------------------
@@ -255,150 +255,150 @@ typedef struct fat_boot fat_boot_t;
  *
  */
 struct fat32_boot {
-         /**
-          * The first three bytes of the boot sector must be valid,
-          * executable x 86-based CPU instructions. This includes a
-          * jump instruction that skips the next non-executable bytes.
-          */
+  /**
+   * The first three bytes of the boot sector must be valid,
+   * executable x 86-based CPU instructions. This includes a
+   * jump instruction that skips the next non-executable bytes.
+   */
   uint8_t jump[3];
-         /**
-          * This is typically a string of characters that identifies
-          * the operating system that formatted the volume.
-          */
+  /**
+   * This is typically a string of characters that identifies
+   * the operating system that formatted the volume.
+   */
   char    oemId[8];
-          /**
-           * The size of a hardware sector. Valid decimal values for this
-           * field are 512, 1024, 2048, and 4096. For most disks used in
-           * the United States, the value of this field is 512.
-           */
+  /**
+   * The size of a hardware sector. Valid decimal values for this
+   * field are 512, 1024, 2048, and 4096. For most disks used in
+   * the United States, the value of this field is 512.
+   */
   uint16_t bytesPerSector;
-          /**
-           * Number of sectors per allocation unit. This value must be a
-           * power of 2 that is greater than 0. The legal values are
-           * 1, 2, 4, 8, 16, 32, 64, and 128.  128 should be avoided.
-           */
+  /**
+   * Number of sectors per allocation unit. This value must be a
+   * power of 2 that is greater than 0. The legal values are
+   * 1, 2, 4, 8, 16, 32, 64, and 128.  128 should be avoided.
+   */
   uint8_t  sectorsPerCluster;
-          /**
-           * The number of sectors preceding the start of the first FAT,
-           * including the boot sector. Must not be zero
-           */
+  /**
+   * The number of sectors preceding the start of the first FAT,
+   * including the boot sector. Must not be zero
+   */
   uint16_t reservedSectorCount;
-          /**
-           * The number of copies of the FAT on the volume.
-           * The value of this field is always 2.
-           */
+  /**
+   * The number of copies of the FAT on the volume.
+   * The value of this field is always 2.
+   */
   uint8_t  fatCount;
-          /**
-           * FAT12/FAT16 only. For FAT32 volumes, this field must be set to 0.
-           */
+  /**
+   * FAT12/FAT16 only. For FAT32 volumes, this field must be set to 0.
+   */
   uint16_t rootDirEntryCount;
-          /**
-           * For FAT32 volumes, this field must be 0.
-           */
+  /**
+   * For FAT32 volumes, this field must be 0.
+   */
   uint16_t totalSectors16;
-          /**
-           * This dates back to the old MS-DOS 1.x media determination and is
-           * no longer usually used for anything.  0xF8 is the standard value
-           * for fixed (non-removable) media. For removable media, 0xF0 is
-           * frequently used. Legal values are 0xF0 or 0xF8-0xFF.
-           */
+  /**
+   * This dates back to the old MS-DOS 1.x media determination and is
+   * no longer usually used for anything.  0xF8 is the standard value
+   * for fixed (non-removable) media. For removable media, 0xF0 is
+   * frequently used. Legal values are 0xF0 or 0xF8-0xFF.
+   */
   uint8_t  mediaType;
-          /**
-           * On FAT32 volumes this field must be 0, and sectorsPerFat32
-           * contains the FAT size count.
-           */
+  /**
+   * On FAT32 volumes this field must be 0, and sectorsPerFat32
+   * contains the FAT size count.
+   */
   uint16_t sectorsPerFat16;
-           /** Sectors per track for interrupt 0x13. Not used otherwise. */
+  /** Sectors per track for interrupt 0x13. Not used otherwise. */
   uint16_t sectorsPerTrack;
-           /** Number of heads for interrupt 0x13.  Not used otherwise. */
+  /** Number of heads for interrupt 0x13.  Not used otherwise. */
   uint16_t headCount;
-          /**
-           * Count of hidden sectors preceding the partition that contains this
-           * FAT volume. This field is generally only relevant for media
-           * visible on interrupt 0x13.
-           */
+  /**
+   * Count of hidden sectors preceding the partition that contains this
+   * FAT volume. This field is generally only relevant for media
+   * visible on interrupt 0x13.
+   */
   uint32_t hidddenSectors;
-          /**
-           * Contains the total number of sectors in the FAT32 volume.
-           */
+  /**
+   * Contains the total number of sectors in the FAT32 volume.
+   */
   uint32_t totalSectors32;
-         /**
-           * Count of sectors occupied by one FAT on FAT32 volumes.
-           */
+  /**
+    * Count of sectors occupied by one FAT on FAT32 volumes.
+    */
   uint32_t sectorsPerFat32;
-          /**
-           * This field is only defined for FAT32 media and does not exist on
-           * FAT12 and FAT16 media.
-           * Bits 0-3 -- Zero-based number of active FAT.
-           *             Only valid if mirroring is disabled.
-           * Bits 4-6 -- Reserved.
-           * Bit 7	-- 0 means the FAT is mirrored at runtime into all FATs.
-	         *        -- 1 means only one FAT is active; it is the one referenced
-	         *             in bits 0-3.
-           * Bits 8-15 	-- Reserved.
-           */
+  /**
+   * This field is only defined for FAT32 media and does not exist on
+   * FAT12 and FAT16 media.
+   * Bits 0-3 -- Zero-based number of active FAT.
+   *             Only valid if mirroring is disabled.
+   * Bits 4-6 -- Reserved.
+   * Bit 7	-- 0 means the FAT is mirrored at runtime into all FATs.
+   *        -- 1 means only one FAT is active; it is the one referenced
+   *             in bits 0-3.
+   * Bits 8-15 	-- Reserved.
+   */
   uint16_t fat32Flags;
-          /**
-           * FAT32 version. High byte is major revision number.
-           * Low byte is minor revision number. Only 0.0 define.
-           */
+  /**
+   * FAT32 version. High byte is major revision number.
+   * Low byte is minor revision number. Only 0.0 define.
+   */
   uint16_t fat32Version;
-          /**
-           * Cluster number of the first cluster of the root directory for FAT32.
-           * This usually 2 but not required to be 2.
-           */
+  /**
+   * Cluster number of the first cluster of the root directory for FAT32.
+   * This usually 2 but not required to be 2.
+   */
   uint32_t fat32RootCluster;
-          /**
-           * Sector number of FSINFO structure in the reserved area of the
-           * FAT32 volume. Usually 1.
-           */
+  /**
+   * Sector number of FSINFO structure in the reserved area of the
+   * FAT32 volume. Usually 1.
+   */
   uint16_t fat32FSInfo;
-          /**
-           * If non-zero, indicates the sector number in the reserved area
-           * of the volume of a copy of the boot record. Usually 6.
-           * No value other than 6 is recommended.
-           */
+  /**
+   * If non-zero, indicates the sector number in the reserved area
+   * of the volume of a copy of the boot record. Usually 6.
+   * No value other than 6 is recommended.
+   */
   uint16_t fat32BackBootBlock;
-          /**
-           * Reserved for future expansion. Code that formats FAT32 volumes
-           * should always set all of the bytes of this field to 0.
-           */
+  /**
+   * Reserved for future expansion. Code that formats FAT32 volumes
+   * should always set all of the bytes of this field to 0.
+   */
   uint8_t  fat32Reserved[12];
-           /**
-            * Related to the BIOS physical drive number. Floppy drives are
-            * identified as 0x00 and physical hard disks are identified as
-            * 0x80, regardless of the number of physical disk drives.
-            * Typically, this value is set prior to issuing an INT 13h BIOS
-            * call to specify the device to access. The value is only
-            * relevant if the device is a boot device.
-            */
+  /**
+   * Related to the BIOS physical drive number. Floppy drives are
+   * identified as 0x00 and physical hard disks are identified as
+   * 0x80, regardless of the number of physical disk drives.
+   * Typically, this value is set prior to issuing an INT 13h BIOS
+   * call to specify the device to access. The value is only
+   * relevant if the device is a boot device.
+   */
   uint8_t  driveNumber;
-           /** used by Windows NT - should be zero for FAT */
+  /** used by Windows NT - should be zero for FAT */
   uint8_t  reserved1;
-           /** 0X29 if next three fields are valid */
+  /** 0X29 if next three fields are valid */
   uint8_t  bootSignature;
-           /**
-            * A random serial number created when formatting a disk,
-            * which helps to distinguish between disks.
-            * Usually generated by combining date and time.
-            */
+  /**
+   * A random serial number created when formatting a disk,
+   * which helps to distinguish between disks.
+   * Usually generated by combining date and time.
+   */
   uint32_t volumeSerialNumber;
-           /**
-            * A field once used to store the volume label. The volume label
-            * is now stored as a special file in the root directory.
-            */
+  /**
+   * A field once used to store the volume label. The volume label
+   * is now stored as a special file in the root directory.
+   */
   char     volumeLabel[11];
-           /**
-            * A text field with a value of FAT32.
-            */
+  /**
+   * A text field with a value of FAT32.
+   */
   char     fileSystemType[8];
-           /** X86 boot code */
+  /** X86 boot code */
   uint8_t  bootCode[420];
-           /** must be 0X55 */
+  /** must be 0X55 */
   uint8_t  bootSectorSig0;
-           /** must be 0XAA */
+  /** must be 0XAA */
   uint8_t  bootSectorSig1;
-}__attribute__((packed));
+} __attribute__((packed));
 /** Type name for FAT32 Boot Sector */
 typedef struct fat32_boot fat32_boot_t;
 //------------------------------------------------------------------------------
@@ -413,32 +413,32 @@ uint32_t const FSINFO_STRUCT_SIG = 0x61417272;
  *
  */
 struct fat32_fsinfo {
-           /** must be 0X52, 0X52, 0X61, 0X41 */
+  /** must be 0X52, 0X52, 0X61, 0X41 */
   uint32_t  leadSignature;
-           /** must be zero */
+  /** must be zero */
   uint8_t  reserved1[480];
-           /** must be 0X72, 0X72, 0X41, 0X61 */
+  /** must be 0X72, 0X72, 0X41, 0X61 */
   uint32_t  structSignature;
-          /**
-           * Contains the last known free cluster count on the volume.
-           * If the value is 0xFFFFFFFF, then the free count is unknown
-           * and must be computed. Any other value can be used, but is
-           * not necessarily correct. It should be range checked at least
-           * to make sure it is <= volume cluster count.
-           */
+  /**
+   * Contains the last known free cluster count on the volume.
+   * If the value is 0xFFFFFFFF, then the free count is unknown
+   * and must be computed. Any other value can be used, but is
+   * not necessarily correct. It should be range checked at least
+   * to make sure it is <= volume cluster count.
+   */
   uint32_t freeCount;
-          /**
-           * This is a hint for the FAT driver. It indicates the cluster
-           * number at which the driver should start looking for free clusters.
-           * If the value is 0xFFFFFFFF, then there is no hint and the driver
-           * should start looking at cluster 2.
-           */
+  /**
+   * This is a hint for the FAT driver. It indicates the cluster
+   * number at which the driver should start looking for free clusters.
+   * If the value is 0xFFFFFFFF, then there is no hint and the driver
+   * should start looking at cluster 2.
+   */
   uint32_t nextFree;
-           /** must be zero */
+  /** must be zero */
   uint8_t  reserved2[12];
-           /** must be 0X00, 0X00, 0X55, 0XAA */
+  /** must be 0X00, 0X00, 0X55, 0XAA */
   uint8_t  tailSignature[4];
-}__attribute__((packed));
+} __attribute__((packed));
 /** Type name for FAT32 FSINFO Sector */
 typedef struct fat32_fsinfo fat32_fsinfo_t;
 //------------------------------------------------------------------------------
@@ -463,85 +463,85 @@ uint32_t const FAT32MASK = 0X0FFFFFFF;
  * \brief FAT short directory entry
  *
  * Short means short 8.3 name, not the entry size.
- *  
- * Date Format. A FAT directory entry date stamp is a 16-bit field that is 
+ *
+ * Date Format. A FAT directory entry date stamp is a 16-bit field that is
  * basically a date relative to the MS-DOS epoch of 01/01/1980. Here is the
- * format (bit 0 is the LSB of the 16-bit word, bit 15 is the MSB of the 
+ * format (bit 0 is the LSB of the 16-bit word, bit 15 is the MSB of the
  * 16-bit word):
- *   
- * Bits 9-15: Count of years from 1980, valid value range 0-127 
+ *
+ * Bits 9-15: Count of years from 1980, valid value range 0-127
  * inclusive (1980-2107).
- *   
+ *
  * Bits 5-8: Month of year, 1 = January, valid value range 1-12 inclusive.
  *
  * Bits 0-4: Day of month, valid value range 1-31 inclusive.
  *
  * Time Format. A FAT directory entry time stamp is a 16-bit field that has
- * a granularity of 2 seconds. Here is the format (bit 0 is the LSB of the 
+ * a granularity of 2 seconds. Here is the format (bit 0 is the LSB of the
  * 16-bit word, bit 15 is the MSB of the 16-bit word).
- *   
+ *
  * Bits 11-15: Hours, valid value range 0-23 inclusive.
- * 
+ *
  * Bits 5-10: Minutes, valid value range 0-59 inclusive.
- *      
+ *
  * Bits 0-4: 2-second count, valid value range 0-29 inclusive (0 - 58 seconds).
- *   
+ *
  * The valid time range is from Midnight 00:00:00 to 23:59:58.
  */
 struct directoryEntry {
-           /** Short 8.3 name.
-            *
-            * The first eight bytes contain the file name with blank fill.
-            * The last three bytes contain the file extension with blank fill.
-            */
+  /** Short 8.3 name.
+   *
+   * The first eight bytes contain the file name with blank fill.
+   * The last three bytes contain the file extension with blank fill.
+   */
   uint8_t  name[11];
-          /** Entry attributes.
-           *
-           * The upper two bits of the attribute byte are reserved and should
-           * always be set to 0 when a file is created and never modified or
-           * looked at after that.  See defines that begin with DIR_ATT_.
-           */
+  /** Entry attributes.
+   *
+   * The upper two bits of the attribute byte are reserved and should
+   * always be set to 0 when a file is created and never modified or
+   * looked at after that.  See defines that begin with DIR_ATT_.
+   */
   uint8_t  attributes;
-          /**
-           * Reserved for use by Windows NT. Set value to 0 when a file is
-           * created and never modify or look at it after that.
-           */
+  /**
+   * Reserved for use by Windows NT. Set value to 0 when a file is
+   * created and never modify or look at it after that.
+   */
   uint8_t  reservedNT;
-          /**
-           * The granularity of the seconds part of creationTime is 2 seconds
-           * so this field is a count of tenths of a second and its valid
-           * value range is 0-199 inclusive. (WHG note - seems to be hundredths)
-           */
+  /**
+   * The granularity of the seconds part of creationTime is 2 seconds
+   * so this field is a count of tenths of a second and its valid
+   * value range is 0-199 inclusive. (WHG note - seems to be hundredths)
+   */
   uint8_t  creationTimeTenths;
-           /** Time file was created. */
+  /** Time file was created. */
   uint16_t creationTime;
-           /** Date file was created. */
+  /** Date file was created. */
   uint16_t creationDate;
-          /**
-           * Last access date. Note that there is no last access time, only
-           * a date.  This is the date of last read or write. In the case of
-           * a write, this should be set to the same date as lastWriteDate.
-           */
+  /**
+   * Last access date. Note that there is no last access time, only
+   * a date.  This is the date of last read or write. In the case of
+   * a write, this should be set to the same date as lastWriteDate.
+   */
   uint16_t lastAccessDate;
-          /**
-           * High word of this entry's first cluster number (always 0 for a
-           * FAT12 or FAT16 volume).
-           */
+  /**
+   * High word of this entry's first cluster number (always 0 for a
+   * FAT12 or FAT16 volume).
+   */
   uint16_t firstClusterHigh;
-           /** Time of last write. File creation is considered a write. */
+  /** Time of last write. File creation is considered a write. */
   uint16_t lastWriteTime;
-           /** Date of last write. File creation is considered a write. */
+  /** Date of last write. File creation is considered a write. */
   uint16_t lastWriteDate;
-           /** Low word of this entry's first cluster number. */
+  /** Low word of this entry's first cluster number. */
   uint16_t firstClusterLow;
-           /** 32-bit unsigned holding this file's size in bytes. */
+  /** 32-bit unsigned holding this file's size in bytes. */
   uint32_t fileSize;
-}__attribute__((packed));
+} __attribute__((packed));
+/** Type name for directoryEntry */
+typedef struct directoryEntry dir_t;
 //------------------------------------------------------------------------------
 // Definitions for directory entries
 //
-/** Type name for directoryEntry */
-typedef struct directoryEntry dir_t;
 /** escape for name[0] = 0XE5 */
 uint8_t const DIR_NAME_0XE5 = 0X05;
 /** name[0] value for entry that is free after being "deleted" */
@@ -550,7 +550,7 @@ uint8_t const DIR_NAME_DELETED = 0XE5;
 uint8_t const DIR_NAME_FREE = 0X00;
 /** file is read-only */
 uint8_t const DIR_ATT_READ_ONLY = 0X01;
-/** File should hidden in directory listings */
+/** File should e hidden in directory listings */
 uint8_t const DIR_ATT_HIDDEN = 0X02;
 /** Entry is for a system file */
 uint8_t const DIR_ATT_SYSTEM = 0X04;
@@ -567,23 +567,47 @@ uint8_t const DIR_ATT_LONG_NAME = 0X0F;
 uint8_t const DIR_ATT_LONG_NAME_MASK = 0X3F;
 /** defined attribute bits */
 uint8_t const DIR_ATT_DEFINED_BITS = 0X3F;
+
+/** Mask for file/subdirectory tests */
+uint8_t const DIR_ATT_FILE_TYPE_MASK = (DIR_ATT_VOLUME_ID | DIR_ATT_DIRECTORY);
+
+/** Filename base-name is all lower case */
+const uint8_t DIR_NT_LC_BASE = 0X08;
+/** Filename extension is all lower case.*/
+const uint8_t DIR_NT_LC_EXT = 0X10;
+
+
+/** Directory entry is for a file
+ * \param[in] dir Pointer to a directory entry.
+ *
+ * \return true if the entry is for a normal file else false.
+ */
+static inline uint8_t DIR_IS_FILE(const dir_t* dir) {
+  return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == 0;
+}
+/** Directory entry is for a file or subdirectory
+ * \param[in] dir Pointer to a directory entry.
+ *
+ * \return true if the entry is for a normal file or subdirectory else false.
+ */
+static inline uint8_t DIR_IS_FILE_OR_SUBDIR(const dir_t* dir) {
+  return (dir->attributes & DIR_ATT_VOLUME_ID) == 0;
+}
 /** Directory entry is part of a long name
  * \param[in] dir Pointer to a directory entry.
  *
  * \return true if the entry is for part of a long name else false.
  */
 static inline uint8_t DIR_IS_LONG_NAME(const dir_t* dir) {
-  return (dir->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME;
+  return dir->attributes == DIR_ATT_LONG_NAME;
 }
-/** Mask for file/subdirectory tests */
-uint8_t const DIR_ATT_FILE_TYPE_MASK = (DIR_ATT_VOLUME_ID | DIR_ATT_DIRECTORY);
-/** Directory entry is for a file
+/** Directory entry is hidden
  * \param[in] dir Pointer to a directory entry.
  *
- * \return true if the entry is for a normal file else false.
+ * \return true if the entry is hidden else false.
  */
-static inline uint8_t DIR_IS_FILE(const dir_t* dir) {
-  return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == 0;
+static inline uint8_t DIR_IS_HIDDEN(const dir_t* dir) {
+  return dir->attributes & DIR_ATT_HIDDEN;
 }
 /** Directory entry is for a subdirectory
  * \param[in] dir Pointer to a directory entry.
@@ -593,13 +617,13 @@ static inline uint8_t DIR_IS_FILE(const dir_t* dir) {
 static inline uint8_t DIR_IS_SUBDIR(const dir_t* dir) {
   return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == DIR_ATT_DIRECTORY;
 }
-/** Directory entry is for a file or subdirectory
+/** Directory entry is system type
  * \param[in] dir Pointer to a directory entry.
  *
- * \return true if the entry is for a normal file or subdirectory else false.
+ * \return true if the entry is system else false.
  */
-static inline uint8_t DIR_IS_FILE_OR_SUBDIR(const dir_t* dir) {
-  return (dir->attributes & DIR_ATT_VOLUME_ID) == 0;
+static inline uint8_t DIR_IS_SYSTEM(const dir_t* dir) {
+  return dir->attributes & DIR_ATT_SYSTEM;
 }
 /** date field for FAT directory entry
  * \param[in] year [1980,2107]
@@ -659,7 +683,7 @@ static inline uint8_t FAT_HOUR(uint16_t fatTime) {
  * \return Extracted minute [0,59]
  */
 static inline uint8_t FAT_MINUTE(uint16_t fatTime) {
-  return(fatTime >> 5) & 0X3F;
+  return (fatTime >> 5) & 0X3F;
 }
 /** second part of FAT directory time field
  * Note second/2 is stored in packed time.
@@ -675,4 +699,57 @@ static inline uint8_t FAT_SECOND(uint16_t fatTime) {
 uint16_t const FAT_DEFAULT_DATE = ((2000 - 1980) << 9) | (1 << 5) | 1;
 /** Default time for file timestamp is 1 am */
 uint16_t const FAT_DEFAULT_TIME = (1 << 11);
+//------------------------------------------------------------------------------
+/** Dimension of first name field in long directory entry */
+const uint8_t LDIR_NAME1_DIM = 5;
+/** Dimension of first name field in long directory entry */
+const uint8_t LDIR_NAME2_DIM = 6;
+/** Dimension of first name field in long directory entry */
+const uint8_t LDIR_NAME3_DIM = 2;
+/**
+ * \struct longDirectoryEntry
+ * \brief FAT long directory entry
+ */
+struct longDirectoryEntry {
+  /**
+   * The order of this entry in the sequence of long dir entries
+   * associated with the short dir entry at the end of the long dir set.
+   *
+   * If masked with 0X40 (LAST_LONG_ENTRY), this indicates the
+   * entry is the last long dir entry in a set of long dir entries.
+   * All valid sets of long dir entries must begin with an entry having
+   * this mask.
+   */
+  uint8_t ord;
+  /** Characters 1-5 of the long-name sub-component in this entry. */
+  uint16_t name1[LDIR_NAME1_DIM];
+  /** Attributes - must be ATTR_LONG_NAME */
+  uint8_t attr;
+  /**
+   * If zero, indicates a directory entry that is a sub-component of a
+   * long name. NOTE: Other values reserved for future extensions.
+   *
+   * Non-zero implies other directory entry types.
+   */
+  uint8_t type;
+  /**
+   * Checksum of name in the short dir entry at the end of the
+   * long dir set.
+   */
+  uint8_t chksum;
+  /** Characters 6-11 of the long-name sub-component in this entry. */
+  uint16_t name2[LDIR_NAME2_DIM];
+  /** Must be ZERO. This is an artifact of the FAT "first cluster" */
+  uint16_t mustBeZero;
+  /** Characters 12 and 13 of the long-name sub-component in this entry. */
+  uint16_t name3[LDIR_NAME3_DIM];
+} __attribute__((packed));
+/** Type name for longDirectoryEntry */
+typedef struct longDirectoryEntry ldir_t;
+/**
+ * Ord mast 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
+ * begin with an entry having this mask.
+ */
+const uint8_t LDIR_ORD_LAST_LONG_ENTRY = 0X40;
 #endif  // FatStructs_h

+ 585 - 0
SdFat/utility/FatVolume.cpp

@@ -0,0 +1,585 @@
+/* FatLib Library
+ * Copyright (C) 2013 by William Greiman
+ *
+ * This file is part of the FatLib Library
+ *
+ * This Library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the FatLib Library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+#include <string.h>
+#include "FatVolume.h"
+//------------------------------------------------------------------------------
+cache_t* FatCache::read(uint32_t lbn, uint8_t option) {
+  if (m_lbn != lbn) {
+    if (!sync()) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    if (!(option & CACHE_OPTION_NO_READ)) {
+      if (!m_vol->readBlock(lbn, m_block.data)) {
+        DBG_FAIL_MACRO;
+        goto fail;
+      }
+    }
+    m_status = 0;
+    m_lbn = lbn;
+  }
+  m_status |= option & CACHE_STATUS_MASK;
+  return &m_block;
+
+fail:
+  return 0;
+}
+//------------------------------------------------------------------------------
+bool FatCache::sync() {
+  if (m_status & CACHE_STATUS_DIRTY) {
+    if (!m_vol->writeBlock(m_lbn, m_block.data)) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    // mirror second FAT
+    if (m_status & CACHE_STATUS_MIRROR_FAT) {
+      uint32_t lbn = m_lbn + m_vol->blocksPerFat();
+      if (!m_vol->writeBlock(lbn, m_block.data)) {
+        DBG_FAIL_MACRO;
+        goto fail;
+      }
+    }
+    m_status &= ~CACHE_STATUS_DIRTY;
+  }
+  return true;
+
+fail:
+  return false;
+}
+//------------------------------------------------------------------------------
+bool FatVolume::allocateCluster(uint32_t current, uint32_t* next) {
+  uint32_t find = current ? current : m_allocSearchStart;
+  uint32_t start = find;
+  while (1) {
+    find++;
+    // If at end of FAT go to beginning of FAT.
+    if (find > m_lastCluster) {
+      find = 2;
+    }
+    uint32_t f;
+    int8_t fg = fatGet(find, &f);
+    if (fg < 0) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    if (fg && f == 0) {
+      break;
+    }
+    if (find == start) {
+      // Can't find space checked all clusters.
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+  }
+  // mark end of chain
+  if (!fatPutEOC(find)) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  if (current) {
+    // link clusters
+    if (!fatPut(current, find)) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+  } else {
+    // Remember place for search start.
+    m_allocSearchStart = find;
+  }
+  *next = find;
+  return true;
+
+fail:
+  return false;
+}
+//------------------------------------------------------------------------------
+// find a contiguous group of clusters
+bool FatVolume::allocContiguous(uint32_t count, uint32_t* firstCluster) {
+  // flag to save place to start next search
+  bool setStart = true;
+  // start of group
+  uint32_t bgnCluster;
+  // end of group
+  uint32_t endCluster;
+  // Start at cluster after last allocated cluster.
+  uint32_t startCluster = m_allocSearchStart;
+  endCluster = bgnCluster = startCluster + 1;
+
+  // search the FAT for free clusters
+  while (1) {
+    // If past end - start from beginning of FAT.
+    if (endCluster > m_lastCluster) {
+      bgnCluster = endCluster = 2;
+    }
+    uint32_t f;
+    int8_t fg = fatGet(endCluster, &f);
+    if (fg < 0) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    if (f || fg == 0) {
+      // cluster in use try next cluster as bgnCluster
+      bgnCluster = endCluster + 1;
+
+      // don't update search start if unallocated clusters before endCluster.
+      if (bgnCluster != endCluster) {
+        setStart = false;
+      }
+    } else if ((endCluster - bgnCluster + 1) == count) {
+      // done - found space
+      break;
+    }
+    // Can't find space if all clusters checked.
+    if (startCluster == endCluster) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    endCluster++;
+  }
+  // remember possible next free cluster
+  if (setStart) {
+    m_allocSearchStart = endCluster + 1;
+  }
+
+  // mark end of chain
+  if (!fatPutEOC(endCluster)) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  // link clusters
+  while (endCluster > bgnCluster) {
+    if (!fatPut(endCluster - 1, endCluster)) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    endCluster--;
+  }
+  // return first cluster number to caller
+  *firstCluster = bgnCluster;
+  return true;
+
+fail:
+  return false;
+}
+//------------------------------------------------------------------------------
+uint32_t FatVolume::clusterStartBlock(uint32_t cluster) const {
+  return m_dataStartBlock + ((cluster - 2) << m_clusterSizeShift);
+}
+//------------------------------------------------------------------------------
+// Fetch a FAT entry - return -1 error, 0 EOC, else 1.
+int8_t FatVolume::fatGet(uint32_t cluster, uint32_t* value) {
+  uint32_t lba;
+  uint32_t next;
+  cache_t* pc;
+
+  // error if reserved cluster of beyond FAT
+  DBG_HALT_IF(cluster < 2 || cluster > m_lastCluster);
+
+  if (m_fatType == 32) {
+    lba = m_fatStartBlock + (cluster >> 7);
+    pc = cacheFetchFat(lba, FatCache::CACHE_FOR_READ);
+    if (!pc) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    next = pc->fat32[cluster & 0X7F] & FAT32MASK;
+    goto done;
+  }
+
+  if (m_fatType == 16) {
+    lba = m_fatStartBlock + ((cluster >> 8) & 0XFF);
+    pc = cacheFetchFat(lba, FatCache::CACHE_FOR_READ);
+    if (!pc) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    next = pc->fat16[cluster & 0XFF];
+    goto done;
+  }
+  if (FAT12_SUPPORT && m_fatType == 12) {
+    uint16_t index = cluster;
+    index += index >> 1;
+    lba = m_fatStartBlock + (index >> 9);
+    pc = cacheFetchFat(lba, FatCache::CACHE_FOR_READ);
+    if (!pc) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    index &= 0X1FF;
+    uint16_t tmp = pc->data[index];
+    index++;
+    if (index == 512) {
+      pc = cacheFetchFat(lba + 1, FatCache::CACHE_FOR_READ);
+      if (!pc) {
+        DBG_FAIL_MACRO;
+        goto fail;
+      }
+      index = 0;
+    }
+    tmp |= pc->data[index] << 8;
+    next = cluster & 1 ? tmp >> 4 : tmp & 0XFFF;
+    goto done;
+  } else {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+done:
+  if (isEOC(next)) {
+    return 0;
+  }
+  *value = next;
+  return 1;
+
+fail:
+  return -1;
+}
+//------------------------------------------------------------------------------
+// Store a FAT entry
+bool FatVolume::fatPut(uint32_t cluster, uint32_t value) {
+  uint32_t lba;
+  cache_t* pc;
+
+  // error if reserved cluster of beyond FAT
+  DBG_HALT_IF(cluster < 2 || cluster > m_lastCluster);
+
+  if (m_fatType == 32) {
+    lba = m_fatStartBlock + (cluster >> 7);
+    pc = cacheFetchFat(lba, FatCache::CACHE_FOR_WRITE);
+    if (!pc) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    pc->fat32[cluster & 0X7F] = value;
+    return true;
+  }
+
+  if (m_fatType == 16) {
+    lba = m_fatStartBlock + ((cluster >> 8) & 0XFF);
+    pc = cacheFetchFat(lba, FatCache::CACHE_FOR_WRITE);
+    if (!pc) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    pc->fat16[cluster & 0XFF] = value;
+    return true;
+  }
+
+  if (FAT12_SUPPORT && m_fatType == 12) {
+    uint16_t index = cluster;
+    index += index >> 1;
+    lba = m_fatStartBlock + (index >> 9);
+    pc = cacheFetchFat(lba, FatCache::CACHE_FOR_WRITE);
+    if (!pc) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    index &= 0X1FF;
+    uint8_t tmp = value;
+    if (cluster & 1) {
+      tmp = (pc->data[index] & 0XF) | tmp << 4;
+    }
+    pc->data[index] = tmp;
+
+    index++;
+    if (index == 512) {
+      lba++;
+      index = 0;
+      pc = cacheFetchFat(lba, FatCache::CACHE_FOR_WRITE);
+      if (!pc) {
+        DBG_FAIL_MACRO;
+        goto fail;
+      }
+    }
+    tmp = value >> 4;
+    if (!(cluster & 1)) {
+      tmp = ((pc->data[index] & 0XF0)) | tmp >> 4;
+    }
+    pc->data[index] = tmp;
+    return true;
+  } else {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+
+fail:
+  return false;
+}
+//------------------------------------------------------------------------------
+// free a cluster chain
+bool FatVolume::freeChain(uint32_t cluster) {
+  uint32_t next;
+  int8_t fg;
+  do {
+    fg = fatGet(cluster, &next);
+    if (fg < 0) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    // free cluster
+    if (!fatPut(cluster, 0)) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    if (cluster < m_allocSearchStart) {
+      m_allocSearchStart = cluster;
+    }
+    cluster = next;
+  } while (fg);
+
+  return true;
+
+fail:
+  return false;
+}
+//------------------------------------------------------------------------------
+int32_t FatVolume::freeClusterCount() {
+  uint32_t free = 0;
+  uint32_t lba;
+  uint32_t todo = m_lastCluster + 1;
+  uint16_t n;
+
+  if (FAT12_SUPPORT && m_fatType == 12) {
+    for (unsigned i = 2; i < todo; i++) {
+      uint32_t c;
+      int8_t fg = fatGet(i, &c);
+      if (fg < 0) {
+        DBG_FAIL_MACRO;
+        goto fail;
+      }
+      if (fg && c == 0) {
+        free++;
+      }
+    }
+  } else if (m_fatType == 16 || m_fatType == 32) {
+    lba = m_fatStartBlock;
+    while (todo) {
+      cache_t* pc = cacheFetchFat(lba++, FatCache::CACHE_FOR_READ);
+      if (!pc) {
+        DBG_FAIL_MACRO;
+        goto fail;
+      }
+      n = m_fatType == 16 ? 256 : 128;
+      if (todo < n) {
+        n = todo;
+      }
+      if (m_fatType == 16) {
+        for (uint16_t i = 0; i < n; i++) {
+          if (pc->fat16[i] == 0) {
+            free++;
+          }
+        }
+      } else {
+        for (uint16_t i = 0; i < n; i++) {
+          if (pc->fat32[i] == 0) {
+            free++;
+          }
+        }
+      }
+      todo -= n;
+    }
+  } else {
+    // invalid FAT type
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  return free;
+
+fail:
+  return -1;
+}
+//------------------------------------------------------------------------------
+bool FatVolume::init(uint8_t part) {
+  uint32_t clusterCount;
+  uint32_t totalBlocks;
+  uint32_t volumeStartBlock = 0;
+  fat32_boot_t* fbs;
+  cache_t* pc;
+  uint8_t tmp;
+  m_fatType = 0;
+  m_allocSearchStart = 1;
+
+  m_cache.init(this);
+#if USE_SEPARATE_FAT_CACHE
+  m_fatCache.init(this);
+#endif  // USE_SEPARATE_FAT_CACHE
+
+  // if part == 0 assume super floppy with FAT boot sector in block zero
+  // if part > 0 assume mbr volume with partition table
+  if (part) {
+    if (part > 4) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    pc = cacheFetchData(0, FatCache::CACHE_FOR_READ);
+    if (!pc) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    part_t* p = &pc->mbr.part[part - 1];
+    if ((p->boot & 0X7F) != 0 || p->firstSector == 0) {
+      // not a valid partition
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+    volumeStartBlock = p->firstSector;
+  }
+  pc = cacheFetchData(volumeStartBlock, FatCache::CACHE_FOR_READ);
+  if (!pc) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  fbs = &(pc->fbs32);
+  if (fbs->bytesPerSector != 512 ||
+      fbs->fatCount != 2 ||
+      fbs->reservedSectorCount == 0) {
+    // not valid FAT volume
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  m_blocksPerCluster = fbs->sectorsPerCluster;
+  m_clusterBlockMask = m_blocksPerCluster - 1;
+
+  // determine shift that is same as multiply by m_blocksPerCluster
+  m_clusterSizeShift = 0;
+  for (tmp = 1; m_blocksPerCluster != tmp; tmp <<= 1, m_clusterSizeShift++) {
+    if (tmp == 0) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+  }
+
+  m_blocksPerFat = fbs->sectorsPerFat16 ?
+                   fbs->sectorsPerFat16 : fbs->sectorsPerFat32;
+
+  m_fatStartBlock = volumeStartBlock + fbs->reservedSectorCount;
+
+  // count for FAT16 zero for FAT32
+  m_rootDirEntryCount = fbs->rootDirEntryCount;
+
+  // directory start for FAT16 dataStart for FAT32
+  m_rootDirStart = m_fatStartBlock + 2 * m_blocksPerFat;
+  // data start for FAT16 and FAT32
+  m_dataStartBlock = m_rootDirStart + ((32 * fbs->rootDirEntryCount + 511)/512);
+
+  // total blocks for FAT16 or FAT32
+  totalBlocks = fbs->totalSectors16 ?
+                fbs->totalSectors16 : fbs->totalSectors32;
+  // total data blocks
+  clusterCount = totalBlocks - (m_dataStartBlock - volumeStartBlock);
+
+  // divide by cluster size to get cluster count
+  clusterCount >>= m_clusterSizeShift;
+  m_lastCluster = clusterCount + 1;
+
+  // FAT type is determined by cluster count
+  if (clusterCount < 4085) {
+    m_fatType = 12;
+    if (!FAT12_SUPPORT) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+  } else if (clusterCount < 65525) {
+    m_fatType = 16;
+  } else {
+    m_rootDirStart = fbs->fat32RootCluster;
+    m_fatType = 32;
+  }
+  return true;
+
+fail:
+  return false;
+}
+//------------------------------------------------------------------------------
+bool FatVolume::wipe(print_t* pr) {
+  cache_t* cache;
+  uint16_t count;
+  uint32_t lbn;
+  if (!m_fatType) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  cache = cacheClear();
+  if (!cache) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  memset(cache->data, 0, 512);
+  // Zero root.
+  if (m_fatType == 32) {
+    lbn = clusterStartBlock(m_rootDirStart);
+    count = m_blocksPerCluster;
+  } else {
+    lbn = m_rootDirStart;
+    count = m_rootDirEntryCount/16;
+  }
+  for (uint32_t n = 0; n < count; n++) {
+    if (!writeBlock(lbn + n, cache->data)) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+  }
+  // Clear FATs.
+  count = 2*m_blocksPerFat;
+  lbn = m_fatStartBlock;
+  for (uint32_t nb = 0; nb < count; nb++) {
+    if (pr && (nb & 0XFF) == 0) {
+      pr->write('.');
+    }
+    if (!writeBlock(lbn + nb, cache->data)) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+  }
+  // Reserve first two clusters.
+  if (m_fatType == 32) {
+    cache->fat32[0] = 0x0FFFFFF8;
+    cache->fat32[1] = 0x0FFFFFFF;
+  } else if (m_fatType == 16) {
+    cache->fat16[0] = 0XFFF8;
+    cache->fat16[1] = 0XFFFF;
+  } else if (FAT12_SUPPORT && m_fatType == 12) {
+    cache->fat32[0] = 0XFFFFF8;
+  } else {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  if (!writeBlock(m_fatStartBlock, cache->data) ||
+      !writeBlock(m_fatStartBlock + m_blocksPerFat, cache->data)) {
+    DBG_FAIL_MACRO;
+    goto fail;
+  }
+  if (m_fatType == 32) {
+    // Reserve root cluster.
+    if (!fatPutEOC(m_rootDirStart) || !cacheSync()) {
+      DBG_FAIL_MACRO;
+      goto fail;
+    }
+  }
+  if (pr) {
+    pr->write('\r');
+    pr->write('\n');
+  }
+  m_fatType = 0;
+  return true;
+
+fail:
+  m_fatType = 0;
+  return false;
+}

+ 338 - 0
SdFat/utility/FatVolume.h

@@ -0,0 +1,338 @@
+/* FatLib Library
+ * Copyright (C) 2013 by William Greiman
+ *
+ * This file is part of the FatLib Library
+ *
+ * This Library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the FatLib Library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+#ifndef FatVolume_h
+#define FatVolume_h
+/**
+ * \file
+ * \brief FatVolume class
+ */
+#include <stddef.h>
+#include "FatLibConfig.h"
+#include "FatStructs.h"
+//------------------------------------------------------------------------------
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+/** Macro for debug. */
+#define DEBUG_MODE 0
+#if DEBUG_MODE
+#include <Arduino.h>
+#define DBG_FAIL_MACRO Serial.print(F(__FILE__)); Serial.println(__LINE__)
+#define DBG_PRINT_IF(b) if (b) {Serial.println(F(#b)); DBG_FAIL_MACRO;}
+#define DBG_HALT_IF(b) if (b) {Serial.println(F(#b));\
+                               DBG_FAIL_MACRO; while (1);}
+#else  // DEBUG_MODE
+#define DBG_FAIL_MACRO
+#define DBG_PRINT_IF(b)
+#define DBG_HALT_IF(b)
+#endif  // DEBUG_MODE
+#endif  // DOXYGEN_SHOULD_SKIP_THIS
+//------------------------------------------------------------------------------
+#if ENABLE_ARDUINO_FEATURES
+#include <Arduino.h>
+/** Use Print on Arduino */
+typedef Print print_t;
+#else  // ENABLE_ARDUINO_FEATURES
+//  Arduino type for flash string.
+class __FlashStringHelper;
+/**
+ * \class CharWriter
+ * \brief Character output - often serial port.
+ */
+class CharWriter {
+ public:
+  virtual size_t write(char c) = 0;
+  virtual size_t write(const char* s) = 0;
+};
+typedef CharWriter print_t;
+#endif  // ENABLE_ARDUINO_FEATURES
+//------------------------------------------------------------------------------
+// Forward declaration of FatVolume.
+class FatVolume;
+//------------------------------------------------------------------------------
+/**
+ * \brief Cache for an raw data block.
+ */
+union cache_t {
+  /** Used to access cached file data blocks. */
+  uint8_t  data[512];
+  /** Used to access cached FAT16 entries. */
+  uint16_t fat16[256];
+  /** Used to access cached FAT32 entries. */
+  uint32_t fat32[128];
+  /** Used to access cached directory entries. */
+  dir_t    dir[16];
+  /** Used to access a cached Master Boot Record. */
+  mbr_t    mbr;
+  /** Used to access to a cached FAT boot sector. */
+  fat_boot_t fbs;
+  /** Used to access to a cached FAT32 boot sector. */
+  fat32_boot_t fbs32;
+  /** Used to access to a cached FAT32 FSINFO sector. */
+  fat32_fsinfo_t fsinfo;
+};
+//==============================================================================
+/**
+ * \class FatCache
+ * \brief Block cache.
+ */
+class FatCache {
+ public:
+  /** Cached block is dirty */
+  static const uint8_t CACHE_STATUS_DIRTY = 1;
+  /** Cashed block is FAT entry and must be mirrored in second FAT. */
+  static const uint8_t CACHE_STATUS_MIRROR_FAT = 2;
+  /** Cache block status bits */
+  static const uint8_t CACHE_STATUS_MASK
+    = CACHE_STATUS_DIRTY | CACHE_STATUS_MIRROR_FAT;
+  /** Sync existing block but do not read new block. */
+  static const uint8_t CACHE_OPTION_NO_READ = 4;
+  /** Cache block for read. */
+  static uint8_t const CACHE_FOR_READ = 0;
+  /** Cache block for write. */
+  static uint8_t const CACHE_FOR_WRITE = CACHE_STATUS_DIRTY;
+  /** Reserve cache block for write - do not read from block device. */
+  static uint8_t const CACHE_RESERVE_FOR_WRITE
+    = CACHE_STATUS_DIRTY | CACHE_OPTION_NO_READ;
+  /** \return Cache block address. */
+  cache_t* block() {
+    return &m_block;
+  }
+  /** Set current block dirty. */
+  void dirty() {
+    m_status |= CACHE_STATUS_DIRTY;
+  }
+  /** Initialize the cache.
+   * \param[in] vol FatVolume that owns this FatCache.
+   */
+  void init(FatVolume *vol) {
+    m_vol = vol;
+    invalidate();
+  }
+  /** Invalidate current cache block. */
+  void invalidate() {
+    m_status = 0;
+    m_lbn = 0XFFFFFFFF;
+  }
+  /** \return Logical block number for cached block. */
+  uint32_t lbn() {
+    return m_lbn;
+  }
+  /** Read a block into the cache.
+   * \param[in] lbn Block to read.
+   * \param[in] option mode for cached block.
+   * \return Address of cached block. */
+  cache_t* read(uint32_t lbn, uint8_t option);
+  /** Write current block if dirty.
+   * \return true for success else false.
+   */
+  bool sync();
+
+ private:
+  uint8_t m_status;
+  FatVolume* m_vol;
+  uint32_t m_lbn;
+  cache_t m_block;
+};
+//==============================================================================
+/**
+ * \class FatVolume
+ * \brief Access FAT16 and FAT32 volumes on raw file devices.
+ */
+class FatVolume {
+ public:
+  /** Create an instance of FatVolume
+   */
+  FatVolume() : m_fatType(0) {}
+
+  /** \return The volume's cluster size in blocks. */
+  uint8_t blocksPerCluster() const {
+    return m_blocksPerCluster;
+  }
+  /** \return The number of blocks in one FAT. */
+  uint32_t blocksPerFat()  const {
+    return m_blocksPerFat;
+  }
+  /** Clear the cache and returns a pointer to the cache.  Not for normal apps.
+   * \return A pointer to the cache buffer or zero if an error occurs.
+   */
+  cache_t* cacheClear() {
+    if (!cacheSync()) {
+      return 0;
+    }
+    m_cache.invalidate();
+    return m_cache.block();
+  }
+  /** \return The total number of clusters in the volume. */
+  uint32_t clusterCount() const {
+    return m_lastCluster - 1;
+  }
+  /** \return The shift count required to multiply by blocksPerCluster. */
+  uint8_t clusterSizeShift() const {
+    return m_clusterSizeShift;
+  }
+  /** \return The logical block number for the start of file data. */
+  uint32_t dataStartBlock() const {
+    return m_dataStartBlock;
+  }
+  /** \return The number of File Allocation Tables. */
+  uint8_t fatCount() {
+    return 2;
+  }
+  /** \return The logical block number for the start of the first FAT. */
+  uint32_t fatStartBlock() const {
+    return m_fatStartBlock;
+  }
+  /** \return The FAT type of the volume. Values are 12, 16 or 32. */
+  uint8_t fatType() const {
+    return m_fatType;
+  }
+  /** Volume free space in clusters.
+   *
+   * \return Count of free clusters for success or -1 if an error occurs.
+   */
+  int32_t freeClusterCount();
+  /** Initialize a FAT volume.  Try partition one first then try super
+   * floppy format.
+   *
+   * \return The value true is returned for success and
+   * the value false is returned for failure. 
+   */
+  bool init() {
+    return init(1) || init(0);
+  }
+  /** Initialize a FAT volume.
+
+   * \param[in] part The partition to be used.  Legal values for \a part are
+   * 1-4 to use the corresponding partition on a device formatted with
+   * a MBR, Master Boot Record, or zero if the device is formatted as
+   * a super floppy with the FAT boot sector in block zero.
+   *
+   * \return The value true is returned for success and
+   * the value false is returned for failure.
+   */
+  bool init(uint8_t part);
+  /** \return The number of entries in the root directory for FAT16 volumes. */
+  uint16_t rootDirEntryCount() const {
+    return m_rootDirEntryCount;
+  }
+  /** \return The logical block number for the start of the root directory
+       on FAT16 volumes or the first cluster number on FAT32 volumes. */
+  uint32_t rootDirStart() const {
+    return m_rootDirStart;
+  }
+  /** \return The number of blocks in the volume */
+  uint32_t volumeBlockCount() const {
+    return blocksPerCluster()*clusterCount();
+  }
+  /** Wipe all data from the volume.
+   * \param[in] pr print stream for status dots.
+   * \return true for success else false.
+   */
+  bool wipe(print_t* pr = 0);
+  /** Debug access to FAT table
+   *
+   * \param[in] n cluster number.
+   * \param[out] v value of entry
+   * \return true for success or false for failure
+   */
+  int8_t dbgFat(uint32_t n, uint32_t* v) {
+    return fatGet(n, v);
+  }
+//------------------------------------------------------------------------------
+ private:
+  // Allow FatFile and FatCache access to FatVolume private functions.
+  friend class FatCache;
+  friend class FatFile;
+//------------------------------------------------------------------------------
+  uint8_t  m_blocksPerCluster;     // Cluster size in blocks.
+  uint8_t  m_clusterBlockMask;     // Mask to extract block of cluster.
+  uint8_t  m_clusterSizeShift;     // Cluster count to block count shift.
+  uint8_t  m_fatType;              // Volume type (12, 16, OR 32).
+  uint16_t m_rootDirEntryCount;    // Number of entries in FAT16 root dir.
+  uint32_t m_allocSearchStart;     // Start cluster for alloc search.
+  uint32_t m_blocksPerFat;         // FAT size in blocks
+  uint32_t m_dataStartBlock;       // First data block number.
+  uint32_t m_fatStartBlock;        // Start block for first FAT.
+  uint32_t m_lastCluster;          // Last cluster number in FAT.
+  uint32_t m_rootDirStart;         // Start block for FAT16, cluster for FAT32.
+//------------------------------------------------------------------------------
+// block caches
+  FatCache m_cache;
+#if USE_SEPARATE_FAT_CACHE
+  FatCache m_fatCache;
+  cache_t* cacheFetchFat(uint32_t blockNumber, uint8_t options) {
+    return m_fatCache.read(blockNumber,
+                           options | FatCache::CACHE_STATUS_MIRROR_FAT);
+  }
+  bool cacheSync() {
+    return m_cache.sync() && m_fatCache.sync();
+  }
+#else  //
+  cache_t* cacheFetchFat(uint32_t blockNumber, uint8_t options) {
+    return cacheFetchData(blockNumber,
+                          options | FatCache::CACHE_STATUS_MIRROR_FAT);
+  }
+  bool cacheSync() {
+    return m_cache.sync();
+  }
+#endif  // USE_SEPARATE_FAT_CACHE
+  cache_t* cacheFetchData(uint32_t blockNumber, uint8_t options) {
+    return m_cache.read(blockNumber, options);
+  }
+  void cacheInvalidate() {
+    m_cache.invalidate();
+  }
+  bool cacheSyncData() {
+    return m_cache.sync();
+  }
+  cache_t *cacheAddress() {
+    return m_cache.block();
+  }
+  uint32_t cacheBlockNumber() {
+    return m_cache.lbn();
+  }
+  void cacheDirty() {
+    m_cache.dirty();
+  }
+//------------------------------------------------------------------------------
+  bool allocateCluster(uint32_t current, uint32_t* next);
+  bool allocContiguous(uint32_t count, uint32_t* firstCluster);
+  uint8_t blockOfCluster(uint32_t position) const {
+    return (position >> 9) & m_clusterBlockMask;
+  }
+  uint32_t clusterStartBlock(uint32_t cluster) const;
+  int8_t fatGet(uint32_t cluster, uint32_t* value);
+  bool fatPut(uint32_t cluster, uint32_t value);
+  bool fatPutEOC(uint32_t cluster) {
+    return fatPut(cluster, 0x0FFFFFFF);
+  }
+  bool freeChain(uint32_t cluster);
+  bool isEOC(uint32_t cluster) const {
+    return cluster > m_lastCluster;
+  }
+  //----------------------------------------------------------------------------
+  // Virtual block I/O functions.
+  virtual bool readBlock(uint32_t block, uint8_t* dst) = 0;
+  virtual bool writeBlock(uint32_t block, const uint8_t* src) = 0;
+#if USE_MULTI_BLOCK_IO
+  virtual bool readBlocks(uint32_t block, uint8_t* dst, size_t nb) = 0;
+  virtual bool writeBlocks(uint32_t block, const uint8_t* src, size_t nb) = 0;
+#endif  // USE_MULTI_BLOCK_IO
+};
+#endif  // FatVolume

+ 78 - 28
SdFat/utility/FmtNumber.cpp

@@ -17,10 +17,10 @@
  * along with the FatLib Library.  If not, see
  * <http://www.gnu.org/licenses/>.
  */
-#include <avr/pgmspace.h>
-#include <FmtNumber.h>
+#include "FmtNumber.h"
 // Use Stimmer div/mod 10 on avr
 #ifdef __AVR__
+#include <avr/pgmspace.h>
 #define USE_STIMMER
 #endif  // __AVR__
 //------------------------------------------------------------------------------
@@ -149,9 +149,15 @@ unsigned divu10(unsigned n) {
 }
 */
 //------------------------------------------------------------------------------
-
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+#ifdef __AVR__
 static const float m[] PROGMEM = {1e-1, 1e-2, 1e-4, 1e-8, 1e-16, 1e-32};
 static const float p[] PROGMEM = {1e+1, 1e+2, 1e+4, 1e+8, 1e+16, 1e+32};
+#else  // __AVR__
+static const float m[] = {1e-1, 1e-2, 1e-4, 1e-8, 1e-16, 1e-32};
+static const float p[] = {1e+1, 1e+2, 1e+4, 1e+8, 1e+16, 1e+32};
+#endif  // __AVR__
+#endif  // DOXYGEN_SHOULD_SKIP_THIS
 // scale float v by power of ten. return v*10^n
 float scale10(float v, int8_t n) {
   const float *s;
@@ -163,7 +169,15 @@ float scale10(float v, int8_t n) {
   }
   n &= 63;
   for (uint8_t i = 0; n; n >>= 1, i++) {
-    if (n & 1) v *= pgm_read_float(&s[i]);
+#ifdef __AVR__
+    if (n & 1) {
+      v *= pgm_read_float(&s[i]);
+    }
+#else  // __AVR__
+    if (n & 1) {
+      v *= s[i];
+    }
+#endif  // __AVR__
   }
   return v;
 }
@@ -219,7 +233,9 @@ char* fmtDec(uint32_t n, char* p) {
 //------------------------------------------------------------------------------
 char* fmtFloat(float value, char* p, uint8_t prec) {
   char sign = value < 0 ? '-' : 0;
-  if (sign) value = -value;
+  if (sign) {
+    value = -value;
+  }
 
   if (isnan(value)) {
     *--p = 'n';
@@ -239,7 +255,9 @@ char* fmtFloat(float value, char* p, uint8_t prec) {
     *--p = 'o';
     return p;
   }
-  if (prec > 9) prec = 9;
+  if (prec > 9) {
+    prec = 9;
+  }
   value += scale10(0.5, -prec);
 
   uint32_t whole = value;
@@ -247,11 +265,15 @@ char* fmtFloat(float value, char* p, uint8_t prec) {
     char* tmp = p - prec;
     uint32_t fraction = scale10(value - whole, prec);
     p = fmtDec(fraction, p);
-    while (p > tmp) *--p = '0';
+    while (p > tmp) {
+      *--p = '0';
+    }
     *--p = '.';
   }
   p = fmtDec(whole, p);
-  if (sign) *--p = sign;
+  if (sign) {
+    *--p = sign;
+  }
   return p;
 }
 //------------------------------------------------------------------------------
@@ -264,7 +286,9 @@ char* fmtFloat(float value, char* p, uint8_t prec) {
  */
 char* fmtFloat(float value, char* ptr, uint8_t prec, char expChar) {
   bool neg = value < 0;
-  if (neg) value = -value;
+  if (neg) {
+    value = -value;
+  }
 
   // check for nan inf ovf
   if (isnan(value)) {
@@ -285,7 +309,9 @@ char* fmtFloat(float value, char* ptr, uint8_t prec, char expChar) {
     *--ptr = 'o';
     return ptr;
   }
-  if (prec > 9) prec = 9;
+  if (prec > 9) {
+    prec = 9;
+  }
   float round = scale10(0.5, -prec);
   if (expChar) {
     int8_t exp = 0;
@@ -305,10 +331,14 @@ char* fmtFloat(float value, char* ptr, uint8_t prec, char expChar) {
         exp++;
       }
       expNeg = exp < 0;
-      if (expNeg) exp = -exp;
+      if (expNeg) {
+        exp = -exp;
+      }
     }
     ptr = fmtDec((uint16_t)exp, ptr);
-    if (exp < 10) *--ptr = '0';
+    if (exp < 10) {
+      *--ptr = '0';
+    }
     *--ptr = expNeg ? '-' : '+';
     *--ptr = expChar;
   } else {
@@ -320,11 +350,15 @@ char* fmtFloat(float value, char* ptr, uint8_t prec, char expChar) {
     char* tmp = ptr - prec;
     uint32_t fraction = scale10(value - whole, prec);
     ptr = fmtDec(fraction, ptr);
-    while (ptr > tmp) *--ptr = '0';
+    while (ptr > tmp) {
+      *--ptr = '0';
+    }
     *--ptr = '.';
   }
   ptr = fmtDec(whole, ptr);
-  if (neg) *--ptr = '-';
+  if (neg) {
+    *--ptr = '-';
+  }
   return ptr;
 }
 //------------------------------------------------------------------------------
@@ -347,33 +381,45 @@ float scanFloat(const char* str, char** ptr) {
   bool neg;
   int c;
   float v;
-  const char* successPtr;
+  const char* successPtr = str;
 
-  if (ptr) *ptr = const_cast<char*>(str);
+  if (ptr) {
+    *ptr = const_cast<char*>(str);
+  }
 
-  while (isspace((c = *str++))) {}
+  while (isSpace((c = *str++))) {}
   neg = c == '-';
-  if (c == '-' || c == '+') c = *str++;
+  if (c == '-' || c == '+') {
+    c = *str++;
+  }
   // Skip leading zeros
   while (c == '0') {
     c = *str++;
     digit = true;
   }
   for (;;) {
-    if (isdigit(c)) {
+    if (isDigit(c)) {
       digit = true;
       if (nd < 9) {
         fract = 10*fract + c - '0';
         nd++;
-        if (dot) fracExp--;
+        if (dot) {
+          fracExp--;
+        }
       } else {
-        if (!dot) fracExp++;
+        if (!dot) {
+          fracExp++;
+        }
       }
     } else if (c == '.') {
-      if (dot) goto fail;
+      if (dot) {
+        goto fail;
+      }
       dot = true;
     } else {
-      if (!digit) goto fail;
+      if (!digit) {
+        goto fail;
+      }
       break;
     }
     successPtr = str;
@@ -386,19 +432,23 @@ float scanFloat(const char* str, char** ptr) {
     if (c == '-' || c == '+') {
       c = *str++;
     }
-    while (isdigit(c)) {
-      if (exp > EXP_LIMIT) goto fail;
+    while (isDigit(c)) {
+      if (exp > EXP_LIMIT) {
+        goto fail;
+      }
       exp = 10*exp + c - '0';
       successPtr = str;
       c = *str++;
     }
     fracExp += expNeg ? -exp : exp;
   }
-  if (ptr) *ptr = const_cast<char*>(successPtr);
+  if (ptr) {
+    *ptr = const_cast<char*>(successPtr);
+  }
   v = scale10(static_cast<float>(fract), fracExp);
-  return neg ? -v: v;
+  return neg ? -v : v;
 
- fail:
+fail:
   return 0;
 }
 

+ 9 - 1
SdFat/utility/FmtNumber.h

@@ -19,7 +19,15 @@
  */
 #ifndef FmtNumber_h
 #define FmtNumber_h
-#include <Arduino.h>
+//  #include <ctype.h>
+inline bool isDigit(char c) {
+  return '0' <= c && c <= '9';
+}
+inline bool isSpace(char c) {
+  return c == ' ' || (0X9 <= c && c <= 0XD);
+}
+#include <math.h>
+#include <stdint.h>
 char* fmtDec(uint16_t n, char* p);
 char* fmtDec(uint32_t n, char* p);
 char* fmtFloat(float value, char* p, uint8_t prec);

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません