From 450c4aeb9c4824c619f29306e953e51fde58f7db Mon Sep 17 00:00:00 2001 From: KemoNine Date: Sat, 12 Sep 2020 16:51:56 -0400 Subject: [PATCH] Bring in SdFat lib, turn on sd config in GUI Slice --- hardware/_controller/README.md | 3 +- hardware/_controller/src/SdFat/BlockDriver.h | 40 + .../src/SdFat/FatLib/ArduinoFiles.h | 249 +++ .../src/SdFat/FatLib/ArduinoStream.h | 153 ++ .../src/SdFat/FatLib/BaseBlockDriver.h | 80 + .../src/SdFat/FatLib/FatApiConstants.h | 87 + .../_controller/src/SdFat/FatLib/FatFile.cpp | 1531 +++++++++++++++++ .../_controller/src/SdFat/FatLib/FatFile.h | 1032 +++++++++++ .../src/SdFat/FatLib/FatFileLFN.cpp | 685 ++++++++ .../src/SdFat/FatLib/FatFilePrint.cpp | 267 +++ .../src/SdFat/FatLib/FatFileSFN.cpp | 278 +++ .../src/SdFat/FatLib/FatFileSystem.h | 332 ++++ .../_controller/src/SdFat/FatLib/FatLib.h | 36 + .../src/SdFat/FatLib/FatLibConfig.h | 146 ++ .../_controller/src/SdFat/FatLib/FatStructs.h | 882 ++++++++++ .../src/SdFat/FatLib/FatVolume.cpp | 625 +++++++ .../_controller/src/SdFat/FatLib/FatVolume.h | 396 +++++ .../src/SdFat/FatLib/FmtNumber.cpp | 460 +++++ .../_controller/src/SdFat/FatLib/FmtNumber.h | 43 + .../src/SdFat/FatLib/StdioStream.cpp | 508 ++++++ .../src/SdFat/FatLib/StdioStream.h | 667 +++++++ .../_controller/src/SdFat/FatLib/bufstream.h | 172 ++ .../_controller/src/SdFat/FatLib/fstream.cpp | 172 ++ .../_controller/src/SdFat/FatLib/fstream.h | 320 ++++ hardware/_controller/src/SdFat/FatLib/ios.h | 423 +++++ .../_controller/src/SdFat/FatLib/iostream.h | 158 ++ .../_controller/src/SdFat/FatLib/istream.cpp | 396 +++++ .../_controller/src/SdFat/FatLib/istream.h | 384 +++++ .../_controller/src/SdFat/FatLib/ostream.cpp | 196 +++ .../_controller/src/SdFat/FatLib/ostream.h | 276 +++ hardware/_controller/src/SdFat/FreeStack.h | 61 + .../_controller/src/SdFat/MinimumSerial.cpp | 71 + .../_controller/src/SdFat/MinimumSerial.h | 67 + .../_controller/src/SdFat/SdCard/SdInfo.h | 485 ++++++ .../src/SdFat/SdCard/SdSpiCard.cpp | 802 +++++++++ .../_controller/src/SdFat/SdCard/SdSpiCard.h | 379 ++++ .../src/SdFat/SdCard/SdSpiCardEX.cpp | 94 + .../_controller/src/SdFat/SdCard/SdioCard.h | 303 ++++ .../src/SdFat/SdCard/SdioCardEX.cpp | 108 ++ .../src/SdFat/SdCard/SdioTeensy.cpp | 800 +++++++++ hardware/_controller/src/SdFat/SdFat.h | 512 ++++++ hardware/_controller/src/SdFat/SdFatConfig.h | 235 +++ .../src/SdFat/SpiDriver/DigitalPin.h | 386 +++++ .../src/SdFat/SpiDriver/SdSpiBaseDriver.h | 79 + .../src/SdFat/SpiDriver/SdSpiDriver.h | 438 +++++ .../src/SdFat/SpiDriver/SdSpiESP8266.cpp | 104 ++ .../src/SdFat/SpiDriver/SdSpiParticle.cpp | 102 ++ .../src/SdFat/SpiDriver/SdSpiSAM3X.cpp | 218 +++ .../src/SdFat/SpiDriver/SdSpiSTM32.cpp | 105 ++ .../src/SdFat/SpiDriver/SdSpiTeensy3.cpp | 233 +++ .../_controller/src/SdFat/SpiDriver/SoftSPI.h | 167 ++ .../boards/AvrDevelopersGpioPinMap.h | 37 + .../SpiDriver/boards/BobuinoGpioPinMap.h | 37 + .../src/SdFat/SpiDriver/boards/GpioPinMap.h | 45 + .../SpiDriver/boards/LeonardoGpioPinMap.h | 35 + .../SdFat/SpiDriver/boards/MegaGpioPinMap.h | 75 + .../boards/SleepingBeautyGpioPinMap.h | 37 + .../SpiDriver/boards/Standard1284GpioPinMap.h | 37 + .../SpiDriver/boards/Teensy2GpioPinMap.h | 30 + .../SpiDriver/boards/Teensy2ppGpioPinMap.h | 51 + .../SdFat/SpiDriver/boards/UnoGpioPinMap.h | 25 + hardware/_controller/src/SdFat/SysCall.h | 88 + hardware/_controller/src/SdFat/sdios.h | 33 + .../src/guislice/GUIslice_drv_adagfx.cpp | 2 +- .../src/guislice/ard-adagfx-ili9341-notouch.h | 2 +- .../guislice/ard-adagfx-ili9341-stmpe610.h | 2 +- 66 files changed, 17278 insertions(+), 4 deletions(-) create mode 100644 hardware/_controller/src/SdFat/BlockDriver.h create mode 100644 hardware/_controller/src/SdFat/FatLib/ArduinoFiles.h create mode 100644 hardware/_controller/src/SdFat/FatLib/ArduinoStream.h create mode 100644 hardware/_controller/src/SdFat/FatLib/BaseBlockDriver.h create mode 100644 hardware/_controller/src/SdFat/FatLib/FatApiConstants.h create mode 100644 hardware/_controller/src/SdFat/FatLib/FatFile.cpp create mode 100644 hardware/_controller/src/SdFat/FatLib/FatFile.h create mode 100644 hardware/_controller/src/SdFat/FatLib/FatFileLFN.cpp create mode 100644 hardware/_controller/src/SdFat/FatLib/FatFilePrint.cpp create mode 100644 hardware/_controller/src/SdFat/FatLib/FatFileSFN.cpp create mode 100644 hardware/_controller/src/SdFat/FatLib/FatFileSystem.h create mode 100644 hardware/_controller/src/SdFat/FatLib/FatLib.h create mode 100644 hardware/_controller/src/SdFat/FatLib/FatLibConfig.h create mode 100644 hardware/_controller/src/SdFat/FatLib/FatStructs.h create mode 100644 hardware/_controller/src/SdFat/FatLib/FatVolume.cpp create mode 100644 hardware/_controller/src/SdFat/FatLib/FatVolume.h create mode 100644 hardware/_controller/src/SdFat/FatLib/FmtNumber.cpp create mode 100644 hardware/_controller/src/SdFat/FatLib/FmtNumber.h create mode 100644 hardware/_controller/src/SdFat/FatLib/StdioStream.cpp create mode 100644 hardware/_controller/src/SdFat/FatLib/StdioStream.h create mode 100644 hardware/_controller/src/SdFat/FatLib/bufstream.h create mode 100644 hardware/_controller/src/SdFat/FatLib/fstream.cpp create mode 100644 hardware/_controller/src/SdFat/FatLib/fstream.h create mode 100644 hardware/_controller/src/SdFat/FatLib/ios.h create mode 100644 hardware/_controller/src/SdFat/FatLib/iostream.h create mode 100644 hardware/_controller/src/SdFat/FatLib/istream.cpp create mode 100644 hardware/_controller/src/SdFat/FatLib/istream.h create mode 100644 hardware/_controller/src/SdFat/FatLib/ostream.cpp create mode 100644 hardware/_controller/src/SdFat/FatLib/ostream.h create mode 100644 hardware/_controller/src/SdFat/FreeStack.h create mode 100644 hardware/_controller/src/SdFat/MinimumSerial.cpp create mode 100644 hardware/_controller/src/SdFat/MinimumSerial.h create mode 100644 hardware/_controller/src/SdFat/SdCard/SdInfo.h create mode 100644 hardware/_controller/src/SdFat/SdCard/SdSpiCard.cpp create mode 100644 hardware/_controller/src/SdFat/SdCard/SdSpiCard.h create mode 100644 hardware/_controller/src/SdFat/SdCard/SdSpiCardEX.cpp create mode 100644 hardware/_controller/src/SdFat/SdCard/SdioCard.h create mode 100644 hardware/_controller/src/SdFat/SdCard/SdioCardEX.cpp create mode 100644 hardware/_controller/src/SdFat/SdCard/SdioTeensy.cpp create mode 100644 hardware/_controller/src/SdFat/SdFat.h create mode 100644 hardware/_controller/src/SdFat/SdFatConfig.h create mode 100644 hardware/_controller/src/SdFat/SpiDriver/DigitalPin.h create mode 100644 hardware/_controller/src/SdFat/SpiDriver/SdSpiBaseDriver.h create mode 100644 hardware/_controller/src/SdFat/SpiDriver/SdSpiDriver.h create mode 100644 hardware/_controller/src/SdFat/SpiDriver/SdSpiESP8266.cpp create mode 100644 hardware/_controller/src/SdFat/SpiDriver/SdSpiParticle.cpp create mode 100644 hardware/_controller/src/SdFat/SpiDriver/SdSpiSAM3X.cpp create mode 100644 hardware/_controller/src/SdFat/SpiDriver/SdSpiSTM32.cpp create mode 100644 hardware/_controller/src/SdFat/SpiDriver/SdSpiTeensy3.cpp create mode 100644 hardware/_controller/src/SdFat/SpiDriver/SoftSPI.h create mode 100644 hardware/_controller/src/SdFat/SpiDriver/boards/AvrDevelopersGpioPinMap.h create mode 100644 hardware/_controller/src/SdFat/SpiDriver/boards/BobuinoGpioPinMap.h create mode 100644 hardware/_controller/src/SdFat/SpiDriver/boards/GpioPinMap.h create mode 100644 hardware/_controller/src/SdFat/SpiDriver/boards/LeonardoGpioPinMap.h create mode 100644 hardware/_controller/src/SdFat/SpiDriver/boards/MegaGpioPinMap.h create mode 100644 hardware/_controller/src/SdFat/SpiDriver/boards/SleepingBeautyGpioPinMap.h create mode 100644 hardware/_controller/src/SdFat/SpiDriver/boards/Standard1284GpioPinMap.h create mode 100644 hardware/_controller/src/SdFat/SpiDriver/boards/Teensy2GpioPinMap.h create mode 100644 hardware/_controller/src/SdFat/SpiDriver/boards/Teensy2ppGpioPinMap.h create mode 100644 hardware/_controller/src/SdFat/SpiDriver/boards/UnoGpioPinMap.h create mode 100644 hardware/_controller/src/SdFat/SysCall.h create mode 100644 hardware/_controller/src/SdFat/sdios.h diff --git a/hardware/_controller/README.md b/hardware/_controller/README.md index 8a5a557..1c3f2d2 100644 --- a/hardware/_controller/README.md +++ b/hardware/_controller/README.md @@ -15,7 +15,8 @@ A debug console and configuration shield for serial TTL muxers. See the ```muxer These librarires are used by the project and contained in the ```src/``` folder local to the project. -- [GUIslice](https://github.com/ImpulseAdventure/GUIslice) +- [GUIslice 0.15](https://github.com/ImpulseAdventure/GUIslice) +- [SdFat 1.1.4](https://github.com/greiman/SdFat) ### External diff --git a/hardware/_controller/src/SdFat/BlockDriver.h b/hardware/_controller/src/SdFat/BlockDriver.h new file mode 100644 index 0000000..1bb65fc --- /dev/null +++ b/hardware/_controller/src/SdFat/BlockDriver.h @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + /** + * \file + * \brief Define block driver. + */ +#ifndef BlockDriver_h +#define BlockDriver_h +#include "FatLib/BaseBlockDriver.h" +#include "SdCard/SdSpiCard.h" +//----------------------------------------------------------------------------- +/** typedef for BlockDriver */ +#if ENABLE_EXTENDED_TRANSFER_CLASS || ENABLE_SDIO_CLASS +typedef BaseBlockDriver BlockDriver; +#else // ENABLE_EXTENDED_TRANSFER_CLASS || ENABLE_SDIO_CLASS +typedef SdSpiCard BlockDriver; +#endif // ENABLE_EXTENDED_TRANSFER_CLASS || ENABLE_SDIO_CLASS +#endif // BlockDriver_h diff --git a/hardware/_controller/src/SdFat/FatLib/ArduinoFiles.h b/hardware/_controller/src/SdFat/FatLib/ArduinoFiles.h new file mode 100644 index 0000000..a16b65b --- /dev/null +++ b/hardware/_controller/src/SdFat/FatLib/ArduinoFiles.h @@ -0,0 +1,249 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +/** + * \file + * \brief PrintFile class + */ +#ifndef ArduinoFiles_h +#define ArduinoFiles_h +#include "FatLibConfig.h" +#if ENABLE_ARDUINO_FEATURES +#include "FatFile.h" +#include +//------------------------------------------------------------------------------ +/** Arduino SD.h style flag for open for read. */ +#define FILE_READ O_RDONLY +/** 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*, oflag_t). + */ + PrintFile(const char* path, oflag_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 + */ +class File : public FatFile, public 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*, oflag_t). + */ + File(const char* path, oflag_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] oflag open oflag flags. + * \return a File object. + */ + File openNextFile(oflag_t oflag = O_RDONLY) { + File tmpFile; + tmpFile.openNext(this, oflag); + 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 diff --git a/hardware/_controller/src/SdFat/FatLib/ArduinoStream.h b/hardware/_controller/src/SdFat/FatLib/ArduinoStream.h new file mode 100644 index 0000000..e08d1f3 --- /dev/null +++ b/hardware/_controller/src/SdFat/FatLib/ArduinoStream.h @@ -0,0 +1,153 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef ArduinoStream_h +#define ArduinoStream_h +/** + * \file + * \brief ArduinoInStream and ArduinoOutStream classes + */ +#include "FatLibConfig.h" +#if ENABLE_ARDUINO_FEATURES +#include "bufstream.h" +//============================================================================== +/** + * \class ArduinoInStream + * \brief Input stream for Arduino Stream objects + */ +class ArduinoInStream : public ibufstream { + public: + /** + * Constructor + * \param[in] hws hardware stream + * \param[in] buf buffer for input line + * \param[in] size size of input buffer + */ + ArduinoInStream(Stream &hws, char* buf, size_t size) { + m_hw = &hws; + m_line = buf; + m_size = size; + } + /** read a line. */ + void readline() { + size_t i = 0; + uint32_t t; + m_line[0] = '\0'; + while (!m_hw->available()) { + yield(); + } + + while (1) { + t = millis(); + while (!m_hw->available()) { + if ((millis() - t) > 10) { + goto done; + } + } + if (i >= (m_size - 1)) { + setstate(failbit); + return; + } + m_line[i++] = m_hw->read(); + m_line[i] = '\0'; + } +done: + init(m_line); + } + + protected: + /** Internal - do not use. + * \param[in] off + * \param[in] way + * \return true/false. + */ + bool seekoff(off_type off, seekdir way) { + (void)off; + (void)way; + return false; + } + /** Internal - do not use. + * \param[in] pos + * \return true/false. + */ + bool seekpos(pos_type pos) { + (void)pos; + return false; + } + + private: + char *m_line; + size_t m_size; + Stream* m_hw; +}; +//============================================================================== +/** + * \class ArduinoOutStream + * \brief Output stream for Arduino Print objects + */ +class ArduinoOutStream : public ostream { + public: + /** constructor + * + * \param[in] pr Print object for this ArduinoOutStream. + */ + explicit ArduinoOutStream(Print& pr) : m_pr(&pr) {} + + protected: + /// @cond SHOW_PROTECTED + /** + * Internal do not use + * \param[in] c + */ + void putch(char c) { + 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) { + (void)off; + (void)way; + return false; + } + bool seekpos(pos_type pos) { + (void)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 diff --git a/hardware/_controller/src/SdFat/FatLib/BaseBlockDriver.h b/hardware/_controller/src/SdFat/FatLib/BaseBlockDriver.h new file mode 100644 index 0000000..bb4c820 --- /dev/null +++ b/hardware/_controller/src/SdFat/FatLib/BaseBlockDriver.h @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef BaseBlockDriver_h +#define BaseBlockDriver_h +#include "FatLibConfig.h" +/** + * \class BaseBlockDriver + * \brief Base block driver. + */ +class BaseBlockDriver { + public: + /** + * 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. + */ + virtual bool readBlock(uint32_t block, uint8_t* dst) = 0; + /** End multi-block transfer and go to idle state. + * \return The value true is returned for success and + * the value false is returned for failure. + */ + virtual bool syncBlocks() = 0; + /** + * Writes a 512 byte block to an SD card. + * + * \param[in] block 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. + */ + virtual bool writeBlock(uint32_t block, const uint8_t* src) = 0; +#if USE_MULTI_BLOCK_IO + /** + * Read multiple 512 byte blocks from an SD card. + * + * \param[in] block Logical block to be read. + * \param[in] nb 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. + */ + virtual bool readBlocks(uint32_t block, uint8_t* dst, size_t nb) = 0; + /** + * Write multiple 512 byte blocks to an SD card. + * + * \param[in] block Logical block to be written. + * \param[in] nb 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. + */ + virtual bool writeBlocks(uint32_t block, const uint8_t* src, size_t nb) = 0; +#endif // USE_MULTI_BLOCK_IO +}; +#endif // BaseBlockDriver_h diff --git a/hardware/_controller/src/SdFat/FatLib/FatApiConstants.h b/hardware/_controller/src/SdFat/FatLib/FatApiConstants.h new file mode 100644 index 0000000..376b9b5 --- /dev/null +++ b/hardware/_controller/src/SdFat/FatLib/FatApiConstants.h @@ -0,0 +1,87 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef FatApiConstants_h +#define FatApiConstants_h +#include "../SdFatConfig.h" + +#if USE_FCNTL_H +#include +/* values for GNU Arm Embedded Toolchain. + * O_RDONLY: 0x0 + * O_WRONLY: 0x1 + * O_RDWR: 0x2 + * O_ACCMODE: 0x3 + * O_APPEND: 0x8 + * O_CREAT: 0x200 + * O_TRUNC: 0x400 + * O_EXCL: 0x800 + * O_SYNC: 0x2000 + * O_NONBLOCK: 0x4000 + */ +/** Use O_NONBLOCK for open at EOF */ +#define O_AT_END O_NONBLOCK ///< Open at EOF. +typedef int oflag_t; +#else // USE_FCNTL_H +#define O_RDONLY 0X00 ///< Open for reading only. +#define O_WRONLY 0X01 ///< Open for writing only. +#define O_RDWR 0X02 ///< Open for reading and writing. +#define O_AT_END 0X04 ///< Open at EOF. +#define O_APPEND 0X08 ///< Set append mode. +#define O_CREAT 0x10 ///< Create file if it does not exist. +#define O_TRUNC 0x20 ///< Truncate file to zero length. +#define O_EXCL 0x40 ///< Fail if the file exists. +#define O_SYNC 0x80 ///< Synchronized write I/O operations. + +#define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR) ///< Mask for access mode. +typedef uint8_t oflag_t; +#endif // USE_FCNTL_H + +#define O_READ O_RDONLY +#define O_WRITE O_WRONLY + +inline bool isWriteMode(oflag_t oflag) { + oflag &= O_ACCMODE; + return oflag == O_WRONLY || oflag == O_RDWR; +} + +// FatFile class static and const definitions +// flags for ls() +/** ls() flag for list all files including hidden. */ +#define LS_A 1 +/** ls() flag to print modify. date */ +#define LS_DATE 2 +/** ls() flag to print file size. */ +#define LS_SIZE 4 +/** ls() flag for recursive list of subdirectories */ +#define LS_R 8 + +// flags for timestamp +/** set the file's last access date */ +#define T_ACCESS 1 +/** set the file's creation date and time */ +#define T_CREATE 2 +/** Set the file's write date and time */ +#define T_WRITE 4 +#endif // FatApiConstants_h diff --git a/hardware/_controller/src/SdFat/FatLib/FatFile.cpp b/hardware/_controller/src/SdFat/FatLib/FatFile.cpp new file mode 100644 index 0000000..a47788f --- /dev/null +++ b/hardware/_controller/src/SdFat/FatLib/FatFile.cpp @@ -0,0 +1,1531 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "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->clusterFirstBlock(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->clusterFirstBlock(m_firstCluster); + *endBlock = m_vol->clusterFirstBlock(c) + + m_vol->blocksPerCluster() - 1; + return true; + } + } + +fail: + return false; +} +//------------------------------------------------------------------------------ +bool FatFile::createContiguous(FatFile* dirFile, const char* path, + uint32_t size, uint32_t startCluster) { + uint32_t count; + + // don't allow zero length file + if (size == 0) { + DBG_FAIL_MACRO; + goto fail; + } + if (!open(dirFile, path, O_RDWR | O_CREAT | O_EXCL)) { + 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, startCluster)) { + 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 || !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_RDONLY)) { + 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 || !parent->isDir()) { + DBG_FAIL_MACRO; + goto fail; + } + // create a normal file + if (!open(parent, fname, O_RDWR | O_CREAT | O_EXCL)) { + DBG_FAIL_MACRO; + goto fail; + } + // convert file to directory + m_flags = F_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->clusterFirstBlock(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, oflag_t oflag) { + return open(fs->vwd(), path, oflag); +} +//------------------------------------------------------------------------------ +bool FatFile::open(FatFile* dirFile, const char* path, oflag_t oflag) { + FatFile tmpDir; + fname_t fname; + + // error if already open + if (isOpen() || !dirFile || !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_RDONLY)) { + 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, oflag_t oflag) { + uint8_t chksum = 0; + uint8_t lfnOrd = 0; + dir_t* dir; + ldir_t*ldir; + + // Error if already open. + if (isOpen() || !dirFile || !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(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, + oflag_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; + + switch (oflag & O_ACCMODE) { + case O_RDONLY: + if (oflag & O_TRUNC) { + DBG_FAIL_MACRO; + goto fail; + } + m_flags = F_READ; + break; + + case O_RDWR: + m_flags = F_READ | F_WRITE; + break; + + case O_WRONLY: + m_flags = F_WRITE; + break; + + default: + DBG_FAIL_MACRO; + goto fail; + } + + if (m_flags & F_WRITE) { + if (isSubDir() || isReadOnly()) { + DBG_FAIL_MACRO; + goto fail; + } + } + + m_flags |= (oflag & O_APPEND ? F_APPEND : 0) | (oflag & O_SYNC ? F_SYNC : 0); + + 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::openCwd() { + if (!cwd()) { + DBG_FAIL_MACRO; + return false; + } + *this = *cwd(); + rewind(); + return true; +} +//------------------------------------------------------------------------------ +bool FatFile::openNext(FatFile* dirFile, oflag_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 || !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(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 || !dirFile->isOpen()) { + goto fail; + } + if (dirFile->m_dirCluster == 0) { + return openRoot(dirFile->m_vol); + } + lbn = dirFile->m_vol->clusterFirstBlock(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 = F_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_RDONLY); + +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 = F_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(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 & F_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->clusterFirstBlock(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) { + size_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->cacheSyncData()) { + 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; + } + 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_WRONLY)) { + 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_WRONLY | O_CREAT | O_EXCL)) { + 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->clusterFirstBlock(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->clusterFirstBlock(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 = F_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 |= F_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_RDONLY)) { + 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 |= F_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; + } + // Optimize O_APPEND writes. + if (pos == m_curPosition) { + return true; + } + 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; + + // must be files to get timestamps + if (!isFile() || !file || !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 & F_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(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 & F_WRITE)) { + DBG_FAIL_MACRO; + goto fail; + } + // seek to end of file if append flag + if ((m_flags & F_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->clusterFirstBlock(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; + size_t nb = nToWrite >> 9; + if (nb > maxBlocks) { + nb = maxBlocks; + } + n = 512*nb; + if (m_vol->cacheBlockNumber() <= block + && block < (m_vol->cacheBlockNumber() + nb)) { + // invalidate cache if block is in cache + m_vol->cacheInvalidate(); + } + if (!m_vol->writeBlocks(block, src, nb)) { + 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 & F_SYNC) { + if (!sync()) { + DBG_FAIL_MACRO; + goto fail; + } + } + return nbyte; + +fail: + // return for write error + m_error |= WRITE_ERROR; + return -1; +} diff --git a/hardware/_controller/src/SdFat/FatLib/FatFile.h b/hardware/_controller/src/SdFat/FatLib/FatFile.h new file mode 100644 index 0000000..f5e1fc5 --- /dev/null +++ b/hardware/_controller/src/SdFat/FatLib/FatFile.h @@ -0,0 +1,1032 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef FatFile_h +#define FatFile_h +/** + * \file + * \brief FatFile class + */ +// #include +#include +#include +#include +#include "FatLibConfig.h" +#include "FatApiConstants.h" +#include "FatStructs.h" +#include "FatVolume.h" +class FatFileSystem; +//------------------------------------------------------------------------------ +// Stuff to store strings in AVR flash. +#ifdef __AVR__ +#include +#else // __AVR__ +#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 +#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*, oflag_t). + */ + FatFile(const char* path, oflag_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. + * + * \return true for success or false if an error occurred. + */ + bool ls(uint8_t flags = 0) { + return 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. + * + * \param[in] dirFile The directory where the file will be created. + * \param[in] path A path with a valid file name. + * \param[in] size The desired file size. + * \param[in] startCluster The desired startCluster. + * + * \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, uint32_t startCluster = 0); + /** Create and open a new contiguous file of a specified size. + * + * \param[in] path A path with a valid file name. + * \param[in] size The desired file size. + * \param[in] startCluster The desired startCluster. + * + * \return The value true is returned for success and + * the value false, is returned for failure. + */ + bool createContiguous(const char* path, + uint32_t size, uint32_t startCluster = 0) { + return createContiguous(m_cwd, path, size, startCluster); + } + /** \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_RDONLY); + } + /** + * 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. + * + * \return true for success or false if an error occurred. + */ + bool 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*, oflag_t). + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool open(FatFileSystem* fs, const char* path, oflag_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*, oflag_t). + * + * See open() by path for definition of flags. + * \return true for success or false for failure. + */ + bool open(FatFile* dirFile, uint16_t index, oflag_t oflag); + /** 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_RDONLY - Open for reading. + * + * O_READ - Same as O_RDONLY (GNU). + * + * O_WRONLY - Open for writing. + * + * O_WRITE - Same as O_WRONLY (GNU). + * + * 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, oflag_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*, oflag_t). + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool open(const char* path, oflag_t oflag = O_RDONLY) { + return open(m_cwd, path, oflag); + } + /** Open current working directory. + * + * \return true for success or false for failure. + */ + bool openCwd(); + /** Open the next file or subdirectory in a directory. + * + * \param[in] dirFile An open 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*, oflag_t). + * + * \return true for success or false for failure. + */ + bool openNext(FatFile* dirFile, oflag_t oflag = O_RDONLY); + /** 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. + * + * \note the file will be moved to the current working directory. + * + * \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(const char* newPath) { + return rename(cwd(), newPath); + } + /** 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 || !dir->isDir()) { + return false; + } + m_cwd = dir; + return true; + } + /** \return first block of file or zero for empty file. */ + uint32_t firstBlock() { + if (m_firstCluster) { + return m_vol->clusterFirstBlock(m_firstCluster); + } + return 0; + } + /** 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, oflag_t oflag); + bool openCachedEntry(FatFile* dirFile, uint16_t cacheIndex, oflag_t oflag, + uint8_t lfnOrd); + bool readLBN(uint32_t* lbn); + dir_t* readDirCache(bool skipReadOk = false); + bool setDirSize(); + + // bits defined in m_flags + static const uint8_t F_READ = 0X01; + static const uint8_t F_WRITE = 0X02; + static const uint8_t F_FILE_DIR_DIRTY = 0X04; + static const uint8_t F_APPEND = 0X08; + static const uint8_t F_SYNC = 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 diff --git a/hardware/_controller/src/SdFat/FatLib/FatFileLFN.cpp b/hardware/_controller/src/SdFat/FatLib/FatFileLFN.cpp new file mode 100644 index 0000000..4ff9867 --- /dev/null +++ b/hardware/_controller/src/SdFat/FatLib/FatFileLFN.cpp @@ -0,0 +1,685 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "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(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 = F_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, oflag_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 || !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(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 write mode. + if (!(oflag & O_CREAT) || !isWriteMode(oflag)) { + 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(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; + ldir_t* ldir; + size_t n = 0; + uint16_t u; + uint8_t buf[13]; + uint8_t i; + + 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(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 (i = 0; i < 13; i++) { + u = lfnGetChar(ldir, i); + if (u == 0) { + // End of name. + break; + } + buf[i] = u < 0X7F ? u : '?'; + n++; + } + pr->write(buf, i); + } + return n; + +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 & F_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(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 diff --git a/hardware/_controller/src/SdFat/FatLib/FatFilePrint.cpp b/hardware/_controller/src/SdFat/FatLib/FatFilePrint.cpp new file mode 100644 index 0000000..5cc79e8 --- /dev/null +++ b/hardware/_controller/src/SdFat/FatLib/FatFilePrint.cpp @@ -0,0 +1,267 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include +#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'); +} +//------------------------------------------------------------------------------ +bool FatFile::ls(print_t* pr, uint8_t flags, uint8_t indent) { + FatFile file; + if (!isDir() || getError()) { + DBG_FAIL_MACRO; + goto fail; + } + rewind(); + while (file.openNext(this, O_RDONLY)) { + if (!file.isHidden() || (flags & LS_A)) { + // indent for dir level + 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()) { + if (!file.ls(pr, flags, indent + 2)) { + DBG_FAIL_MACRO; + goto fail; + } + } + } + file.close(); + } + if (getError()) { + DBG_FAIL_MACRO; + goto fail; + } + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +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 +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); +} diff --git a/hardware/_controller/src/SdFat/FatLib/FatFileSFN.cpp b/hardware/_controller/src/SdFat/FatLib/FatFileSFN.cpp new file mode 100644 index 0000000..afb045f --- /dev/null +++ b/hardware/_controller/src/SdFat/FatLib/FatFileSFN.cpp @@ -0,0 +1,278 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "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, oflag_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(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 write mode + if (!(oflag & O_CREAT) || !isWriteMode(oflag)) { + 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 & F_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 diff --git a/hardware/_controller/src/SdFat/FatLib/FatFileSystem.h b/hardware/_controller/src/SdFat/FatLib/FatFileSystem.h new file mode 100644 index 0000000..044b9cf --- /dev/null +++ b/hardware/_controller/src/SdFat/FatLib/FatFileSystem.h @@ -0,0 +1,332 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef 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] blockDev Device block driver. + * \param[in] part partition to initialize. + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool begin(BlockDriver* blockDev, uint8_t part = 0) { + m_blockDev = blockDev; + 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. + * + * \return true for success or false if an error occurred. + */ + bool ls(uint8_t flags = 0) { + return 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. + * + * \return true for success or false if an error occurred. + */ + bool ls(const char* path, uint8_t flags = 0) { + return ls(&Serial, path, flags); + } + /** open a file + * + * \param[in] path location of file to be opened. + * \param[in] oflag open flags. + * \return a File object. + */ + File open(const char *path, oflag_t oflag = FILE_READ) { + File tmpFile; + tmpFile.open(vwd(), path, oflag); + return tmpFile; + } + /** open a file + * + * \param[in] path location of file to be opened. + * \param[in] oflag open flags. + * \return a File object. + */ + File open(const String &path, oflag_t oflag = FILE_READ) { + return open(path.c_str(), oflag ); + } +#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_RDONLY)) { + goto fail; + } + if (!dir.isDir()) { + goto fail; + } + 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 \: 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. + * + * \return true for success or false if an error occurred. + */ + bool ls(print_t* pr, uint8_t flags = 0) { + return 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. + * + * \return true for success or false if an error occurred. + */ + bool ls(print_t* pr, const char* path, uint8_t flags) { + FatFile dir; + return dir.open(vwd(), path, O_RDONLY) && 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_RDONLY)) { + 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_RDONLY)) { + 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_WRONLY)) { + 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 diff --git a/hardware/_controller/src/SdFat/FatLib/FatLib.h b/hardware/_controller/src/SdFat/FatLib/FatLib.h new file mode 100644 index 0000000..f054ea2 --- /dev/null +++ b/hardware/_controller/src/SdFat/FatLib/FatLib.h @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef FatLib_h +#define FatLib_h +#include "ArduinoFiles.h" +#include "FatFileSystem.h" +#include "FatLibConfig.h" +#include "FatVolume.h" +#include "FatFile.h" +#include "StdioStream.h" +//------------------------------------------------------------------------------ +/** FatFileSystem version YYYYMMDD */ +#define FAT_LIB_VERSION 20150131 +#endif // FatLib_h diff --git a/hardware/_controller/src/SdFat/FatLib/FatLibConfig.h b/hardware/_controller/src/SdFat/FatLib/FatLibConfig.h new file mode 100644 index 0000000..bbefe36 --- /dev/null +++ b/hardware/_controller/src/SdFat/FatLib/FatLibConfig.h @@ -0,0 +1,146 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +/** + * \file + * \brief configuration definitions + */ +#ifndef FatLibConfig_h +#define FatLibConfig_h +#include +// Allow this file to override defaults. +#include "../SdFatConfig.h" + +#ifdef __AVR__ +#include +#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 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 MAINTAIN_FREE_CLUSTER_COUNT nonzero to keep the count of free clusters + * updated. This will increase the speed of the freeClusterCount() call + * after the first call. Extra flash will be required. + */ +#ifndef MAINTAIN_FREE_CLUSTER_COUNT +#define MAINTAIN_FREE_CLUSTER_COUNT 0 +#endif // MAINTAIN_FREE_CLUSTER_COUNT +//------------------------------------------------------------------------------ +/** + * 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. + */ +// #define ENABLE_ARDUINO_FEATURES 0 ////////////////////////FIX THIS ///////////////// +#ifndef ENABLE_ARDUINO_FEATURES +#include +#if defined(ARDUINO) || defined(PLATFORM_ID) || 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 diff --git a/hardware/_controller/src/SdFat/FatLib/FatStructs.h b/hardware/_controller/src/SdFat/FatLib/FatStructs.h new file mode 100644 index 0000000..0727d10 --- /dev/null +++ b/hardware/_controller/src/SdFat/FatLib/FatStructs.h @@ -0,0 +1,882 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef FatStructs_h +#define FatStructs_h +/** + * \file + * \brief FAT file structures + */ +/* + * mostly from Microsoft document fatgen103.doc + * http://www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx + */ +//------------------------------------------------------------------------------ +/** Value for byte 510 of boot block or MBR */ +const uint8_t BOOTSIG0 = 0X55; +/** Value for byte 511 of boot block or MBR */ +const uint8_t BOOTSIG1 = 0XAA; +/** Value for bootSignature field int FAT/FAT32 boot sector */ +const uint8_t EXTENDED_BOOT_SIG = 0X29; +//------------------------------------------------------------------------------ +/** + * \struct partitionTable + * \brief MBR partition table entry + * + * A partition table entry for a MBR formatted storage device. + * 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. + */ + 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. + */ + 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. + */ + unsigned beginSector : 6; + /** 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. + */ + uint8_t beginCylinderLow; + /** + * 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. + */ + 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. + */ + unsigned endSector : 6; + /** High bits of end cylinder */ + unsigned endCylinderHigh : 2; + /** + * 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. */ + uint32_t firstSector; + /** Length of the partition, in blocks. */ + uint32_t totalSectors; +} __attribute__((packed)); +/** Type name for partitionTable */ +typedef struct partitionTable part_t; +//------------------------------------------------------------------------------ +/** + * \struct masterBootRecord + * + * \brief Master Boot Record + * + * The first block of a storage device that is formatted with a MBR. + */ +struct masterBootRecord { + /** Code Area for master boot program. */ + uint8_t codeArea[440]; + /** Optional Windows NT disk signature. May contain boot code. */ + uint32_t diskSignature; + /** Usually zero but may be more boot code. */ + uint16_t usuallyZero; + /** Partition tables. */ + part_t part[4]; + /** First MBR signature byte. Must be 0X55 */ + uint8_t mbrSig0; + /** Second MBR signature byte. Must be 0XAA */ + uint8_t mbrSig1; +} __attribute__((packed)); +/** Type name for masterBootRecord */ +typedef struct masterBootRecord mbr_t; +//------------------------------------------------------------------------------ +/** + * \struct biosParmBlock + * + * \brief BIOS parameter block + * + * The BIOS parameter block describes the physical layout of a FAT volume. + */ +struct biosParmBlock { + /** + * Count of bytes per sector. This value may take on only the + * following values: 512, 1024, 2048 or 4096 + */ + 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. + */ + uint8_t sectorsPerCluster; + /** + * Number of sectors before the first FAT. + * This value must not be zero. + */ + uint16_t reservedSectorCount; + /** The count of FAT data structures on the volume. This field should + * always contain the value 2 for any FAT volume of any type. + */ + 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. + */ + 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 nonzero. 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. + */ + 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. + */ + uint16_t sectorsPerFat16; + /** Sectors per track for interrupt 0x13. Not used otherwise. */ + uint16_t sectorsPerTrtack; + /** 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. + */ + 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 nonzero. + */ + uint32_t totalSectors32; + /** + * 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. + */ + uint16_t fat32Flags; + /** + * 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. + */ + uint32_t fat32RootCluster; + /** + * Sector number of FSINFO structure in the reserved area of the + * FAT32 volume. Usually 1. + */ + uint16_t fat32FSInfo; + /** + * If nonzero, 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. + */ + uint8_t fat32Reserved[12]; +} __attribute__((packed)); +/** Type name for biosParmBlock */ +typedef struct biosParmBlock bpb_t; +//------------------------------------------------------------------------------ +/** + * \struct fat_boot + * + * \brief Boot sector for a FAT12/FAT16 volume. + * + */ +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. + */ + uint8_t jump[3]; + /** + * 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. + */ + 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. + */ + 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. + */ + uint16_t reservedSectorCount; + /** + * 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. + */ + 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). + */ + 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. + */ + 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. + */ + uint16_t sectorsPerFat16; + /** Sectors per track for interrupt 0x13. Not used otherwise. */ + uint16_t sectorsPerTrack; + /** 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. + */ + 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. + */ + 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. + */ + uint8_t driveNumber; + /** used by Windows NT - should be zero for FAT */ + uint8_t reserved1; + /** 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. + */ + 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. + */ + char volumeLabel[11]; + /** + * A field with a value of either FAT, FAT12 or FAT16, + * depending on the disk format. + */ + char fileSystemType[8]; + /** X86 boot code */ + uint8_t bootCode[448]; + /** must be 0X55 */ + uint8_t bootSectorSig0; + /** must be 0XAA */ + uint8_t bootSectorSig1; +} __attribute__((packed)); +/** Type name for FAT Boot Sector */ +typedef struct fat_boot fat_boot_t; +//------------------------------------------------------------------------------ +/** + * \struct fat32_boot + * + * \brief Boot sector for a FAT32 volume. + * + */ +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. + */ + uint8_t jump[3]; + /** + * 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. + */ + 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. + */ + uint8_t sectorsPerCluster; + /** + * 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. + */ + uint8_t fatCount; + /** + * FAT12/FAT16 only. For FAT32 volumes, this field must be set to 0. + */ + uint16_t rootDirEntryCount; + /** + * 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. + */ + uint8_t mediaType; + /** + * 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. */ + uint16_t sectorsPerTrack; + /** 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. + */ + uint32_t hidddenSectors; + /** + * Contains the total number of sectors in the FAT32 volume. + */ + uint32_t totalSectors32; + /** + * 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. + */ + uint16_t fat32Flags; + /** + * 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. + */ + uint32_t fat32RootCluster; + /** + * 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. + */ + uint16_t fat32BackBootBlock; + /** + * 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. + */ + uint8_t driveNumber; + /** used by Windows NT - should be zero for FAT */ + uint8_t reserved1; + /** 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. + */ + 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. + */ + char volumeLabel[11]; + /** + * A text field with a value of FAT32. + */ + char fileSystemType[8]; + /** X86 boot code */ + uint8_t bootCode[420]; + /** must be 0X55 */ + uint8_t bootSectorSig0; + /** must be 0XAA */ + uint8_t bootSectorSig1; +} __attribute__((packed)); +/** Type name for FAT32 Boot Sector */ +typedef struct fat32_boot fat32_boot_t; +//------------------------------------------------------------------------------ +/** Lead signature for a FSINFO sector */ +const uint32_t FSINFO_LEAD_SIG = 0x41615252; +/** Struct signature for a FSINFO sector */ +const uint32_t FSINFO_STRUCT_SIG = 0x61417272; +/** + * \struct fat32_fsinfo + * + * \brief FSINFO sector for a FAT32 volume. + * + */ +struct fat32_fsinfo { + /** must be 0X52, 0X52, 0X61, 0X41 */ + uint32_t leadSignature; + /** must be zero */ + uint8_t reserved1[480]; + /** 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. + */ + 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. + */ + uint32_t nextFree; + /** must be zero */ + uint8_t reserved2[12]; + /** must be 0X00, 0X00, 0X55, 0XAA */ + uint8_t tailSignature[4]; +} __attribute__((packed)); +/** Type name for FAT32 FSINFO Sector */ +typedef struct fat32_fsinfo fat32_fsinfo_t; +//------------------------------------------------------------------------------ +// End Of Chain values for FAT entries +/** FAT12 end of chain value used by Microsoft. */ +const uint16_t FAT12EOC = 0XFFF; +/** Minimum value for FAT12 EOC. Use to test for EOC. */ +const uint16_t FAT12EOC_MIN = 0XFF8; +/** FAT16 end of chain value used by Microsoft. */ +const uint16_t FAT16EOC = 0XFFFF; +/** Minimum value for FAT16 EOC. Use to test for EOC. */ +const uint16_t FAT16EOC_MIN = 0XFFF8; +/** FAT32 end of chain value used by Microsoft. */ +const uint32_t FAT32EOC = 0X0FFFFFFF; +/** Minimum value for FAT32 EOC. Use to test for EOC. */ +const uint32_t FAT32EOC_MIN = 0X0FFFFFF8; +/** Mask a for FAT32 entry. Entries are 28 bits. */ +const uint32_t FAT32MASK = 0X0FFFFFFF; +//------------------------------------------------------------------------------ +/** + * \struct directoryEntry + * \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 + * 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 + * 16-bit word): + * + * 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 + * 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. + */ + 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_. + */ + 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. + */ + 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) + */ + uint8_t creationTimeTenths; + /** Time file was created. */ + uint16_t creationTime; + /** 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. + */ + uint16_t lastAccessDate; + /** + * 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. */ + uint16_t lastWriteTime; + /** Date of last write. File creation is considered a write. */ + uint16_t lastWriteDate; + /** Low word of this entry's first cluster number. */ + uint16_t firstClusterLow; + /** 32-bit unsigned holding this file's size in bytes. */ + uint32_t fileSize; +} __attribute__((packed)); +/** Type name for directoryEntry */ +typedef struct directoryEntry dir_t; +//------------------------------------------------------------------------------ +// Definitions for directory entries +// +/** escape for name[0] = 0XE5 */ +const uint8_t DIR_NAME_0XE5 = 0X05; +/** name[0] value for entry that is free after being "deleted" */ +const uint8_t DIR_NAME_DELETED = 0XE5; +/** name[0] value for entry that is free and no allocated entries follow */ +const uint8_t DIR_NAME_FREE = 0X00; +/** file is read-only */ +const uint8_t DIR_ATT_READ_ONLY = 0X01; +/** File should e hidden in directory listings */ +const uint8_t DIR_ATT_HIDDEN = 0X02; +/** Entry is for a system file */ +const uint8_t DIR_ATT_SYSTEM = 0X04; +/** Directory entry contains the volume label */ +const uint8_t DIR_ATT_VOLUME_ID = 0X08; +/** Entry is for a directory */ +const uint8_t DIR_ATT_DIRECTORY = 0X10; +/** Old DOS archive bit for backup support */ +const uint8_t DIR_ATT_ARCHIVE = 0X20; +/** Test value for long name entry. Test is + (d->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME. */ +const uint8_t DIR_ATT_LONG_NAME = 0X0F; +/** Test mask for long name entry */ +const uint8_t DIR_ATT_LONG_NAME_MASK = 0X3F; +/** defined attribute bits */ +const uint8_t DIR_ATT_DEFINED_BITS = 0X3F; + +/** Mask for file/subdirectory tests */ +const uint8_t 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; +} +/** Directory entry is hidden + * \param[in] dir Pointer to a directory entry. + * + * \return true if the entry is hidden else false. + */ +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. + * + * \return true if the entry is for a subdirectory else false. + */ +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 system type + * \param[in] dir Pointer to a directory entry. + * + * \return true if the entry is system else false. + */ +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] + * \param[in] month [1,12] + * \param[in] day [1,31] + * + * \return Packed date for dir_t entry. + */ +static inline uint16_t FAT_DATE(uint16_t year, uint8_t month, uint8_t day) { + return (year - 1980) << 9 | month << 5 | day; +} +/** year part of FAT directory date field + * \param[in] fatDate Date in packed dir format. + * + * \return Extracted year [1980,2107] + */ +static inline uint16_t FAT_YEAR(uint16_t fatDate) { + return 1980 + (fatDate >> 9); +} +/** month part of FAT directory date field + * \param[in] fatDate Date in packed dir format. + * + * \return Extracted month [1,12] + */ +static inline uint8_t FAT_MONTH(uint16_t fatDate) { + return (fatDate >> 5) & 0XF; +} +/** day part of FAT directory date field + * \param[in] fatDate Date in packed dir format. + * + * \return Extracted day [1,31] + */ +static inline uint8_t FAT_DAY(uint16_t fatDate) { + return fatDate & 0X1F; +} +/** time field for FAT directory entry + * \param[in] hour [0,23] + * \param[in] minute [0,59] + * \param[in] second [0,59] + * + * \return Packed time for dir_t entry. + */ +static inline uint16_t FAT_TIME(uint8_t hour, uint8_t minute, uint8_t second) { + return hour << 11 | minute << 5 | second >> 1; +} +/** hour part of FAT directory time field + * \param[in] fatTime Time in packed dir format. + * + * \return Extracted hour [0,23] + */ +static inline uint8_t FAT_HOUR(uint16_t fatTime) { + return fatTime >> 11; +} +/** minute part of FAT directory time field + * \param[in] fatTime Time in packed dir format. + * + * \return Extracted minute [0,59] + */ +static inline uint8_t FAT_MINUTE(uint16_t fatTime) { + return (fatTime >> 5) & 0X3F; +} +/** second part of FAT directory time field + * \note second/2 is stored in packed time. + * + * \param[in] fatTime Time in packed dir format. + * + * \return Extracted second [0,58] + */ +static inline uint8_t FAT_SECOND(uint16_t fatTime) { + return 2*(fatTime & 0X1F); +} +/** Default date for file timestamps is 1 Jan 2000 */ +const uint16_t FAT_DEFAULT_DATE = ((2000 - 1980) << 9) | (1 << 5) | 1; +/** Default time for file timestamp is 1 am */ +const uint16_t 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 diff --git a/hardware/_controller/src/SdFat/FatLib/FatVolume.cpp b/hardware/_controller/src/SdFat/FatLib/FatVolume.cpp new file mode 100644 index 0000000..772ed73 --- /dev/null +++ b/hardware/_controller/src/SdFat/FatLib/FatVolume.cpp @@ -0,0 +1,625 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include +#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; + bool setStart; + if (m_allocSearchStart < current) { + // Try to keep file contiguous. Start just after current cluster. + find = current; + setStart = false; + } else { + find = m_allocSearchStart; + setStart = true; + } + while (1) { + find++; + if (find > m_lastCluster) { + if (setStart) { + // Can't find space, checked all clusters. + DBG_FAIL_MACRO; + goto fail; + } + find = m_allocSearchStart; + setStart = true; + continue; + } + if (find == current) { + // Can't find space, already searched clusters after current. + DBG_FAIL_MACRO; + goto fail; + } + uint32_t f; + int8_t fg = fatGet(find, &f); + if (fg < 0) { + DBG_FAIL_MACRO; + goto fail; + } + if (fg && f == 0) { + break; + } + } + if (setStart) { + m_allocSearchStart = find; + } + // 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; + } + } + updateFreeClusterCount(-1); + *next = find; + return true; + +fail: + return false; +} +//------------------------------------------------------------------------------ +// find a contiguous group of clusters +bool FatVolume::allocContiguous(uint32_t count, + uint32_t* firstCluster, uint32_t startCluster) { + // flag to save place to start next search + bool setStart; + // start of group + uint32_t bgnCluster; + // end of group + uint32_t endCluster; + if (startCluster != 0) { + bgnCluster = startCluster; + setStart = false; + } else { + // Start at cluster after last allocated cluster. + bgnCluster = m_allocSearchStart + 1; + setStart = true; + } + endCluster = bgnCluster; + // search the FAT for free clusters + while (1) { + if (endCluster > m_lastCluster) { + // Can't find space. + DBG_FAIL_MACRO; + goto fail; + } + uint32_t f; + int8_t fg = fatGet(endCluster, &f); + if (fg < 0) { + DBG_FAIL_MACRO; + goto fail; + } + if (f || fg == 0) { + if (startCluster) { + DBG_FAIL_MACRO; + goto fail; + } + // 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; + } + endCluster++; + } + // Remember possible next free cluster. + if (setStart) { + m_allocSearchStart = endCluster; + } + // 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--; + } + // Maintain count of free clusters. + updateFreeClusterCount(-count); + + // return first cluster number to caller + *firstCluster = bgnCluster; + return true; + +fail: + return false; +} +//------------------------------------------------------------------------------ +uint32_t FatVolume::clusterFirstBlock(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 + if (cluster < 2 || cluster > m_lastCluster) { + DBG_FAIL_MACRO; + goto fail; + } + + if (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 (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 && 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 + if (cluster < 2 || cluster > m_lastCluster) { + DBG_FAIL_MACRO; + goto fail; + } + + if (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 (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 && 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 = 0; + 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; + } + // Add one to count of free clusters. + updateFreeClusterCount(1); + + if (cluster <= m_allocSearchStart) { + m_allocSearchStart = cluster - 1; + } + cluster = next; + } while (fg); + + return true; + +fail: + return false; +} +//------------------------------------------------------------------------------ +int32_t FatVolume::freeClusterCount() { +#if MAINTAIN_FREE_CLUSTER_COUNT + if (m_freeClusterCount >= 0) { + return m_freeClusterCount; + } +#endif // MAINTAIN_FREE_CLUSTER_COUNT + uint32_t free = 0; + uint32_t lba; + uint32_t todo = m_lastCluster + 1; + uint16_t n; + + if (FAT12_SUPPORT && 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 (fatType() == 16 || fatType() == 32) { + lba = m_fatStartBlock; + while (todo) { + cache_t* pc = cacheFetchFat(lba++, FatCache::CACHE_FOR_READ); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + n = fatType() == 16 ? 256 : 128; + if (todo < n) { + n = todo; + } + if (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; + } + setFreeClusterCount(free); + 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; + + // Indicate unknown number of free clusters. + setFreeClusterCount(-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 (!fatType()) { + DBG_FAIL_MACRO; + goto fail; + } + cache = cacheClear(); + if (!cache) { + DBG_FAIL_MACRO; + goto fail; + } + memset(cache->data, 0, 512); + // Zero root. + if (fatType() == 32) { + lbn = clusterFirstBlock(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 (fatType() == 32) { + cache->fat32[0] = 0x0FFFFFF8; + cache->fat32[1] = 0x0FFFFFFF; + } else if (fatType() == 16) { + cache->fat16[0] = 0XFFF8; + cache->fat16[1] = 0XFFFF; + } else if (FAT12_SUPPORT && 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 (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; +} diff --git a/hardware/_controller/src/SdFat/FatLib/FatVolume.h b/hardware/_controller/src/SdFat/FatLib/FatVolume.h new file mode 100644 index 0000000..11f3d87 --- /dev/null +++ b/hardware/_controller/src/SdFat/FatLib/FatVolume.h @@ -0,0 +1,396 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef FatVolume_h +#define FatVolume_h +/** + * \file + * \brief FatVolume class + */ +#include +#include "FatLibConfig.h" +#include "FatStructs.h" +#include "../BlockDriver.h" +//------------------------------------------------------------------------------ +#ifndef DOXYGEN_SHOULD_SKIP_THIS +/** Macro for debug. */ +#define DEBUG_MODE 0 +#if DEBUG_MODE +#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 +/** Use Print for Arduino */ +typedef Print print_t; +#else // ENABLE_ARDUINO_FEATURES +/** + * \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 const uint8_t CACHE_FOR_READ = 0; + /** Cache block for write. */ + static const uint8_t CACHE_FOR_WRITE = CACHE_STATUS_DIRTY; + /** Reserve cache block for write - do not read from block device. */ + static const uint8_t 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 dirty status */ + bool isDirty() { + return m_status & CACHE_STATUS_DIRTY; + } + /** \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 sector number for the start of file data. */ + uint32_t dataStartSector() 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 sector number for the start of the first FAT. */ + uint32_t fatStartSector() 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 cluster number of last cluster in the volume. */ + uint32_t lastCluster() const { + return m_lastCluster; + } + /** \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 volume's cluster size in sectors. */ + uint8_t sectorsPerCluster() const { + return m_blocksPerCluster; + } + /** \return The number of blocks in the volume */ + uint32_t volumeBlockCount() const { + return blocksPerCluster()*clusterCount(); + } + /** \return The number of sectors in the volume */ + uint32_t volumeSectorCount() const { + return sectorsPerCluster()*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 -1 error, 0 EOC, else 1. + */ + 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; ///< Allow access to FatVolume. + friend class FatFile; ///< Allow access to FatVolume. + friend class FatFileSystem; ///< Allow access to FatVolume. +//------------------------------------------------------------------------------ + BlockDriver* m_blockDev; // block device + 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 I/O functions. + bool readBlock(uint32_t block, uint8_t* dst) { + return m_blockDev->readBlock(block, dst); + } + bool syncBlocks() { + return m_blockDev->syncBlocks(); + } + bool writeBlock(uint32_t block, const uint8_t* src) { + return m_blockDev->writeBlock(block, src); + } +#if USE_MULTI_BLOCK_IO + bool readBlocks(uint32_t block, uint8_t* dst, size_t nb) { + return m_blockDev->readBlocks(block, dst, nb); + } + bool writeBlocks(uint32_t block, const uint8_t* src, size_t nb) { + return m_blockDev->writeBlocks(block, src, nb); + } +#endif // USE_MULTI_BLOCK_IO +#if MAINTAIN_FREE_CLUSTER_COUNT + int32_t m_freeClusterCount; // Count of free clusters in volume. + void setFreeClusterCount(int32_t value) { + m_freeClusterCount = value; + } + void updateFreeClusterCount(int32_t change) { + if (m_freeClusterCount >= 0) { + m_freeClusterCount += change; + } + } +#else // MAINTAIN_FREE_CLUSTER_COUNT + void setFreeClusterCount(int32_t value) { + (void)value; + } + void updateFreeClusterCount(int32_t change) { + (void)change; + } +#endif // MAINTAIN_FREE_CLUSTER_COUNT + +// 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() && syncBlocks(); + } +#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() && syncBlocks(); + } +#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, uint32_t startCluster = 0); + uint8_t blockOfCluster(uint32_t position) const { + return (position >> 9) & m_clusterBlockMask; + } + uint32_t clusterFirstBlock(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; + } +}; +#endif // FatVolume diff --git a/hardware/_controller/src/SdFat/FatLib/FmtNumber.cpp b/hardware/_controller/src/SdFat/FatLib/FmtNumber.cpp new file mode 100644 index 0000000..2953145 --- /dev/null +++ b/hardware/_controller/src/SdFat/FatLib/FmtNumber.cpp @@ -0,0 +1,460 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "FmtNumber.h" +// Use Stimmer div/mod 10 on avr +#ifdef __AVR__ +#include +#define USE_STIMMER +#endif // __AVR__ +//------------------------------------------------------------------------------ +// Stimmer div/mod 10 for AVR +// this code fragment works out i/10 and i%10 by calculating +// i*(51/256)*(256/255)/2 == i*51/510 == i/10 +// by "j.k" I mean 32.8 fixed point, j is integer part, k is fractional part +// j.k = ((j+1.0)*51.0)/256.0 +// (we add 1 because we will be using the floor of the result later) +// divmod10_asm16 and divmod10_asm32 are public domain code by Stimmer. +// http://forum.arduino.cc/index.php?topic=167414.msg1293679#msg1293679 +#define divmod10_asm16(in32, mod8, tmp8) \ +asm volatile( \ + " ldi %2,51 \n\t" \ + " mul %A0,%2 \n\t" \ + " clr %A0 \n\t" \ + " add r0,%2 \n\t" \ + " adc %A0,r1 \n\t" \ + " mov %1,r0 \n\t" \ + " mul %B0,%2 \n\t" \ + " clr %B0 \n\t" \ + " add %A0,r0 \n\t" \ + " adc %B0,r1 \n\t" \ + " clr r1 \n\t" \ + " add %1,%A0 \n\t" \ + " adc %A0,%B0 \n\t" \ + " adc %B0,r1 \n\t" \ + " add %1,%B0 \n\t" \ + " adc %A0,r1 \n\t" \ + " adc %B0,r1 \n\t" \ + " lsr %B0 \n\t" \ + " ror %A0 \n\t" \ + " ror %1 \n\t" \ + " ldi %2,10 \n\t" \ + " mul %1,%2 \n\t" \ + " mov %1,r1 \n\t" \ + " clr r1 \n\t" \ + :"+r"(in32), "=d"(mod8), "=d"(tmp8) : : "r0") + +#define divmod10_asm32(in32, mod8, tmp8) \ +asm volatile( \ + " ldi %2,51 \n\t" \ + " mul %A0,%2 \n\t" \ + " clr %A0 \n\t" \ + " add r0,%2 \n\t" \ + " adc %A0,r1 \n\t" \ + " mov %1,r0 \n\t" \ + " mul %B0,%2 \n\t" \ + " clr %B0 \n\t" \ + " add %A0,r0 \n\t" \ + " adc %B0,r1 \n\t" \ + " mul %C0,%2 \n\t" \ + " clr %C0 \n\t" \ + " add %B0,r0 \n\t" \ + " adc %C0,r1 \n\t" \ + " mul %D0,%2 \n\t" \ + " clr %D0 \n\t" \ + " add %C0,r0 \n\t" \ + " adc %D0,r1 \n\t" \ + " clr r1 \n\t" \ + " add %1,%A0 \n\t" \ + " adc %A0,%B0 \n\t" \ + " adc %B0,%C0 \n\t" \ + " adc %C0,%D0 \n\t" \ + " adc %D0,r1 \n\t" \ + " add %1,%B0 \n\t" \ + " adc %A0,%C0 \n\t" \ + " adc %B0,%D0 \n\t" \ + " adc %C0,r1 \n\t" \ + " adc %D0,r1 \n\t" \ + " add %1,%D0 \n\t" \ + " adc %A0,r1 \n\t" \ + " adc %B0,r1 \n\t" \ + " adc %C0,r1 \n\t" \ + " adc %D0,r1 \n\t" \ + " lsr %D0 \n\t" \ + " ror %C0 \n\t" \ + " ror %B0 \n\t" \ + " ror %A0 \n\t" \ + " ror %1 \n\t" \ + " ldi %2,10 \n\t" \ + " mul %1,%2 \n\t" \ + " mov %1,r1 \n\t" \ + " clr r1 \n\t" \ + :"+r"(in32), "=d"(mod8), "=d"(tmp8) : : "r0") +//------------------------------------------------------------------------------ +/* +// C++ code is based on this version of divmod10 by robtillaart. +// http://forum.arduino.cc/index.php?topic=167414.msg1246851#msg1246851 +// from robtillaart post: +// The code is based upon the divu10() code from the book Hackers Delight1. +// My insight was that the error formula in divu10() was in fact modulo 10 +// but not always. Sometimes it was 10 more. +void divmod10(uint32_t in, uint32_t &div, uint32_t &mod) +{ + // q = in * 0.8; + uint32_t q = (in >> 1) + (in >> 2); + q = q + (q >> 4); + q = q + (q >> 8); + q = q + (q >> 16); // not needed for 16 bit version + + // q = q / 8; ==> q = in *0.1; + q = q >> 3; + + // determine error + uint32_t r = in - ((q << 3) + (q << 1)); // r = in - q*10; + div = q + (r > 9); + if (r > 9) mod = r - 10; + else mod = r; +} +// Hackers delight function is here: +// http://www.hackersdelight.org/hdcodetxt/divuc.c.txt +// Code below uses 8/10 = 0.1100 1100 1100 1100 1100 1100 1100 1100. +// 15 ops including the multiply, or 17 elementary ops. +unsigned divu10(unsigned n) { + unsigned q, r; + + q = (n >> 1) + (n >> 2); + q = q + (q >> 4); + q = q + (q >> 8); + q = q + (q >> 16); + q = q >> 3; + r = n - q*10; + return q + ((r + 6) >> 4); +// return q + (r > 9); +} +*/ +//------------------------------------------------------------------------------ +#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; + if (n < 0) { + n = -n; + s = m; + } else { + s = p; + } + n &= 63; + for (uint8_t i = 0; n; n >>= 1, i++) { +#ifdef __AVR__ + if (n & 1) { + v *= pgm_read_float(&s[i]); + } +#else // __AVR__ + if (n & 1) { + v *= s[i]; + } +#endif // __AVR__ + } + return v; +} +//------------------------------------------------------------------------------ +// Format 16-bit unsigned +char* fmtDec(uint16_t n, char* p) { + while (n > 9) { +#ifdef USE_STIMMER + uint8_t tmp8, r; + divmod10_asm16(n, r, tmp8); +#else // USE_STIMMER + uint16_t t = n; + n = (n >> 1) + (n >> 2); + n = n + (n >> 4); + n = n + (n >> 8); + // n = n + (n >> 16); // no code for 16-bit n + n = n >> 3; + uint8_t r = t - (((n << 2) + n) << 1); + if (r > 9) { + n++; + r -= 10; + } +#endif // USE_STIMMER + *--p = r + '0'; + } + *--p = n + '0'; + return p; +} +//------------------------------------------------------------------------------ +// format 32-bit unsigned +char* fmtDec(uint32_t n, char* p) { + while (n >> 16) { +#ifdef USE_STIMMER + uint8_t tmp8, r; + divmod10_asm32(n, r, tmp8); +#else // USE_STIMMER + uint32_t t = n; + n = (n >> 1) + (n >> 2); + n = n + (n >> 4); + n = n + (n >> 8); + n = n + (n >> 16); + n = n >> 3; + uint8_t r = t - (((n << 2) + n) << 1); + if (r > 9) { + n++; + r -= 10; + } +#endif // USE_STIMMER + *--p = r + '0'; + } + return fmtDec((uint16_t)n, p); +} +//------------------------------------------------------------------------------ +char* fmtFloat(float value, char* p, uint8_t prec) { + char sign = value < 0 ? '-' : 0; + if (sign) { + value = -value; + } + + if (isnan(value)) { + *--p = 'n'; + *--p = 'a'; + *--p = 'n'; + return p; + } + if (isinf(value)) { + *--p = 'f'; + *--p = 'n'; + *--p = 'i'; + return p; + } + if (value > 4294967040.0) { + *--p = 'f'; + *--p = 'v'; + *--p = 'o'; + return p; + } + if (prec > 9) { + prec = 9; + } + value += scale10(0.5, -prec); + + uint32_t whole = value; + if (prec) { + char* tmp = p - prec; + uint32_t fraction = scale10(value - whole, prec); + p = fmtDec(fraction, p); + while (p > tmp) { + *--p = '0'; + } + *--p = '.'; + } + p = fmtDec(whole, p); + if (sign) { + *--p = sign; + } + return p; +} +//------------------------------------------------------------------------------ +/** Print a number followed by a field terminator. + * \param[in] value The number to be printed. + * \param[in] ptr Pointer to last char in buffer. + * \param[in] prec Number of digits after decimal point. + * \param[in] expChar Use exp format if non zero. + * \return Pointer to first character of result. + */ +char* fmtFloat(float value, char* ptr, uint8_t prec, char expChar) { + bool neg = value < 0; + if (neg) { + value = -value; + } + + // check for nan inf ovf + if (isnan(value)) { + *--ptr = 'n'; + *--ptr = 'a'; + *--ptr = 'n'; + return ptr; + } + if (isinf(value)) { + *--ptr = 'f'; + *--ptr = 'n'; + *--ptr = 'i'; + return ptr; + } + if (!expChar && value > 4294967040.0) { + *--ptr = 'f'; + *--ptr = 'v'; + *--ptr = 'o'; + return ptr; + } + if (prec > 9) { + prec = 9; + } + float round = scale10(0.5, -prec); + if (expChar) { + int8_t exp = 0; + bool expNeg = false; + if (value) { + while (value > 10.0) { + value *= 0.1; + exp++; + } + while (value < 1.0) { + value *= 10.0; + exp--; + } + value += round; + if (value > 10.0) { + value *= 0.1; + exp++; + } + expNeg = exp < 0; + if (expNeg) { + exp = -exp; + } + } + ptr = fmtDec((uint16_t)exp, ptr); + if (exp < 10) { + *--ptr = '0'; + } + *--ptr = expNeg ? '-' : '+'; + *--ptr = expChar; + } else { + // round value + value += round; + } + uint32_t whole = value; + if (prec) { + char* tmp = ptr - prec; + uint32_t fraction = scale10(value - whole, prec); + ptr = fmtDec(fraction, ptr); + while (ptr > tmp) { + *--ptr = '0'; + } + *--ptr = '.'; + } + ptr = fmtDec(whole, ptr); + if (neg) { + *--ptr = '-'; + } + return ptr; +} +//------------------------------------------------------------------------------ +char* fmtHex(uint32_t n, char* p) { + do { + uint8_t h = n & 0XF; + *--p = h + (h < 10 ? '0' : 'A' - 10); + n >>= 4; + } while (n); + return p; +} +//------------------------------------------------------------------------------ +float scanFloat(const char* str, char** ptr) { + int16_t const EXP_LIMIT = 100; + bool digit = false; + bool dot = false; + uint32_t fract = 0; + int fracExp = 0; + uint8_t nd = 0; + bool neg; + int c; + float v; + const char* successPtr = str; + + if (ptr) { + *ptr = const_cast(str); + } + + while (isSpace((c = *str++))) {} + neg = c == '-'; + if (c == '-' || c == '+') { + c = *str++; + } + // Skip leading zeros + while (c == '0') { + c = *str++; + digit = true; + } + for (;;) { + if (isDigit(c)) { + digit = true; + if (nd < 9) { + fract = 10*fract + c - '0'; + nd++; + if (dot) { + fracExp--; + } + } else { + if (!dot) { + fracExp++; + } + } + } else if (c == '.') { + if (dot) { + goto fail; + } + dot = true; + } else { + if (!digit) { + goto fail; + } + break; + } + successPtr = str; + c = *str++; + } + if (c == 'e' || c == 'E') { + int exp = 0; + c = *str++; + bool expNeg = c == '-'; + if (c == '-' || c == '+') { + c = *str++; + } + 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(successPtr); + } + v = scale10(static_cast(fract), fracExp); + return neg ? -v : v; + +fail: + return 0; +} + + diff --git a/hardware/_controller/src/SdFat/FatLib/FmtNumber.h b/hardware/_controller/src/SdFat/FatLib/FmtNumber.h new file mode 100644 index 0000000..7834c8f --- /dev/null +++ b/hardware/_controller/src/SdFat/FatLib/FmtNumber.h @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef FmtNumber_h +#define FmtNumber_h +// #include +inline bool isDigit(char c) { + return '0' <= c && c <= '9'; +} +inline bool isSpace(char c) { + return c == ' ' || (0X9 <= c && c <= 0XD); +} +#include +#include +char* fmtDec(uint16_t n, char* p); +char* fmtDec(uint32_t n, char* p); +char* fmtFloat(float value, char* p, uint8_t prec); +char* fmtFloat(float value, char* ptr, uint8_t prec, char expChar); +char* fmtHex(uint32_t n, char* p); +float scale10(float v, int8_t n); +float scanFloat(const char* str, char** ptr); +#endif // FmtNumber_h diff --git a/hardware/_controller/src/SdFat/FatLib/StdioStream.cpp b/hardware/_controller/src/SdFat/FatLib/StdioStream.cpp new file mode 100644 index 0000000..076c2b2 --- /dev/null +++ b/hardware/_controller/src/SdFat/FatLib/StdioStream.cpp @@ -0,0 +1,508 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "StdioStream.h" +#include "FmtNumber.h" +//------------------------------------------------------------------------------ +int StdioStream::fclose() { + int rtn = 0; + if (!m_status) { + return EOF; + } + if (m_status & S_SWR) { + if (!flushBuf()) { + rtn = EOF; + } + } + if (!FatFile::close()) { + rtn = EOF; + } + m_r = 0; + m_w = 0; + m_status = 0; + return rtn; +} +//------------------------------------------------------------------------------ +int StdioStream::fflush() { + if ((m_status & (S_SWR | S_SRW)) && !(m_status & S_SRD)) { + if (flushBuf() && FatFile::sync()) { + return 0; + } + } + return EOF; +} +//------------------------------------------------------------------------------ +char* StdioStream::fgets(char* str, size_t num, size_t* len) { + char* s = str; + size_t n; + if (num-- == 0) { + return 0; + } + while (num) { + if ((n = m_r) == 0) { + if (!fillBuf()) { + if (s == str) { + return 0; + } + break; + } + n = m_r; + } + if (n > num) { + n = num; + } + uint8_t* end = reinterpret_cast(memchr(m_p, '\n', n)); + if (end != 0) { + n = ++end - m_p; + memcpy(s, m_p, n); + m_r -= n; + m_p = end; + s += n; + break; + } + memcpy(s, m_p, n); + m_r -= n; + m_p += n; + s += n; + num -= n; + } + *s = 0; + if (len) { + *len = s - str; + } + return str; +} +//------------------------------------------------------------------------------ +bool StdioStream::fopen(const char* path, const char* mode) { + oflag_t oflag; + uint8_t m; + switch (*mode++) { + case 'a': + m = O_WRONLY; + oflag = O_CREAT | O_APPEND; + m_status = S_SWR; + break; + + case 'r': + m = O_RDONLY; + oflag = 0; + m_status = S_SRD; + break; + + case 'w': + m = O_WRONLY; + oflag = O_CREAT | O_TRUNC; + m_status = S_SWR; + break; + + default: + goto fail; + } + while (*mode) { + switch (*mode++) { + case '+': + m_status = S_SRW; + m = O_RDWR; + break; + + case 'b': + break; + + case 'x': + oflag |= O_EXCL; + break; + + default: + goto fail; + } + } + oflag |= m; + + if (!FatFile::open(path, oflag)) { + goto fail; + } + m_r = 0; + m_w = 0; + m_p = m_buf; + return true; + +fail: + m_status = 0; + return false; +} +//------------------------------------------------------------------------------ +int StdioStream::fputs(const char* str) { + size_t len = strlen(str); + return fwrite(str, 1, len) == len ? len : EOF; +} +//------------------------------------------------------------------------------ +size_t StdioStream::fread(void* ptr, size_t size, size_t count) { + uint8_t* dst = reinterpret_cast(ptr); + size_t total = size*count; + if (total == 0) { + return 0; + } + size_t need = total; + while (need > m_r) { + memcpy(dst, m_p, m_r); + dst += m_r; + m_p += m_r; + need -= m_r; + if (!fillBuf()) { + return (total - need)/size; + } + } + memcpy(dst, m_p, need); + m_r -= need; + m_p += need; + return count; +} +//------------------------------------------------------------------------------ +int StdioStream::fseek(int32_t offset, int origin) { + int32_t pos; + if (m_status & S_SWR) { + if (!flushBuf()) { + goto fail; + } + } + switch (origin) { + case SEEK_CUR: + pos = ftell(); + if (pos < 0) { + goto fail; + } + pos += offset; + if (!FatFile::seekCur(pos)) { + goto fail; + } + break; + + case SEEK_SET: + if (!FatFile::seekSet(offset)) { + goto fail; + } + break; + + case SEEK_END: + if (!FatFile::seekEnd(offset)) { + goto fail; + } + break; + + default: + goto fail; + } + m_r = 0; + m_p = m_buf; + return 0; + +fail: + return EOF; +} +//------------------------------------------------------------------------------ +int32_t StdioStream::ftell() { + uint32_t pos = FatFile::curPosition(); + if (m_status & S_SRD) { + if (m_r > pos) { + return -1L; + } + pos -= m_r; + } else if (m_status & S_SWR) { + pos += m_p - m_buf; + } + return pos; +} +//------------------------------------------------------------------------------ +size_t StdioStream::fwrite(const void* ptr, size_t size, size_t count) { + return write(ptr, count*size) < 0 ? EOF : count; +} +//------------------------------------------------------------------------------ +int StdioStream::write(const void* buf, size_t count) { + const uint8_t* src = static_cast(buf); + size_t todo = count; + + while (todo > m_w) { + memcpy(m_p, src, m_w); + m_p += m_w; + src += m_w; + todo -= m_w; + if (!flushBuf()) { + return EOF; + } + } + memcpy(m_p, src, todo); + m_p += todo; + m_w -= todo; + return count; +} +//------------------------------------------------------------------------------ +#if (defined(ARDUINO) && ENABLE_ARDUINO_FEATURES) || defined(DOXYGEN) +size_t StdioStream::print(const __FlashStringHelper *str) { + const char *p = (const char*)str; + uint8_t c; + while ((c = pgm_read_byte(p))) { + if (putc(c) < 0) { + return 0; + } + p++; + } + return p - (const char*)str; +} +#endif // (defined(ARDUINO) && ENABLE_ARDUINO_FEATURES) || defined(DOXYGEN) +//------------------------------------------------------------------------------ +int StdioStream::printDec(float value, uint8_t prec) { + char buf[24]; + char *ptr = fmtFloat(value, buf + sizeof(buf), prec); + return write(ptr, buf + sizeof(buf) - ptr); +} +//------------------------------------------------------------------------------ +int StdioStream::printDec(signed char n) { + if (n < 0) { + if (fputc('-') < 0) { + return -1; + } + n = -n; + } + return printDec((unsigned char)n); +} +//------------------------------------------------------------------------------ +int StdioStream::printDec(int16_t n) { + int s; + uint8_t rtn = 0; + if (n < 0) { + if (fputc('-') < 0) { + return -1; + } + n = -n; + rtn++; + } + if ((s = printDec((uint16_t)n)) < 0) { + return s; + } + return rtn; +} +//------------------------------------------------------------------------------ +int StdioStream::printDec(uint16_t n) { +#define NEW_WAY +#ifdef NEW_WAY + char buf[5]; + char *ptr = fmtDec(n, buf + sizeof(buf)); + uint8_t len = buf + sizeof(buf) - ptr; + return write(ptr, len); +#else + uint8_t len; + if (n < 100) { + len = n < 10 ? 1 : 2; + } else { + len = n < 1000 ? 3 : n < 10000 ? 4 : 5; + } + char* str = fmtSpace(len); + if (!str) { + return -1; + } + fmtDec(n, str); + return len; +#endif +} +//------------------------------------------------------------------------------ +int StdioStream::printDec(int32_t n) { + uint8_t s = 0; + if (n < 0) { + if (fputc('-') < 0) { + return -1; + } + n = -n; + s = 1; + } + int rtn = printDec((uint32_t)n); + return rtn > 0 ? rtn + s : -1; +} +//------------------------------------------------------------------------------ +int StdioStream::printDec(uint32_t n) { +#ifdef NEW_WAY + char buf[10]; + char *ptr = fmtDec(n, buf + sizeof(buf)); + uint8_t len = buf + sizeof(buf) - ptr; + return write(ptr, len); +#else + uint8_t len; + if (n < 0X10000) { + return printDec((uint16_t)n); + } + if (n < 10000000) { + len = n < 100000 ? 5 : n < 1000000 ? 6 : 7; + } else { + len = n < 100000000 ? 8 : n < 1000000000 ? 9 : 10; + } + + char* str = fmtSpace(len); + if (!str) { + return -1; + } + fmtDec(n, str); + return len; +#endif +} +//------------------------------------------------------------------------------ +int StdioStream::printHex(uint32_t n) { +#ifdef NEW_WAY + char buf[8]; + char *ptr = fmtHex(n, buf + sizeof(buf)); + uint8_t len = buf + sizeof(buf) - ptr; + return write(ptr, len); +#else + size_t len; + if (n < 0X10000) { + len = n < 0X10 ? 1 : n < 0X100 ? 2 : n < 0X1000 ? 3 : 4; + } else { + len = n < 0X100000 ? 5 : n < 0X1000000 ? 6 : n < 0X10000000 ? 7 : 8; + } + char* str = fmtSpace(len); + if (!str) { + return -1; + } + + do { + uint8_t h = n & 0XF; + *str-- = h + (h < 10 ? '0' : 'A' - 10); + n >>= 4; + } while (n); + return len; +#endif +} +//------------------------------------------------------------------------------ +bool StdioStream::rewind() { + if (m_status & S_SWR) { + if (!flushBuf()) { + return false; + } + } + FatFile::seekSet(0); + m_r = 0; + return true; +} +//------------------------------------------------------------------------------ +int StdioStream::ungetc(int c) { + // error if EOF. + if (c == EOF) { + return EOF; + } + // error if not reading. + if ((m_status & S_SRD) == 0) { + return EOF; + } + // error if no space. + if (m_p == m_buf) { + return EOF; + } + m_r++; + m_status &= ~S_EOF; + return *--m_p = (uint8_t)c; +} +//============================================================================== +// private +//------------------------------------------------------------------------------ +int StdioStream::fillGet() { + if (!fillBuf()) { + return EOF; + } + m_r--; + return *m_p++; +} +//------------------------------------------------------------------------------ +// private +bool StdioStream::fillBuf() { + if (!(m_status & + S_SRD)) { // check for S_ERR and S_EOF ??///////////////// + if (!(m_status & S_SRW)) { + m_status |= S_ERR; + return false; + } + if (m_status & S_SWR) { + if (!flushBuf()) { + return false; + } + m_status &= ~S_SWR; + m_status |= S_SRD; + m_w = 0; + } + } + m_p = m_buf + UNGETC_BUF_SIZE; + int nr = FatFile::read(m_p, sizeof(m_buf) - UNGETC_BUF_SIZE); + if (nr <= 0) { + m_status |= nr < 0 ? S_ERR : S_EOF; + m_r = 0; + return false; + } + m_r = nr; + return true; +} +//------------------------------------------------------------------------------ +// private +bool StdioStream::flushBuf() { + if (!(m_status & + S_SWR)) { // check for S_ERR ??//////////////////////// + if (!(m_status & S_SRW)) { + m_status |= S_ERR; + return false; + } + m_status &= ~S_SRD; + m_status |= S_SWR; + m_r = 0; + m_w = sizeof(m_buf); + m_p = m_buf; + return true; + } + uint8_t n = m_p - m_buf; + m_p = m_buf; + m_w = sizeof(m_buf); + if (FatFile::write(m_buf, n) == n) { + return true; + } + m_status |= S_ERR; + return false; +} +//------------------------------------------------------------------------------ +int StdioStream::flushPut(uint8_t c) { + if (!flushBuf()) { + return EOF; + } + m_w--; + return *m_p++ = c; +} +//------------------------------------------------------------------------------ +char* StdioStream::fmtSpace(uint8_t len) { + if (m_w < len) { + if (!flushBuf() || m_w < len) { + return 0; + } + } + if (len > m_w) { + return 0; + } + m_p += len; + m_w -= len; + return reinterpret_cast(m_p); +} + diff --git a/hardware/_controller/src/SdFat/FatLib/StdioStream.h b/hardware/_controller/src/SdFat/FatLib/StdioStream.h new file mode 100644 index 0000000..a4d5e9a --- /dev/null +++ b/hardware/_controller/src/SdFat/FatLib/StdioStream.h @@ -0,0 +1,667 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef StdioStream_h +#define StdioStream_h +/** + * \file + * \brief StdioStream class + */ +#include +#include "FatFile.h" +//------------------------------------------------------------------------------ +/** Total size of stream buffer. The entire buffer is used for output. + * During input UNGETC_BUF_SIZE of this space is reserved for ungetc. + */ +const uint8_t STREAM_BUF_SIZE = 64; +/** Amount of buffer allocated for ungetc during input. */ +const uint8_t UNGETC_BUF_SIZE = 2; +//------------------------------------------------------------------------------ +// Get rid of any macros defined in . +#include +#undef clearerr +#undef fclose +#undef feof +#undef ferror +#undef fflush +#undef fgetc +#undef fgetpos +#undef fgets +#undef fopen +#undef fprintf +#undef fputc +#undef fputs +#undef fread +#undef freopen +#undef fscanf +#undef fseek +#undef fsetpos +#undef ftell +#undef fwrite +#undef getc +#undef getchar +#undef gets +#undef perror +//#undef printf // NOLINT +#undef putc +#undef putchar +#undef puts +#undef remove +#undef rename +#undef rewind +#undef scanf +#undef setbuf +#undef setvbuf +//#undef sprintf // NOLINT +#undef sscanf +#undef tmpfile +#undef tmpnam +#undef ungetc +#undef vfprintf +#undef vprintf +#undef vsprintf + +// make sure needed macros are defined +#ifndef EOF +/** End-of-file return value. */ +#define EOF (-1) +#endif // EOF +#ifndef NULL +/** Null pointer */ +#define NULL 0 +#endif // NULL +#ifndef SEEK_CUR +/** Seek relative to current position. */ +#define SEEK_CUR 1 +#endif // SEEK_CUR +#ifndef SEEK_END +/** Seek relative to end-of-file. */ +#define SEEK_END 2 +#endif // SEEK_END +#ifndef SEEK_SET +/** Seek relative to start-of-file. */ +#define SEEK_SET 0 +#endif // SEEK_SET +//------------------------------------------------------------------------------ +/** \class StdioStream + * \brief StdioStream implements a minimal stdio stream. + * + * StdioStream does not support subdirectories or long file names. + */ +class StdioStream : private FatFile { + public: + /** Constructor + * + */ + StdioStream() { + m_w = m_r = 0; + m_p = m_buf; + m_status = 0; + } + //---------------------------------------------------------------------------- + /** Clear the stream's end-of-file and error indicators. */ + void clearerr() { + m_status &= ~(S_ERR | S_EOF); + } + //---------------------------------------------------------------------------- + /** Close a stream. + * + * A successful call to the fclose function causes the stream to be + * flushed and the associated file to be closed. Any unwritten buffered + * data is written to the file; any unread buffered data is discarded. + * Whether or not the call succeeds, the stream is disassociated from + * the file. + * + * \return zero if the stream was successfully closed, or EOF if any any + * errors are detected. + */ + int fclose(); + //---------------------------------------------------------------------------- + /** Test the stream's end-of-file indicator. + * \return non-zero if and only if the end-of-file indicator is set. + */ + int feof() { + return (m_status & S_EOF) != 0; + } + //---------------------------------------------------------------------------- + /** Test the stream's error indicator. + * \return return non-zero if and only if the error indicator is set. + */ + int ferror() { + return (m_status & S_ERR) != 0; + } + //---------------------------------------------------------------------------- + /** Flush the stream. + * + * If stream is an output stream or an update stream in which the most + * recent operation was not input, any unwritten data is written to the + * file; otherwise the call is an error since any buffered input data + * would be lost. + * + * \return sets the error indicator for the stream and returns EOF if an + * error occurs, otherwise it returns zero. + */ + int fflush(); + //---------------------------------------------------------------------------- + /** Get a byte from the stream. + * + * \return If the end-of-file indicator for the stream is set, or if the + * stream is at end-of-file, the end-of-file indicator for the stream is + * set and the fgetc function returns EOF. Otherwise, the fgetc function + * returns the next character from the input stream. + */ + int fgetc() { + return m_r-- == 0 ? fillGet() : *m_p++; + } + //---------------------------------------------------------------------------- + /** Get a string from a stream. + * + * The fgets function reads at most one less than the number of + * characters specified by num from the stream into the array pointed + * to by str. No additional characters are read after a new-line + * character (which is retained) or after end-of-file. A null character + * is written immediately after the last character read into the array. + * + * \param[out] str Pointer to an array of where the string is copied. + * + * \param[in] num Maximum number of characters including the null + * character. + * + * \param[out] len If len is not null and fgets is successful, the + * length of the string is returned. + * + * \return str if successful. If end-of-file is encountered and no + * characters have been read into the array, the contents of the array + * remain unchanged and a null pointer is returned. If a read error + * occurs during the operation, the array contents are indeterminate + * and a null pointer is returned. + */ + char* fgets(char* str, size_t num, size_t* len = 0); + //---------------------------------------------------------------------------- + /** Open a stream. + * + * Open a file and associates the stream with it. + * + * \param[in] path file to be opened. + * + * \param[in] mode a string that indicates the open mode. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
"r" or "rb"Open a file for reading. The file must exist.
"w" or "wb"Truncate an existing to zero length or create an empty file + * for writing.
"wx" or "wbx"Create a file for writing. Fails if the file already exists.
"a" or "ab"Append; open or create file for writing at end-of-file.
"r+" or "rb+" or "r+b"Open a file for update (reading and writing).
"w+" or "w+b" or "wb+"Truncate an existing to zero length or create a file for update.
"w+x" or "w+bx" or "wb+x"Create a file for update. Fails if the file already exists.
"a+" or "a+b" or "ab+"Append; open or create a file for update, writing at end-of-file.
+ * The character 'b' shall have no effect, but is allowed for ISO C + * standard conformance. + * + * Opening a file with append mode causes all subsequent writes to the + * file to be forced to the then current end-of-file, regardless of + * intervening calls to the fseek function. + * + * When a file is opened with update mode, both input and output may be + * performed on the associated stream. However, output shall not be + * directly followed by input without an intervening call to the fflush + * function or to a file positioning function (fseek, or rewind), and + * input shall not be directly followed by output without an intervening + * call to a file positioning function, unless the input operation + * encounters end-of-file. + * + * \return true for success or false for failure. + */ + bool fopen(const char* path, const char * mode); + //---------------------------------------------------------------------------- + /** Write a byte to a stream. + * + * \param[in] c the byte to be written (converted to an unsigned char). + * + * \return Upon successful completion, fputc() returns the value it + * has written. Otherwise, it returns EOF and sets the error indicator for + * the stream. + */ + int fputc(int c) { + return m_w-- == 0 ? flushPut(c) : *m_p++ = c; + } + //---------------------------------------------------------------------------- + /** Write a string to a stream. + * + * \param[in] str a pointer to the string to be written. + * + * \return for success, fputs() returns a non-negative + * number. Otherwise, it returns EOF and sets the error indicator for + * the stream. + */ + int fputs(const char* str); + //---------------------------------------------------------------------------- + /** Binary input. + * + * Reads an array of up to count elements, each one with a size of size + * bytes. + * \param[out] ptr pointer to area of at least (size*count) bytes where + * the data will be stored. + * + * \param[in] size the size, in bytes, of each element to be read. + * + * \param[in] count the number of elements to be read. + * + * \return number of elements successfully read, which may be less than + * count if a read error or end-of-file is encountered. If size or count + * is zero, fread returns zero and the contents of the array and the + * state of the stream remain unchanged. + */ + size_t fread(void* ptr, size_t size, size_t count); + //---------------------------------------------------------------------------- + /** Set the file position for the stream. + * + * \param[in] offset number of offset from the origin. + * + * \param[in] origin position used as reference for the offset. It is + * specified by one of the following constants. + * + * SEEK_SET - Beginning of file. + * + * SEEK_CUR - Current position of the file pointer. + * + * SEEK_END - End of file. + * + * \return zero for success. Otherwise, it returns non-zero and sets the + * error indicator for the stream. + */ + int fseek(int32_t offset, int origin); + //---------------------------------------------------------------------------- + /** Get the current position in a stream. + * + * \return If successful, ftell return the current value of the position + * indicator. On failure, ftell returns −1L. + */ + int32_t ftell(); + //---------------------------------------------------------------------------- + /** Binary output. + * + * Writes an array of up to count elements, each one with a size of size + * bytes. + * \param[in] ptr pointer to (size*count) bytes of data to be written. + * + * \param[in] size the size, in bytes, of each element to be written. + * + * \param[in] count the number of elements to be written. + * + * \return number of elements successfully written. if this number is + * less than count, an error has occurred. If size or count is zero, + * fwrite returns zero. + */ + size_t fwrite(const void * ptr, size_t size, size_t count); + //---------------------------------------------------------------------------- + /** Get a byte from the stream. + * + * getc and fgetc are equivalent but getc is in-line so it is faster but + * require more flash memory. + * + * \return If the end-of-file indicator for the stream is set, or if the + * stream is at end-of-file, the end-of-file indicator for the stream is + * set and the fgetc function returns EOF. Otherwise, the fgetc function + * returns the next character from the input stream. + */ + inline __attribute__((always_inline)) + int getc() { + return m_r-- == 0 ? fillGet() : *m_p++; + } + //---------------------------------------------------------------------------- + /** Write a byte to a stream. + * + * putc and fputc are equivalent but putc is in-line so it is faster but + * require more flash memory. + * + * \param[in] c the byte to be written (converted to an unsigned char). + * + * \return Upon successful completion, fputc() returns the value it + * has written. Otherwise, it returns EOF and sets the error indicator for + * the stream. + */ + inline __attribute__((always_inline)) + int putc(int c) { + return m_w-- == 0 ? flushPut(c) : *m_p++ = c; + } + //---------------------------------------------------------------------------- + /** Write a CR/LF. + * + * \return two, the number of bytes written, for success or -1 for failure. + */ + inline __attribute__((always_inline)) + int putCRLF() { + if (m_w < 2) { + if (!flushBuf()) { + return -1; + } + } + *m_p++ = '\r'; + *m_p++ = '\n'; + m_w -= 2; + return 2; + } + //---------------------------------------------------------------------------- + /** Write a character. + * \param[in] c the character to write. + * \return the number of bytes written. + */ + size_t print(char c) { + return putc(c) < 0 ? 0 : 1; + } + //---------------------------------------------------------------------------- + /** Write a string. + * + * \param[in] str the string to be written. + * + * \return the number of bytes written. + */ + size_t print(const char* str) { + int n = fputs(str); + return n < 0 ? 0 : n; + } + //---------------------------------------------------------------------------- +#if (defined(ARDUINO) && ENABLE_ARDUINO_FEATURES) || defined(DOXYGEN) + /** Print a string stored in flash memory. + * + * \param[in] str the string to print. + * + * \return the number of bytes written. + */ + size_t print(const __FlashStringHelper *str); +#endif // (defined(ARDUINO) && ENABLE_ARDUINO_FEATURES) || defined(DOXYGEN) + //---------------------------------------------------------------------------- + /** Print a floating point number. + * + * \param[in] prec Number of digits after decimal point. + * + * \param[in] val the number to be printed. + * + * \return the number of bytes written. + */ + size_t print(double val, uint8_t prec = 2) { + return print(static_cast(val), prec); + } + //---------------------------------------------------------------------------- + /** Print a floating point number. + * + * \param[in] prec Number of digits after decimal point. + * + * \param[in] val the number to be printed. + * + * \return the number of bytes written. + */ + size_t print(float val, uint8_t prec = 2) { + int n = printDec(val, prec); + return n > 0 ? n : 0; + } + //---------------------------------------------------------------------------- + /** Print a number. + * + * \param[in] val the number to be printed. + * + * \return the number of bytes written. + */ + template + size_t print(T val) { + int n = printDec(val); + return n > 0 ? n : 0; + } + //---------------------------------------------------------------------------- + /** Write a CR/LF. + * + * \return two, the number of bytes written, for success or zero for failure. + */ + size_t println() { + return putCRLF() > 0 ? 2 : 0; + } + //---------------------------------------------------------------------------- + /** Print a floating point number followed by CR/LF. + * + * \param[in] val the number to be printed. + * + * \param[in] prec Number of digits after decimal point. + * + * \return the number of bytes written. + */ + size_t println(double val, uint8_t prec = 2) { + return println(static_cast(val), prec); + } + //---------------------------------------------------------------------------- + /** Print a floating point number followed by CR/LF. + * + * \param[in] val the number to be printed. + * + * \param[in] prec Number of digits after decimal point. + * + * \return the number of bytes written. + */ + size_t println(float val, uint8_t prec = 2) { + int n = printDec(val, prec); + return n > 0 && putCRLF() > 0 ? n + 2 : 0; + } + //---------------------------------------------------------------------------- + /** Print an item followed by CR/LF + * + * \param[in] val the item to be printed. + * + * \return the number of bytes written. + */ + template + size_t println(T val) { + int n = print(val); + return putCRLF() > 0 ? n + 2 : 0; + } + //---------------------------------------------------------------------------- + /** Print a char as a number. + * \param[in] n number to be printed. + * \return The number of bytes written or -1 if an error occurs. + */ + int printDec(char n) { + if (CHAR_MIN == 0) { + return printDec((unsigned char)n); + } else { + return printDec((signed char)n); + } + } + //---------------------------------------------------------------------------- + /** print a signed 8-bit integer + * \param[in] n number to be printed. + * \return The number of bytes written or -1 if an error occurs. + */ + int printDec(signed char n); + //---------------------------------------------------------------------------- + /** Print an unsigned 8-bit number. + * \param[in] n number to be print. + * \return The number of bytes written or -1 if an error occurs. + */ + int printDec(unsigned char n) { + return printDec((uint16_t)n); + } + //---------------------------------------------------------------------------- + /** Print a int16_t + * \param[in] n number to be printed. + * \return The number of bytes written or -1 if an error occurs. + */ + int printDec(int16_t n); + //---------------------------------------------------------------------------- + /** print a uint16_t. + * \param[in] n number to be printed. + * \return The number of bytes written or -1 if an error occurs. + */ + int printDec(uint16_t n); + //---------------------------------------------------------------------------- + /** Print a signed 32-bit integer. + * \param[in] n number to be printed. + * \return The number of bytes written or -1 if an error occurs. + */ + int printDec(int32_t n); + //---------------------------------------------------------------------------- + /** Write an unsigned 32-bit number. + * \param[in] n number to be printed. + * \return The number of bytes written or -1 if an error occurs. + */ + int printDec(uint32_t n); + //---------------------------------------------------------------------------- + /** Print a double. + * \param[in] value The number to be printed. + * \param[in] prec Number of digits after decimal point. + * \return The number of bytes written or -1 if an error occurs. + */ + int printDec(double value, uint8_t prec) { + return printDec(static_cast(value), prec); + } + //---------------------------------------------------------------------------- + /** Print a float. + * \param[in] value The number to be printed. + * \param[in] prec Number of digits after decimal point. + * \return The number of bytes written or -1 if an error occurs. + */ + int printDec(float value, uint8_t prec); + //---------------------------------------------------------------------------- + /** Print a number followed by a field terminator. + * \param[in] value The number to be printed. + * \param[in] term The field terminator. + * \param[in] prec Number of digits after decimal point. + * \return The number of bytes written or -1 if an error occurs. + */ + int printField(double value, char term, uint8_t prec = 2) { + return printField(static_cast(value), term, prec) > 0; + } + //---------------------------------------------------------------------------- + /** Print a number followed by a field terminator. + * \param[in] value The number to be printed. + * \param[in] term The field terminator. + * \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) { + int rtn = printDec(value, prec); + return rtn < 0 || putc(term) < 0 ? -1 : rtn + 1; + } + //---------------------------------------------------------------------------- + /** Print a number followed by a field terminator. + * \param[in] value The number to be printed. + * \param[in] term The field terminator. + * \return The number of bytes written or -1 if an error occurs. + */ + template + int printField(T value, char term) { + int rtn = printDec(value); + return rtn < 0 || putc(term) < 0 ? -1 : rtn + 1; + } + //---------------------------------------------------------------------------- + /** Print HEX + * \param[in] n number to be printed as HEX. + * + * \return The number of bytes written or -1 if an error occurs. + */ + int printHex(uint32_t n); + //---------------------------------------------------------------------------- + /** Print HEX with CRLF + * \param[in] n number to be printed as HEX. + * + * \return The number of bytes written or -1 if an error occurs. + */ + int printHexln(uint32_t n) { + int rtn = printHex(n); + return rtn < 0 || putCRLF() != 2 ? -1 : rtn + 2; + } + //---------------------------------------------------------------------------- + /** Set position of a stream to the beginning. + * + * The rewind function sets the file position to the beginning of the + * file. It is equivalent to fseek(0L, SEEK_SET) except that the error + * indicator for the stream is also cleared. + * + * \return true for success or false for failure. + */ + bool rewind(); + //---------------------------------------------------------------------------- + /** Push a byte back into an input stream. + * + * \param[in] c the byte (converted to an unsigned char) to be pushed back. + * + * One character of push-back is guaranteed. If the ungetc function is + * called too many times without an intervening read or file positioning + * operation on that stream, the operation may fail. + * + * A successful intervening call to a file positioning function (fseek, + * fsetpos, or rewind) discards any pushed-back characters for the stream. + * + * \return Upon successful completion, ungetc() returns the byte pushed + * back after conversion. Otherwise it returns EOF. + */ + int ungetc(int c); + //============================================================================ + private: + bool fillBuf(); + int fillGet(); + bool flushBuf(); + int flushPut(uint8_t c); + char* fmtSpace(uint8_t len); + int write(const void* buf, size_t count); + //---------------------------------------------------------------------------- + // S_SRD and S_WR are never simultaneously asserted + static const uint8_t S_SRD = 0x01; // OK to read + static const uint8_t S_SWR = 0x02; // OK to write + static const uint8_t S_SRW = 0x04; // open for reading & writing + static const uint8_t S_EOF = 0x10; // found EOF + static const uint8_t S_ERR = 0x20; // found error + //---------------------------------------------------------------------------- + uint8_t m_status; + uint8_t* m_p; + uint8_t m_r; + uint8_t m_w; + uint8_t m_buf[STREAM_BUF_SIZE]; +}; +//------------------------------------------------------------------------------ +#endif // StdioStream_h diff --git a/hardware/_controller/src/SdFat/FatLib/bufstream.h b/hardware/_controller/src/SdFat/FatLib/bufstream.h new file mode 100644 index 0000000..f3cb883 --- /dev/null +++ b/hardware/_controller/src/SdFat/FatLib/bufstream.h @@ -0,0 +1,172 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef bufstream_h +#define bufstream_h +/** + * \file + * \brief \ref ibufstream and \ref obufstream classes + */ +#include +#include "iostream.h" +//============================================================================== +/** + * \class ibufstream + * \brief parse a char string + */ +class ibufstream : public istream { + public: + /** Constructor */ + ibufstream() : m_buf(0), m_len(0) {} + /** Constructor + * \param[in] str pointer to string to be parsed + * Warning: The string will not be copied so must stay in scope. + */ + explicit ibufstream(const char* str) { + init(str); + } + /** Initialize an ibufstream + * \param[in] str pointer to string to be parsed + * Warning: The string will not be copied so must stay in scope. + */ + void init(const char* str) { + m_buf = str; + m_len = strlen(m_buf); + m_pos = 0; + clear(); + } + + protected: + /// @cond SHOW_PROTECTED + int16_t getch() { + if (m_pos < m_len) { + return m_buf[m_pos++]; + } + setstate(eofbit); + return -1; + } + void getpos(FatPos_t *pos) { + pos->position = m_pos; + } + bool seekoff(off_type off, seekdir way) { + (void)off; + (void)way; + return false; + } + bool seekpos(pos_type pos) { + if (pos < m_len) { + m_pos = pos; + return true; + } + return false; + } + void setpos(FatPos_t *pos) { + m_pos = pos->position; + } + pos_type tellpos() { + return m_pos; + } + /// @endcond + private: + const char* m_buf; + size_t m_len; + size_t m_pos; +}; +//============================================================================== +/** + * \class obufstream + * \brief format a char string + */ +class obufstream : public ostream { + public: + /** constructor */ + obufstream() : m_in(0) {} + /** Constructor + * \param[in] buf buffer for formatted string + * \param[in] size buffer size + */ + obufstream(char *buf, size_t size) { + init(buf, size); + } + /** Initialize an obufstream + * \param[in] buf buffer for formatted string + * \param[in] size buffer size + */ + void init(char *buf, size_t size) { + m_buf = buf; + buf[0] = '\0'; + m_size = size; + m_in = 0; + } + /** \return a pointer to the buffer */ + char* buf() { + return m_buf; + } + /** \return the length of the formatted string */ + size_t length() { + return m_in; + } + + protected: + /// @cond SHOW_PROTECTED + void putch(char c) { + if (m_in >= (m_size - 1)) { + setstate(badbit); + return; + } + m_buf[m_in++] = c; + m_buf[m_in] = '\0'; + } + void putstr(const char *str) { + while (*str) { + putch(*str++); + } + } + bool seekoff(off_type off, seekdir way) { + (void)off; + (void)way; + return false; + } + bool seekpos(pos_type pos) { + if (pos > m_in) { + return false; + } + m_in = pos; + m_buf[m_in] = '\0'; + return true; + } + bool sync() { + return true; + } + + pos_type tellpos() { + return m_in; + } + /// @endcond + private: + char *m_buf; + size_t m_size; + size_t m_in; +}; +#endif // bufstream_h diff --git a/hardware/_controller/src/SdFat/FatLib/fstream.cpp b/hardware/_controller/src/SdFat/FatLib/fstream.cpp new file mode 100644 index 0000000..01ec189 --- /dev/null +++ b/hardware/_controller/src/SdFat/FatLib/fstream.cpp @@ -0,0 +1,172 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "fstream.h" +//============================================================================== +/// @cond SHOW_PROTECTED +int16_t FatStreamBase::getch() { + uint8_t c; + int8_t s = read(&c, 1); + if (s != 1) { + if (s < 0) { + setstate(badbit); + } else { + setstate(eofbit); + } + return -1; + } + if (c != '\r' || (getmode() & ios::binary)) { + return c; + } + s = read(&c, 1); + if (s == 1 && c == '\n') { + return c; + } + if (s == 1) { + seekCur(-1); + } + return '\r'; +} +//------------------------------------------------------------------------------ +void FatStreamBase::open(const char* path, ios::openmode mode) { + oflag_t oflag; + switch (mode & (app | in | out | trunc)) { + case app | in: + case app | in | out: + oflag = O_RDWR | O_APPEND | O_CREAT; + break; + + case app: + case app | out: + oflag = O_WRONLY | O_APPEND | O_CREAT; + break; + + case in: + oflag = O_RDONLY; + break; + + case in | out: + oflag = O_RDWR; + break; + + case in | out | trunc: + oflag = O_RDWR | O_TRUNC | O_CREAT; + break; + + case out: + case out | trunc: + oflag = O_WRONLY | O_TRUNC | O_CREAT; + break; + + default: + goto fail; + } + if (mode & ios::ate) { + oflag |= O_AT_END; + } + if (!FatFile::open(path, oflag)) { + goto fail; + } + setmode(mode); + clear(); + return; + +fail: + FatFile::close(); + setstate(failbit); + return; +} +//------------------------------------------------------------------------------ +void FatStreamBase::putch(char c) { + if (c == '\n' && !(getmode() & ios::binary)) { + write('\r'); + } + write(c); + if (getWriteError()) { + setstate(badbit); + } +} +//------------------------------------------------------------------------------ +void FatStreamBase::putstr(const char* str) { + size_t n = 0; + while (1) { + char c = str[n]; + if (c == '\0' || (c == '\n' && !(getmode() & ios::binary))) { + if (n > 0) { + write(str, n); + } + if (c == '\0') { + break; + } + write('\r'); + str += n; + n = 0; + } + n++; + } + if (getWriteError()) { + setstate(badbit); + } +} +//------------------------------------------------------------------------------ +/** Internal do not use + * \param[in] off + * \param[in] way + */ +bool FatStreamBase::seekoff(off_type off, seekdir way) { + pos_type pos; + switch (way) { + case beg: + pos = off; + break; + + case cur: + pos = curPosition() + off; + break; + + case end: + pos = fileSize() + off; + break; + + default: + return false; + } + return seekpos(pos); +} +//------------------------------------------------------------------------------ +/** Internal do not use + * \param[in] pos + */ +bool FatStreamBase::seekpos(pos_type pos) { + return seekSet(pos); +} +//------------------------------------------------------------------------------ +int FatStreamBase::write(const void* buf, size_t n) { + return FatFile::write(buf, n); +} +//------------------------------------------------------------------------------ +void FatStreamBase::write(char c) { + write(&c, 1); +} +/// @endcond diff --git a/hardware/_controller/src/SdFat/FatLib/fstream.h b/hardware/_controller/src/SdFat/FatLib/fstream.h new file mode 100644 index 0000000..f8d80dd --- /dev/null +++ b/hardware/_controller/src/SdFat/FatLib/fstream.h @@ -0,0 +1,320 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef fstream_h +#define fstream_h +/** + * \file + * \brief \ref fstream, \ref ifstream, and \ref ofstream classes + */ +#include "FatFile.h" +#include "iostream.h" +//============================================================================== +/** + * \class FatStreamBase + * \brief Base class for C++ style streams + */ +class FatStreamBase : protected FatFile, virtual public ios { + protected: + /// @cond SHOW_PROTECTED + int16_t getch(); + void putch(char c); + void putstr(const char *str); + void open(const char* path, ios::openmode mode); + /** Internal do not use + * \return mode + */ + ios::openmode getmode() { + return m_mode; + } + /** Internal do not use + * \param[in] mode + */ + void setmode(ios::openmode mode) { + m_mode = mode; + } + bool seekoff(off_type off, seekdir way); + bool seekpos(pos_type pos); + int write(const void* buf, size_t n); + void write(char c); + /// @endcond + private: + ios::openmode m_mode; +}; +//============================================================================== +/** + * \class fstream + * \brief file input/output stream. + */ +class fstream : public iostream, FatStreamBase { + public: + using iostream::peek; + fstream() {} + /** Constructor with open + * + * \param[in] path path to open + * \param[in] mode open mode + */ + explicit fstream(const char* path, openmode mode = in | out) { + open(path, mode); + } +#if DESTRUCTOR_CLOSES_FILE + ~fstream() {} +#endif // DESTRUCTOR_CLOSES_FILE + /** Clear state and writeError + * \param[in] state new state for stream + */ + void clear(iostate state = goodbit) { + ios::clear(state); + FatFile::clearWriteError(); + } + /** Close a file and force cached data and directory information + * to be written to the storage device. + */ + void close() { + FatFile::close(); + } + /** Open a fstream + * \param[in] path file to open + * \param[in] mode open mode + * + * Valid open modes are (at end, ios::ate, and/or ios::binary may be added): + * + * ios::in - Open file for reading. + * + * ios::out or ios::out | ios::trunc - Truncate to 0 length, if existent, + * or create a file for writing only. + * + * ios::app or ios::out | ios::app - Append; open or create file for + * writing at end-of-file. + * + * ios::in | ios::out - Open file for update (reading and writing). + * + * ios::in | ios::out | ios::trunc - Truncate to zero length, if existent, + * or create file for update. + * + * ios::in | ios::app or ios::in | ios::out | ios::app - Append; open or + * create text file for update, writing at end of file. + */ + void open(const char* path, openmode mode = in | out) { + FatStreamBase::open(path, mode); + } + /** \return True if stream is open else false. */ + bool is_open() { + return FatFile::isOpen(); + } + + protected: + /// @cond SHOW_PROTECTED + /** Internal - do not use + * \return + */ + int16_t getch() { + return FatStreamBase::getch(); + } + /** Internal - do not use + * \param[out] pos + */ + void getpos(FatPos_t* pos) { + FatFile::getpos(pos); + } + /** Internal - do not use + * \param[in] c + */ + void putch(char c) { + FatStreamBase::putch(c); + } + /** Internal - do not use + * \param[in] str + */ + void putstr(const char *str) { + FatStreamBase::putstr(str); + } + /** Internal - do not use + * \param[in] pos + */ + bool seekoff(off_type off, seekdir way) { + return FatStreamBase::seekoff(off, way); + } + bool seekpos(pos_type pos) { + return FatStreamBase::seekpos(pos); + } + void setpos(FatPos_t* pos) { + FatFile::setpos(pos); + } + bool sync() { + return FatStreamBase::sync(); + } + pos_type tellpos() { + return FatStreamBase::curPosition(); + } + /// @endcond +}; +//============================================================================== +/** + * \class ifstream + * \brief file input stream. + */ +class ifstream : public istream, FatStreamBase { + public: + using istream::peek; + ifstream() {} + /** Constructor with open + * \param[in] path file to open + * \param[in] mode open mode + */ + explicit ifstream(const char* path, openmode mode = in) { + open(path, mode); + } +#if DESTRUCTOR_CLOSES_FILE + ~ifstream() {} +#endif // DESTRUCTOR_CLOSES_FILE + /** Close a file and force cached data and directory information + * to be written to the storage device. + */ + void close() { + FatFile::close(); + } + /** \return True if stream is open else false. */ + bool is_open() { + return FatFile::isOpen(); + } + /** Open an ifstream + * \param[in] path file to open + * \param[in] mode open mode + * + * \a mode See fstream::open() for valid modes. + */ + void open(const char* path, openmode mode = in) { + FatStreamBase::open(path, mode | in); + } + + protected: + /// @cond SHOW_PROTECTED + /** Internal - do not use + * \return + */ + int16_t getch() { + return FatStreamBase::getch(); + } + /** Internal - do not use + * \param[out] pos + */ + void getpos(FatPos_t* pos) { + FatFile::getpos(pos); + } + /** Internal - do not use + * \param[in] pos + */ + bool seekoff(off_type off, seekdir way) { + return FatStreamBase::seekoff(off, way); + } + bool seekpos(pos_type pos) { + return FatStreamBase::seekpos(pos); + } + void setpos(FatPos_t* pos) { + FatFile::setpos(pos); + } + pos_type tellpos() { + return FatStreamBase::curPosition(); + } + /// @endcond +}; +//============================================================================== +/** + * \class ofstream + * \brief file output stream. + */ +class ofstream : public ostream, FatStreamBase { + public: + ofstream() {} + /** Constructor with open + * \param[in] path file to open + * \param[in] mode open mode + */ + explicit ofstream(const char* path, ios::openmode mode = out) { + open(path, mode); + } +#if DESTRUCTOR_CLOSES_FILE + ~ofstream() {} +#endif // DESTRUCTOR_CLOSES_FILE + /** Clear state and writeError + * \param[in] state new state for stream + */ + void clear(iostate state = goodbit) { + ios::clear(state); + FatFile::clearWriteError(); + } + /** Close a file and force cached data and directory information + * to be written to the storage device. + */ + void close() { + FatFile::close(); + } + /** Open an ofstream + * \param[in] path file to open + * \param[in] mode open mode + * + * \a mode See fstream::open() for valid modes. + */ + void open(const char* path, openmode mode = out) { + FatStreamBase::open(path, mode | out); + } + /** \return True if stream is open else false. */ + bool is_open() { + return FatFile::isOpen(); + } + + protected: + /// @cond SHOW_PROTECTED + /** + * Internal do not use + * \param[in] c + */ + void putch(char c) { + FatStreamBase::putch(c); + } + void putstr(const char* str) { + FatStreamBase::putstr(str); + } + bool seekoff(off_type off, seekdir way) { + return FatStreamBase::seekoff(off, way); + } + bool seekpos(pos_type pos) { + return FatStreamBase::seekpos(pos); + } + /** + * Internal do not use + * \param[in] b + */ + bool sync() { + return FatStreamBase::sync(); + } + pos_type tellpos() { + return FatStreamBase::curPosition(); + } + /// @endcond +}; +//------------------------------------------------------------------------------ +#endif // fstream_h diff --git a/hardware/_controller/src/SdFat/FatLib/ios.h b/hardware/_controller/src/SdFat/FatLib/ios.h new file mode 100644 index 0000000..460718d --- /dev/null +++ b/hardware/_controller/src/SdFat/FatLib/ios.h @@ -0,0 +1,423 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef ios_h +#define ios_h +#include "FatFile.h" +/** + * \file + * \brief \ref ios_base and \ref ios classes + */ +//============================================================================== +/** + * \class ios_base + * \brief Base class for all streams + */ +class ios_base { + public: + /** typedef for iostate bitmask */ + typedef unsigned char iostate; + // State flags. + /** iostate for no flags */ + static const iostate goodbit = 0x00; + /** iostate bad bit for a nonrecoverable error. */ + static const iostate badbit = 0X01; + /** iostate bit for end of file reached */ + static const iostate eofbit = 0x02; + /** iostate fail bit for nonfatal error */ + static const iostate failbit = 0X04; + /** + * unsigned size that can represent maximum file size. + * (violates spec - should be signed) + */ + typedef uint32_t streamsize; + /** type for absolute seek position */ + typedef uint32_t pos_type; + /** type for relative seek offset */ + typedef int32_t off_type; + + /** enumerated type for the direction of relative seeks */ + enum seekdir { + /** seek relative to the beginning of the stream */ + beg, + /** seek relative to the current stream position */ + cur, + /** seek relative to the end of the stream */ + end + }; + /** type for format flags */ + typedef unsigned int fmtflags; + /** left adjust fields */ + static const fmtflags left = 0x0001; + /** right adjust fields */ + static const fmtflags right = 0x0002; + /** fill between sign/base prefix and number */ + static const fmtflags internal = 0x0004; + /** base 10 flag*/ + static const fmtflags dec = 0x0008; + /** base 16 flag */ + static const fmtflags hex = 0x0010; + /** base 8 flag */ + static const fmtflags oct = 0x0020; + // static const fmtflags fixed = 0x0040; + // static const fmtflags scientific = 0x0080; + /** use strings true/false for bool */ + static const fmtflags boolalpha = 0x0100; + /** use prefix 0X for hex and 0 for oct */ + static const fmtflags showbase = 0x0200; + /** always show '.' for floating numbers */ + static const fmtflags showpoint = 0x0400; + /** show + sign for nonnegative numbers */ + static const fmtflags showpos = 0x0800; + /** skip initial white space */ + static const fmtflags skipws = 0x1000; + // static const fmtflags unitbuf = 0x2000; + /** use uppercase letters in number representations */ + static const fmtflags uppercase = 0x4000; + /** mask for adjustfield */ + static const fmtflags adjustfield = left | right | internal; + /** mask for basefield */ + static const fmtflags basefield = dec | hex | oct; + // static const fmtflags floatfield = scientific | fixed; + //---------------------------------------------------------------------------- + /** typedef for iostream open mode */ + typedef uint8_t openmode; + + // Openmode flags. + /** seek to end before each write */ + static const openmode app = 0X4; + /** open and seek to end immediately after opening */ + static const openmode ate = 0X8; + /** perform input and output in binary mode (as opposed to text mode) */ + static const openmode binary = 0X10; + /** open for input */ + static const openmode in = 0X20; + /** open for output */ + static const openmode out = 0X40; + /** truncate an existing stream when opening */ + static const openmode trunc = 0X80; + //---------------------------------------------------------------------------- + ios_base() : m_fill(' '), m_fmtflags(dec | right | skipws) + , m_precision(2), m_width(0) {} + /** \return fill character */ + char fill() { + return m_fill; + } + /** Set fill character + * \param[in] c new fill character + * \return old fill character + */ + char fill(char c) { + char r = m_fill; + m_fill = c; + return r; + } + /** \return format flags */ + fmtflags flags() const { + return m_fmtflags; + } + /** set format flags + * \param[in] fl new flag + * \return old flags + */ + fmtflags flags(fmtflags fl) { + fmtflags tmp = m_fmtflags; + m_fmtflags = fl; + return tmp; + } + /** \return precision */ + int precision() const { + return m_precision; + } + /** set precision + * \param[in] n new precision + * \return old precision + */ + int precision(unsigned int n) { + int r = m_precision; + m_precision = n; + return r; + } + /** set format flags + * \param[in] fl new flags to be or'ed in + * \return old flags + */ + fmtflags setf(fmtflags fl) { + fmtflags r = m_fmtflags; + m_fmtflags |= fl; + return r; + } + /** modify format flags + * \param[in] mask flags to be removed + * \param[in] fl flags to be set after mask bits have been cleared + * \return old flags + */ + fmtflags setf(fmtflags fl, fmtflags mask) { + fmtflags r = m_fmtflags; + m_fmtflags &= ~mask; + m_fmtflags |= fl; + return r; + } + /** clear format flags + * \param[in] fl flags to be cleared + * \return old flags + */ + void unsetf(fmtflags fl) { + m_fmtflags &= ~fl; + } + /** \return width */ + unsigned width() { + return m_width; + } + /** set width + * \param[in] n new width + * \return old width + */ + unsigned width(unsigned n) { + unsigned r = m_width; + m_width = n; + return r; + } + + protected: + /** \return current number base */ + uint8_t flagsToBase() { + uint8_t f = flags() & basefield; + return f == oct ? 8 : f != hex ? 10 : 16; + } + + private: + char m_fill; + fmtflags m_fmtflags; + unsigned char m_precision; + unsigned int m_width; +}; +//------------------------------------------------------------------------------ +/** function for boolalpha manipulator + * \param[in] str The stream + * \return The stream + */ +inline ios_base& boolalpha(ios_base& str) { + str.setf(ios_base::boolalpha); + return str; +} +/** function for dec manipulator + * \param[in] str The stream + * \return The stream + */ +inline ios_base& dec(ios_base& str) { + str.setf(ios_base::dec, ios_base::basefield); + return str; +} +/** function for hex manipulator + * \param[in] str The stream + * \return The stream + */ +inline ios_base& hex(ios_base& str) { + str.setf(ios_base::hex, ios_base::basefield); + return str; +} +/** function for internal manipulator + * \param[in] str The stream + * \return The stream + */ +inline ios_base& internal(ios_base& str) { + str.setf(ios_base::internal, ios_base::adjustfield); + return str; +} +/** function for left manipulator + * \param[in] str The stream + * \return The stream + */ +inline ios_base& left(ios_base& str) { + str.setf(ios_base::left, ios_base::adjustfield); + return str; +} +/** function for noboolalpha manipulator + * \param[in] str The stream + * \return The stream + */ +inline ios_base& noboolalpha(ios_base& str) { + str.unsetf(ios_base::boolalpha); + return str; +} +/** function for noshowbase manipulator + * \param[in] str The stream + * \return The stream + */ +inline ios_base& noshowbase(ios_base& str) { + str.unsetf(ios_base::showbase); + return str; +} +/** function for noshowpoint manipulator + * \param[in] str The stream + * \return The stream + */ +inline ios_base& noshowpoint(ios_base& str) { + str.unsetf(ios_base::showpoint); + return str; +} +/** function for noshowpos manipulator + * \param[in] str The stream + * \return The stream + */ +inline ios_base& noshowpos(ios_base& str) { + str.unsetf(ios_base::showpos); + return str; +} +/** function for noskipws manipulator + * \param[in] str The stream + * \return The stream + */ +inline ios_base& noskipws(ios_base& str) { + str.unsetf(ios_base::skipws); + return str; +} +/** function for nouppercase manipulator + * \param[in] str The stream + * \return The stream + */ +inline ios_base& nouppercase(ios_base& str) { + str.unsetf(ios_base::uppercase); + return str; +} +/** function for oct manipulator + * \param[in] str The stream + * \return The stream + */ +inline ios_base& oct(ios_base& str) { + str.setf(ios_base::oct, ios_base::basefield); + return str; +} +/** function for right manipulator + * \param[in] str The stream + * \return The stream + */ +inline ios_base& right(ios_base& str) { + str.setf(ios_base::right, ios_base::adjustfield); + return str; +} +/** function for showbase manipulator + * \param[in] str The stream + * \return The stream + */ +inline ios_base& showbase(ios_base& str) { + str.setf(ios_base::showbase); + return str; +} +/** function for showpos manipulator + * \param[in] str The stream + * \return The stream + */ +inline ios_base& showpos(ios_base& str) { + str.setf(ios_base::showpos); + return str; +} +/** function for showpoint manipulator + * \param[in] str The stream + * \return The stream + */ +inline ios_base& showpoint(ios_base& str) { + str.setf(ios_base::showpoint); + return str; +} +/** function for skipws manipulator + * \param[in] str The stream + * \return The stream + */ +inline ios_base& skipws(ios_base& str) { + str.setf(ios_base::skipws); + return str; +} +/** function for uppercase manipulator + * \param[in] str The stream + * \return The stream + */ +inline ios_base& uppercase(ios_base& str) { + str.setf(ios_base::uppercase); + return str; +} +//============================================================================== +/** + * \class ios + * \brief Error and state information for all streams + */ +class ios : public ios_base { + public: + /** Create ios with no error flags set */ + ios() : m_iostate(0) {} + + /** \return null pointer if fail() is true. */ + operator const void*() const { + return !fail() ? reinterpret_cast(this) : 0; + } + /** \return true if fail() else false. */ + bool operator!() const { + return fail(); + } + /** \return The iostate flags for this file. */ + iostate rdstate() const { + return m_iostate; + } + /** \return True if no iostate flags are set else false. */ + bool good() const { + return m_iostate == goodbit; + } + /** \return true if end of file has been reached else false. + * + * Warning: An empty file returns false before the first read. + * + * Moral: eof() is only useful in combination with fail(), to find out + * whether EOF was the cause for failure + */ + bool eof() const { + return m_iostate & eofbit; + } + /** \return true if any iostate bit other than eof are set else false. */ + bool fail() const { + return m_iostate & (failbit | badbit); + } + /** \return true if bad bit is set else false. */ + bool bad() const { + return m_iostate & badbit; + } + /** Clear iostate bits. + * + * \param[in] state The flags you want to set after clearing all flags. + **/ + void clear(iostate state = goodbit) { + m_iostate = state; + } + /** Set iostate bits. + * + * \param[in] state Bitts to set. + **/ + void setstate(iostate state) { + m_iostate |= state; + } + + private: + iostate m_iostate; +}; +#endif // ios_h diff --git a/hardware/_controller/src/SdFat/FatLib/iostream.h b/hardware/_controller/src/SdFat/FatLib/iostream.h new file mode 100644 index 0000000..6b47419 --- /dev/null +++ b/hardware/_controller/src/SdFat/FatLib/iostream.h @@ -0,0 +1,158 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef iostream_h +#define iostream_h +/** + * \file + * \brief \ref iostream class + */ +#include "istream.h" +#include "ostream.h" +/** Skip white space + * \param[in] is the Stream + * \return The stream + */ +inline istream& ws(istream& is) { + is.skipWhite(); + return is; +} +/** insert endline + * \param[in] os The Stream + * \return The stream + */ +inline ostream& endl(ostream& os) { + os.put('\n'); +#if ENDL_CALLS_FLUSH + os.flush(); +#endif // ENDL_CALLS_FLUSH + return os; +} +/** flush manipulator + * \param[in] os The stream + * \return The stream + */ +inline ostream& flush(ostream& os) { + os.flush(); + return os; +} +/** + * \struct setfill + * \brief type for setfill manipulator + */ +struct setfill { + /** fill character */ + char c; + /** constructor + * + * \param[in] arg new fill character + */ + explicit setfill(char arg) : c(arg) {} +}; +/** setfill manipulator + * \param[in] os the stream + * \param[in] arg set setfill object + * \return the stream + */ +inline ostream &operator<< (ostream &os, const setfill &arg) { + os.fill(arg.c); + return os; +} +/** setfill manipulator + * \param[in] obj the stream + * \param[in] arg set setfill object + * \return the stream + */ +inline istream &operator>>(istream &obj, const setfill &arg) { + obj.fill(arg.c); + return obj; +} +//------------------------------------------------------------------------------ +/** \struct setprecision + * \brief type for setprecision manipulator + */ +struct setprecision { + /** precision */ + unsigned int p; + /** constructor + * \param[in] arg new precision + */ + explicit setprecision(unsigned int arg) : p(arg) {} +}; +/** setprecision manipulator + * \param[in] os the stream + * \param[in] arg set setprecision object + * \return the stream + */ +inline ostream &operator<< (ostream &os, const setprecision &arg) { + os.precision(arg.p); + return os; +} +/** setprecision manipulator + * \param[in] is the stream + * \param[in] arg set setprecision object + * \return the stream + */ +inline istream &operator>>(istream &is, const setprecision &arg) { + is.precision(arg.p); + return is; +} +//------------------------------------------------------------------------------ +/** \struct setw + * \brief type for setw manipulator + */ +struct setw { + /** width */ + unsigned w; + /** constructor + * \param[in] arg new width + */ + explicit setw(unsigned arg) : w(arg) {} +}; +/** setw manipulator + * \param[in] os the stream + * \param[in] arg set setw object + * \return the stream + */ +inline ostream &operator<< (ostream &os, const setw &arg) { + os.width(arg.w); + return os; +} +/** setw manipulator + * \param[in] is the stream + * \param[in] arg set setw object + * \return the stream + */ +inline istream &operator>>(istream &is, const setw &arg) { + is.width(arg.w); + return is; +} +//============================================================================== +/** + * \class iostream + * \brief Input/Output stream + */ +class iostream : public istream, public ostream { +}; +#endif // iostream_h diff --git a/hardware/_controller/src/SdFat/FatLib/istream.cpp b/hardware/_controller/src/SdFat/FatLib/istream.cpp new file mode 100644 index 0000000..74ae295 --- /dev/null +++ b/hardware/_controller/src/SdFat/FatLib/istream.cpp @@ -0,0 +1,396 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +// #include +#include +#include +#include "istream.h" +//------------------------------------------------------------------------------ +int istream::get() { + int c; + m_gcount = 0; + c = getch(); + if (c < 0) { + setstate(failbit); + } else { + m_gcount = 1; + } + return c; +} +//------------------------------------------------------------------------------ +istream& istream::get(char& c) { + int tmp = get(); + if (tmp >= 0) { + c = tmp; + } + return *this; +} +//------------------------------------------------------------------------------ +istream& istream::get(char *str, streamsize n, char delim) { + int c; + FatPos_t pos; + m_gcount = 0; + while ((m_gcount + 1) < n) { + c = getch(&pos); + if (c < 0) { + break; + } + if (c == delim) { + setpos(&pos); + break; + } + str[m_gcount++] = c; + } + if (n > 0) { + str[m_gcount] = '\0'; + } + if (m_gcount == 0) { + setstate(failbit); + } + return *this; +} +//------------------------------------------------------------------------------ +void istream::getBool(bool *b) { + if ((flags() & boolalpha) == 0) { + getNumber(b); + return; + } +#ifdef __AVR__ + PGM_P truePtr = PSTR("true"); + PGM_P falsePtr = PSTR("false"); +#else // __AVR__ + const char* truePtr = "true"; + const char* falsePtr = "false"; +#endif // __AVR + const uint8_t true_len = 4; + const uint8_t false_len = 5; + bool trueOk = true; + bool falseOk = true; + uint8_t i = 0; + int c = readSkip(); + while (1) { +#ifdef __AVR__ + falseOk = falseOk && c == pgm_read_byte(falsePtr + i); + trueOk = trueOk && c == pgm_read_byte(truePtr + i); +#else // __AVR__ + falseOk = falseOk && c == falsePtr[i]; + trueOk = trueOk && c == truePtr[i]; +#endif // __AVR__ + if (trueOk == false && falseOk == false) { + break; + } + i++; + if (trueOk && i == true_len) { + *b = true; + return; + } + if (falseOk && i == false_len) { + *b = false; + return; + } + c = getch(); + } + setstate(failbit); +} +//------------------------------------------------------------------------------ +void istream::getChar(char* ch) { + int16_t c = readSkip(); + if (c < 0) { + setstate(failbit); + } else { + *ch = c; + } +} +//------------------------------------------------------------------------------ +// +// http://www.exploringbinary.com/category/numbers-in-computers/ +// +int16_t const EXP_LIMIT = 100; +static const uint32_t uint32_max = (uint32_t)-1; +bool istream::getDouble(double* value) { + bool got_digit = false; + bool got_dot = false; + bool neg; + int16_t c; + bool expNeg = false; + int16_t exp = 0; + int16_t fracExp = 0; + uint32_t frac = 0; + FatPos_t endPos; + double pow10; + double v; + + getpos(&endPos); + c = readSkip(); + neg = c == '-'; + if (c == '-' || c == '+') { + c = getch(); + } + while (1) { + if (isdigit(c)) { + got_digit = true; + if (frac < uint32_max/10) { + frac = frac * 10 + (c - '0'); + if (got_dot) { + fracExp--; + } + } else { + if (!got_dot) { + fracExp++; + } + } + } else if (!got_dot && c == '.') { + got_dot = true; + } else { + break; + } + if (fracExp < -EXP_LIMIT || fracExp > EXP_LIMIT) { + goto fail; + } + c = getch(&endPos); + } + if (!got_digit) { + goto fail; + } + if (c == 'e' || c == 'E') { + c = getch(); + expNeg = c == '-'; + if (c == '-' || c == '+') { + c = getch(); + } + while (isdigit(c)) { + if (exp > EXP_LIMIT) { + goto fail; + } + exp = exp * 10 + (c - '0'); + c = getch(&endPos); + } + } + v = static_cast(frac); + exp = expNeg ? fracExp - exp : fracExp + exp; + expNeg = exp < 0; + if (expNeg) { + exp = -exp; + } + pow10 = 10.0; + while (exp) { + if (exp & 1) { + if (expNeg) { + // check for underflow + if (v < FLT_MIN * pow10 && frac != 0) { + goto fail; + } + v /= pow10; + } else { + // check for overflow + if (v > FLT_MAX / pow10) { + goto fail; + } + v *= pow10; + } + } + pow10 *= pow10; + exp >>= 1; + } + setpos(&endPos); + *value = neg ? -v : v; + return true; + +fail: + // error restore position to last good place + setpos(&endPos); + setstate(failbit); + return false; +} +//------------------------------------------------------------------------------ + +istream& istream::getline(char *str, streamsize n, char delim) { + FatPos_t pos; + int c; + m_gcount = 0; + if (n > 0) { + str[0] = '\0'; + } + while (1) { + c = getch(&pos); + if (c < 0) { + break; + } + if (c == delim) { + m_gcount++; + break; + } + if ((m_gcount + 1) >= n) { + setpos(&pos); + setstate(failbit); + break; + } + str[m_gcount++] = c; + str[m_gcount] = '\0'; + } + if (m_gcount == 0) { + setstate(failbit); + } + return *this; +} +//------------------------------------------------------------------------------ +bool istream::getNumber(uint32_t posMax, uint32_t negMax, uint32_t* num) { + int16_t c; + int8_t any = 0; + int8_t have_zero = 0; + uint8_t neg; + uint32_t val = 0; + uint32_t cutoff; + uint8_t cutlim; + FatPos_t endPos; + uint8_t f = flags() & basefield; + uint8_t base = f == oct ? 8 : f != hex ? 10 : 16; + getpos(&endPos); + c = readSkip(); + + neg = c == '-' ? 1 : 0; + if (c == '-' || c == '+') { + c = getch(); + } + + if (base == 16 && c == '0') { // TESTSUITE + c = getch(&endPos); + if (c == 'X' || c == 'x') { + c = getch(); + // remember zero in case no hex digits follow x/X + have_zero = 1; + } else { + any = 1; + } + } + // set values for overflow test + cutoff = neg ? negMax : posMax; + cutlim = cutoff % base; + cutoff /= base; + + while (1) { + if (isdigit(c)) { + c -= '0'; + } else if (isalpha(c)) { + c -= isupper(c) ? 'A' - 10 : 'a' - 10; + } else { + break; + } + if (c >= base) { + break; + } + if (val > cutoff || (val == cutoff && c > cutlim)) { + // indicate overflow error + any = -1; + break; + } + val = val * base + c; + c = getch(&endPos); + any = 1; + } + setpos(&endPos); + if (any > 0 || (have_zero && any >= 0)) { + *num = neg ? -val : val; + return true; + } + setstate(failbit); + return false; +} +//------------------------------------------------------------------------------ +void istream::getStr(char *str) { + FatPos_t pos; + uint16_t i = 0; + uint16_t m = width() ? width() - 1 : 0XFFFE; + if (m != 0) { + getpos(&pos); + int c = readSkip(); + + while (i < m) { + if (c < 0) { + break; + } + if (isspace(c)) { + setpos(&pos); + break; + } + str[i++] = c; + c = getch(&pos); + } + } + str[i] = '\0'; + if (i == 0) { + setstate(failbit); + } + width(0); +} +//------------------------------------------------------------------------------ +istream& istream::ignore(streamsize n, int delim) { + int c; + m_gcount = 0; + while (m_gcount < n) { + c = getch(); + if (c < 0) { + break; + } + m_gcount++; + if (c == delim) { + break; + } + } + return *this; +} +//------------------------------------------------------------------------------ +int istream::peek() { + int16_t c; + FatPos_t pos; + m_gcount = 0; + getpos(&pos); + c = getch(); + if (c < 0) { + if (!bad()) { + setstate(eofbit); + } + } else { + setpos(&pos); + } + return c; +} +//------------------------------------------------------------------------------ +int16_t istream::readSkip() { + int16_t c; + do { + c = getch(); + } while (isspace(c) && (flags() & skipws)); + return c; +} +//------------------------------------------------------------------------------ +/** used to implement ws() */ +void istream::skipWhite() { + int c; + FatPos_t pos; + do { + c = getch(&pos); + } while (isspace(c)); + setpos(&pos); +} diff --git a/hardware/_controller/src/SdFat/FatLib/istream.h b/hardware/_controller/src/SdFat/FatLib/istream.h new file mode 100644 index 0000000..5fa81d1 --- /dev/null +++ b/hardware/_controller/src/SdFat/FatLib/istream.h @@ -0,0 +1,384 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef istream_h +#define istream_h +/** + * \file + * \brief \ref istream class + */ +#include "ios.h" + +/** + * \class istream + * \brief Input Stream + */ +class istream : public virtual ios { + public: + istream() {} + /** call manipulator + * \param[in] pf function to call + * \return the stream + */ + istream& operator>>(istream& (*pf)(istream& str)) { + return pf(*this); + } + /** call manipulator + * \param[in] pf function to call + * \return the stream + */ + istream& operator>>(ios_base& (*pf)(ios_base& str)) { + pf(*this); + return *this; + } + /** call manipulator + * \param[in] pf function to call + * \return the stream + */ + istream& operator>>(ios& (*pf)(ios& str)) { + pf(*this); + return *this; + } + /** + * Extract a character string + * \param[out] str location to store the string. + * \return Is always *this. Failure is indicated by the state of *this. + */ + istream& operator>>(char *str) { + getStr(str); + return *this; + } + /** + * Extract a character + * \param[out] ch location to store the character. + * \return Is always *this. Failure is indicated by the state of *this. + */ + istream& operator>>(char& ch) { + getChar(&ch); + return *this; + } + /** + * Extract a character string + * \param[out] str location to store the string. + * \return Is always *this. Failure is indicated by the state of *this. + */ + istream& operator>>(signed char *str) { + getStr(reinterpret_cast(str)); + return *this; + } + /** + * Extract a character + * \param[out] ch location to store the character. + * \return Is always *this. Failure is indicated by the state of *this. + */ + istream& operator>>(signed char& ch) { + getChar(reinterpret_cast(&ch)); + return *this; + } + /** + * Extract a character string + * \param[out] str location to store the string. + * \return Is always *this. Failure is indicated by the state of *this. + */ + istream& operator>>(unsigned char *str) { + getStr(reinterpret_cast(str)); + return *this; + } + /** + * Extract a character + * \param[out] ch location to store the character. + * \return Is always *this. Failure is indicated by the state of *this. + */ + istream& operator>>(unsigned char& ch) { + getChar(reinterpret_cast(&ch)); + return *this; + } + /** + * Extract a value of type bool. + * \param[out] arg location to store the value. + * \return Is always *this. Failure is indicated by the state of *this. + */ + istream& operator>>(bool& arg) { + getBool(&arg); + return *this; + } + /** + * Extract a value of type short. + * \param[out] arg location to store the value. + * \return Is always *this. Failure is indicated by the state of *this. + */ + istream &operator>>(short& arg) { // NOLINT + getNumber(&arg); + return *this; + } + /** + * Extract a value of type unsigned short. + * \param[out] arg location to store the value. + * \return Is always *this. Failure is indicated by the state of *this. + */ + istream &operator>>(unsigned short& arg) { // NOLINT + getNumber(&arg); + return *this; + } + /** + * Extract a value of type int. + * \param[out] arg location to store the value. + * \return Is always *this. Failure is indicated by the state of *this. + */ + istream &operator>>(int& arg) { + getNumber(&arg); + return *this; + } + /** + * Extract a value of type unsigned int. + * \param[out] arg location to store the value. + * \return Is always *this. Failure is indicated by the state of *this. + */ + istream &operator>>(unsigned int& arg) { + getNumber(&arg); + return *this; + } + /** + * Extract a value of type long. + * \param[out] arg location to store the value. + * \return Is always *this. Failure is indicated by the state of *this. + */ + istream &operator>>(long& arg) { // NOLINT + getNumber(&arg); + return *this; + } + /** + * Extract a value of type unsigned long. + * \param[out] arg location to store the value. + * \return Is always *this. Failure is indicated by the state of *this. + */ + istream &operator>>(unsigned long& arg) { // NOLINT + getNumber(&arg); + return *this; + } + /** + * Extract a value of type double. + * \param[out] arg location to store the value. + * \return Is always *this. Failure is indicated by the state of *this. + */ + istream &operator>> (double& arg) { + getDouble(&arg); + return *this; + } + /** + * Extract a value of type float. + * \param[out] arg location to store the value. + * \return Is always *this. Failure is indicated by the state of *this. + */ + istream &operator>> (float& arg) { + double v; + getDouble(&v); + arg = v; + return *this; + } + /** + * Extract a value of type void*. + * \param[out] arg location to store the value. + * \return Is always *this. Failure is indicated by the state of *this. + */ + istream& operator>> (void*& arg) { + uint32_t val; + getNumber(&val); + arg = reinterpret_cast(val); + return *this; + } + /** + * \return The number of characters extracted by the last unformatted + * input function. + */ + streamsize gcount() const { + return m_gcount; + } + /** + * Extract a character if one is available. + * + * \return The character or -1 if a failure occurs. A failure is indicated + * by the stream state. + */ + int get(); + /** + * Extract a character if one is available. + * + * \param[out] ch location to receive the extracted character. + * + * \return always returns *this. A failure is indicated by the stream state. + */ + istream& get(char& ch); + /** + * Extract characters. + * + * \param[out] str Location to receive extracted characters. + * \param[in] n Size of str. + * \param[in] delim Delimiter + * + * Characters are extracted until extraction fails, n is less than 1, + * n-1 characters are extracted, or the next character equals + * \a delim (delim is not extracted). If no characters are extracted + * failbit is set. If end-of-file occurs the eofbit is set. + * + * \return always returns *this. A failure is indicated by the stream state. + */ + istream& get(char *str, streamsize n, char delim = '\n'); + /** + * Extract characters + * + * \param[out] str Location to receive extracted characters. + * \param[in] n Size of str. + * \param[in] delim Delimiter + * + * Characters are extracted until extraction fails, + * the next character equals \a delim (delim is extracted), or n-1 + * characters are extracted. + * + * The failbit is set if no characters are extracted or n-1 characters + * are extracted. If end-of-file occurs the eofbit is set. + * + * \return always returns *this. A failure is indicated by the stream state. + */ + istream& getline(char *str, streamsize n, char delim = '\n'); + /** + * Extract characters and discard them. + * + * \param[in] n maximum number of characters to ignore. + * \param[in] delim Delimiter. + * + * Characters are extracted until extraction fails, \a n characters + * are extracted, or the next input character equals \a delim + * (the delimiter is extracted). If end-of-file occurs the eofbit is set. + * + * Failures are indicated by the state of the stream. + * + * \return *this + * + */ + istream& ignore(streamsize n = 1, int delim = -1); + /** + * Return the next available character without consuming it. + * + * \return The character if the stream state is good else -1; + * + */ + int peek(); +// istream& read(char *str, streamsize count); +// streamsize readsome(char *str, streamsize count); + /** + * \return the stream position + */ + pos_type tellg() { + return tellpos(); + } + /** + * Set the stream position + * \param[in] pos The absolute position in which to move the read pointer. + * \return Is always *this. Failure is indicated by the state of *this. + */ + istream& seekg(pos_type pos) { + if (!seekpos(pos)) { + setstate(failbit); + } + return *this; + } + /** + * Set the stream position. + * + * \param[in] off An offset to move the read pointer relative to way. + * \a off is a signed 32-bit int so the offset is limited to +- 2GB. + * \param[in] way One of ios::beg, ios::cur, or ios::end. + * \return Is always *this. Failure is indicated by the state of *this. + */ + istream& seekg(off_type off, seekdir way) { + if (!seekoff(off, way)) { + setstate(failbit); + } + return *this; + } + void skipWhite(); + + protected: + /// @cond SHOW_PROTECTED + /** + * Internal - do not use + * \return + */ + virtual int16_t getch() = 0; + /** + * Internal - do not use + * \param[out] pos + * \return + */ + int16_t getch(FatPos_t* pos) { + getpos(pos); + return getch(); + } + /** + * Internal - do not use + * \param[out] pos + */ + virtual void getpos(FatPos_t* pos) = 0; + /** + * Internal - do not use + * \param[in] pos + */ + virtual bool seekoff(off_type off, seekdir way) = 0; + virtual bool seekpos(pos_type pos) = 0; + virtual void setpos(FatPos_t* pos) = 0; + virtual pos_type tellpos() = 0; + + /// @endcond + private: + void getBool(bool *b); + void getChar(char* ch); + bool getDouble(double* value); + template void getNumber(T* value); + bool getNumber(uint32_t posMax, uint32_t negMax, uint32_t* num); + void getStr(char *str); + int16_t readSkip(); + + size_t m_gcount; +}; +//------------------------------------------------------------------------------ +template +void istream::getNumber(T* value) { + uint32_t tmp; + if ((T)-1 < 0) { + // number is signed, max positive value + uint32_t const m = ((uint32_t)-1) >> (33 - sizeof(T) * 8); + // max absolute value of negative number is m + 1. + if (getNumber(m, m + 1, &tmp)) { + *value = (T)tmp; + } + } else { + // max unsigned value for T + uint32_t const m = (T)-1; + if (getNumber(m, m, &tmp)) { + *value = (T)tmp; + } + } +} +#endif // istream_h diff --git a/hardware/_controller/src/SdFat/FatLib/ostream.cpp b/hardware/_controller/src/SdFat/FatLib/ostream.cpp new file mode 100644 index 0000000..5c1a330 --- /dev/null +++ b/hardware/_controller/src/SdFat/FatLib/ostream.cpp @@ -0,0 +1,196 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include +#include "ostream.h" +#ifndef PSTR +#define PSTR(x) x +#endif +//------------------------------------------------------------------------------ +void ostream::do_fill(unsigned len) { + for (; len < width(); len++) { + putch(fill()); + } + width(0); +} +//------------------------------------------------------------------------------ +void ostream::fill_not_left(unsigned len) { + if ((flags() & adjustfield) != left) { + do_fill(len); + } +} +//------------------------------------------------------------------------------ +char* ostream::fmtNum(uint32_t n, char *ptr, uint8_t base) { + char a = flags() & uppercase ? 'A' - 10 : 'a' - 10; + do { + uint32_t m = n; + n /= base; + char c = m - base * n; + *--ptr = c < 10 ? c + '0' : c + a; + } while (n); + return ptr; +} +//------------------------------------------------------------------------------ +void ostream::putBool(bool b) { + if (flags() & boolalpha) { + if (b) { + putPgm(PSTR("true")); + } else { + putPgm(PSTR("false")); + } + } else { + putChar(b ? '1' : '0'); + } +} +//------------------------------------------------------------------------------ +void ostream::putChar(char c) { + fill_not_left(1); + putch(c); + do_fill(1); +} +//------------------------------------------------------------------------------ +void ostream::putDouble(double n) { + uint8_t nd = precision(); + double round = 0.5; + char sign; + char buf[13]; // room for sign, 10 digits, '.', and zero byte + char *end = buf + sizeof(buf) - 1; + char *str = end; + // terminate string + *end = '\0'; + + // get sign and make nonnegative + if (n < 0.0) { + sign = '-'; + n = -n; + } else { + sign = flags() & showpos ? '+' : '\0'; + } + // check for larger than uint32_t + if (n > 4.0E9) { + putPgm(PSTR("BIG FLT")); + return; + } + // round up and separate int and fraction parts + for (uint8_t i = 0; i < nd; ++i) { + round *= 0.1; + } + n += round; + uint32_t intPart = n; + double fractionPart = n - intPart; + + // format intPart and decimal point + if (nd || (flags() & showpoint)) { + *--str = '.'; + } + str = fmtNum(intPart, str, 10); + + // calculate length for fill + uint8_t len = sign ? 1 : 0; + len += nd + end - str; + + // extract adjust field + fmtflags adj = flags() & adjustfield; + if (adj == internal) { + if (sign) { + putch(sign); + } + do_fill(len); + } else { + // do fill for internal or right + fill_not_left(len); + if (sign) { + *--str = sign; + } + } + putstr(str); + // output fraction + while (nd-- > 0) { + fractionPart *= 10.0; + int digit = static_cast(fractionPart); + putch(digit + '0'); + fractionPart -= digit; + } + // do fill if not done above + do_fill(len); +} +//------------------------------------------------------------------------------ +void ostream::putNum(int32_t n) { + bool neg = n < 0 && flagsToBase() == 10; + if (neg) { + n = -n; + } + putNum(n, neg); +} +//------------------------------------------------------------------------------ +void ostream::putNum(uint32_t n, bool neg) { + char buf[13]; + char* end = buf + sizeof(buf) - 1; + char* num; + char* str; + uint8_t base = flagsToBase(); + *end = '\0'; + str = num = fmtNum(n, end, base); + if (base == 10) { + if (neg) { + *--str = '-'; + } else if (flags() & showpos) { + *--str = '+'; + } + } else if (flags() & showbase) { + if (flags() & hex) { + *--str = flags() & uppercase ? 'X' : 'x'; + } + *--str = '0'; + } + uint8_t len = end - str; + fmtflags adj = flags() & adjustfield; + if (adj == internal) { + while (str < num) { + putch(*str++); + } + } + if (adj != left) { + do_fill(len); + } + putstr(str); + do_fill(len); +} +//------------------------------------------------------------------------------ +void ostream::putPgm(const char* str) { + int n; + for (n = 0; pgm_read_byte(&str[n]); n++) {} + fill_not_left(n); + for (uint8_t c; (c = pgm_read_byte(str)); str++) { + putch(c); + } + do_fill(n); +} +//------------------------------------------------------------------------------ +void ostream::putStr(const char *str) { + unsigned n = strlen(str); + fill_not_left(n); + putstr(str); + do_fill(n); +} diff --git a/hardware/_controller/src/SdFat/FatLib/ostream.h b/hardware/_controller/src/SdFat/FatLib/ostream.h new file mode 100644 index 0000000..106898a --- /dev/null +++ b/hardware/_controller/src/SdFat/FatLib/ostream.h @@ -0,0 +1,276 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef ostream_h +#define ostream_h +/** + * \file + * \brief \ref ostream class + */ +#include "ios.h" +//============================================================================== +/** + * \class ostream + * \brief Output Stream + */ +class ostream : public virtual ios { + public: + ostream() {} + + /** call manipulator + * \param[in] pf function to call + * \return the stream + */ + ostream& operator<< (ostream& (*pf)(ostream& str)) { + return pf(*this); + } + /** call manipulator + * \param[in] pf function to call + * \return the stream + */ + ostream& operator<< (ios_base& (*pf)(ios_base& str)) { + pf(*this); + return *this; + } + /** Output bool + * \param[in] arg value to output + * \return the stream + */ + ostream &operator<< (bool arg) { + putBool(arg); + return *this; + } + /** Output string + * \param[in] arg string to output + * \return the stream + */ + ostream &operator<< (const char *arg) { + putStr(arg); + return *this; + } + /** Output string + * \param[in] arg string to output + * \return the stream + */ + ostream &operator<< (const signed char *arg) { + putStr((const char*)arg); + return *this; + } + /** Output string + * \param[in] arg string to output + * \return the stream + */ + ostream &operator<< (const unsigned char *arg) { + putStr((const char*)arg); + return *this; + } + /** Output character + * \param[in] arg character to output + * \return the stream + */ + ostream &operator<< (char arg) { + putChar(arg); + return *this; + } + /** Output character + * \param[in] arg character to output + * \return the stream + */ + ostream &operator<< (signed char arg) { + putChar(static_cast(arg)); + return *this; + } + /** Output character + * \param[in] arg character to output + * \return the stream + */ + ostream &operator<< (unsigned char arg) { + putChar(static_cast(arg)); + return *this; + } + /** Output double + * \param[in] arg value to output + * \return the stream + */ + ostream &operator<< (double arg) { + putDouble(arg); + return *this; + } + /** Output float + * \param[in] arg value to output + * \return the stream + */ + ostream &operator<< (float arg) { + putDouble(arg); + return *this; + } + /** Output signed short + * \param[in] arg value to output + * \return the stream + */ + ostream &operator<< (short arg) { // NOLINT + putNum((int32_t)arg); + return *this; + } + /** Output unsigned short + * \param[in] arg value to output + * \return the stream + */ + ostream &operator<< (unsigned short arg) { // NOLINT + putNum((uint32_t)arg); + return *this; + } + /** Output signed int + * \param[in] arg value to output + * \return the stream + */ + ostream &operator<< (int arg) { + putNum((int32_t)arg); + return *this; + } + /** Output unsigned int + * \param[in] arg value to output + * \return the stream + */ + ostream &operator<< (unsigned int arg) { + putNum((uint32_t)arg); + return *this; + } + /** Output signed long + * \param[in] arg value to output + * \return the stream + */ + ostream &operator<< (long arg) { // NOLINT + putNum((int32_t)arg); + return *this; + } + /** Output unsigned long + * \param[in] arg value to output + * \return the stream + */ + ostream &operator<< (unsigned long arg) { // NOLINT + putNum((uint32_t)arg); + return *this; + } + /** Output pointer + * \param[in] arg value to output + * \return the stream + */ + ostream& operator<< (const void* arg) { + putNum(reinterpret_cast(arg)); + return *this; + } +#if (defined(ARDUINO) && ENABLE_ARDUINO_FEATURES) || defined(DOXYGEN) + /** Output a string from flash using the Arduino F() macro. + * \param[in] arg pointing to flash string + * \return the stream + */ + ostream &operator<< (const __FlashStringHelper *arg) { + putPgm(reinterpret_cast(arg)); + return *this; + } +#endif // (defined(ARDUINO) && ENABLE_ARDUINO_FEATURES) || defined(DOXYGEN) + /** + * Puts a character in a stream. + * + * The unformatted output function inserts the element \a ch. + * It returns *this. + * + * \param[in] ch The character + * \return A reference to the ostream object. + */ + ostream& put(char ch) { + putch(ch); + return *this; + } +// ostream& write(char *str, streamsize count); + /** + * Flushes the buffer associated with this stream. The flush function + * calls the sync function of the associated file. + * \return A reference to the ostream object. + */ + ostream& flush() { + if (!sync()) { + setstate(badbit); + } + return *this; + } + /** + * \return the stream position + */ + pos_type tellp() { + return tellpos(); + } + /** + * Set the stream position + * \param[in] pos The absolute position in which to move the write pointer. + * \return Is always *this. Failure is indicated by the state of *this. + */ + ostream& seekp(pos_type pos) { + if (!seekpos(pos)) { + setstate(failbit); + } + return *this; + } + /** + * Set the stream position. + * + * \param[in] off An offset to move the write pointer relative to way. + * \a off is a signed 32-bit int so the offset is limited to +- 2GB. + * \param[in] way One of ios::beg, ios::cur, or ios::end. + * \return Is always *this. Failure is indicated by the state of *this. + */ + ostream& seekp(off_type off, seekdir way) { + if (!seekoff(off, way)) { + setstate(failbit); + } + return *this; + } + + protected: + /// @cond SHOW_PROTECTED + /** Put character with binary/text conversion + * \param[in] ch character to write + */ + virtual void putch(char ch) = 0; + virtual void putstr(const char *str) = 0; + virtual bool seekoff(off_type pos, seekdir way) = 0; + virtual bool seekpos(pos_type pos) = 0; + virtual bool sync() = 0; + + virtual pos_type tellpos() = 0; + /// @endcond + private: + void do_fill(unsigned len); + void fill_not_left(unsigned len); + char* fmtNum(uint32_t n, char *ptr, uint8_t base); + void putBool(bool b); + void putChar(char c); + void putDouble(double n); + void putNum(uint32_t n, bool neg = false); + void putNum(int32_t n); + void putPgm(const char* str); + void putStr(const char* str); +}; +#endif // ostream_h diff --git a/hardware/_controller/src/SdFat/FreeStack.h b/hardware/_controller/src/SdFat/FreeStack.h new file mode 100644 index 0000000..c655ef6 --- /dev/null +++ b/hardware/_controller/src/SdFat/FreeStack.h @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef FreeStack_h +#define FreeStack_h +/** + * \file + * \brief FreeStack() function. + */ +#if defined(__AVR__) || defined(DOXYGEN) +/** boundary between stack and heap. */ +extern char *__brkval; +/** End of bss section.*/ +extern char __bss_end; +/** Amount of free stack space. + * \return The number of free bytes. + */ +static int FreeStack() { + char* sp = reinterpret_cast(SP); + return __brkval ? sp - __brkval : sp - &__bss_end; +// char top; +// return __brkval ? &top - __brkval : &top - &__bss_end; +} +#elif defined(PLATFORM_ID) // Particle board +static int FreeStack() { + return System.freeMemory(); +} +#elif defined(__arm__) +extern "C" char* sbrk(int incr); +static int FreeStack() { + char top = 't'; + return &top - reinterpret_cast(sbrk(0)); +} +#else +#warning FreeStack is not defined for this system. +static int FreeStack() { + return 0; +} +#endif +#endif // FreeStack_h diff --git a/hardware/_controller/src/SdFat/MinimumSerial.cpp b/hardware/_controller/src/SdFat/MinimumSerial.cpp new file mode 100644 index 0000000..e0d4fb2 --- /dev/null +++ b/hardware/_controller/src/SdFat/MinimumSerial.cpp @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "SysCall.h" +#if defined(UDR0) || defined(DOXYGEN) +#include "MinimumSerial.h" +const uint16_t MIN_2X_BAUD = F_CPU/(4*(2*0XFFF + 1)) + 1; +//------------------------------------------------------------------------------ +int MinimumSerial::available() { + return UCSR0A & (1 << RXC0) ? 1 : 0; +} +//------------------------------------------------------------------------------ +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) && baud > MIN_2X_BAUD) { + // Double the USART Transmission Speed + UCSR0A = 1 << U2X0; + baud_setting = (F_CPU / 4 / baud - 1) / 2; + } else { + // hardcoded exception for compatibility with the bootloader shipped + // with the Duemilanove and previous boards and the firmware on the 8U2 + // on the Uno and Mega 2560. + UCSR0A = 0; + baud_setting = (F_CPU / 8 / baud - 1) / 2; + } + // assign the baud_setting + UBRR0H = baud_setting >> 8; + UBRR0L = baud_setting; + // enable transmit and receive + UCSR0B |= (1 << TXEN0) | (1 << RXEN0); +} +//------------------------------------------------------------------------------ +void MinimumSerial::flush() { + while (((1 << UDRIE0) & UCSR0B) || !(UCSR0A & (1 << UDRE0))) {} +} +//------------------------------------------------------------------------------ +int MinimumSerial::read() { + if (UCSR0A & (1 << RXC0)) { + return UDR0; + } + return -1; +} +//------------------------------------------------------------------------------ +size_t MinimumSerial::write(uint8_t b) { + while (((1 << UDRIE0) & UCSR0B) || !(UCSR0A & (1 << UDRE0))) {} + UDR0 = b; + return 1; +} +#endif // defined(UDR0) || defined(DOXYGEN) diff --git a/hardware/_controller/src/SdFat/MinimumSerial.h b/hardware/_controller/src/SdFat/MinimumSerial.h new file mode 100644 index 0000000..0a0f667 --- /dev/null +++ b/hardware/_controller/src/SdFat/MinimumSerial.h @@ -0,0 +1,67 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + /** + * \file + * \brief Minimal AVR Serial driver. + */ +#ifndef MinimumSerial_h +#define MinimumSerial_h +#include "SysCall.h" +//============================================================================== +/** + * \class MinimumSerial + * \brief mini serial class for the %SdFat library. + */ +class MinimumSerial : public Print { + public: + /** \return true for hardware serial */ + operator bool() { return true; } + /** + * \return one if data is available. + */ + int available(); + /** + * 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); + /** Wait for write done. */ + void flush(); + /** + * 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; +}; +#endif // MinimumSerial_h diff --git a/hardware/_controller/src/SdFat/SdCard/SdInfo.h b/hardware/_controller/src/SdFat/SdCard/SdInfo.h new file mode 100644 index 0000000..3857941 --- /dev/null +++ b/hardware/_controller/src/SdFat/SdCard/SdInfo.h @@ -0,0 +1,485 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef SdInfo_h +#define SdInfo_h +#include +// Based on the document: +// +// SD Specifications +// Part 1 +// Physical Layer +// Simplified Specification +// Version 5.00 +// Aug 10, 2016 +// +// https://www.sdcard.org/downloads/pls/ +//------------------------------------------------------------------------------ +// SD card errors +// See the SD Specification for command info. +typedef enum { + SD_CARD_ERROR_NONE = 0, + + // Basic commands and switch command. + SD_CARD_ERROR_CMD0 = 0X20, + SD_CARD_ERROR_CMD2, + SD_CARD_ERROR_CMD3, + SD_CARD_ERROR_CMD6, + SD_CARD_ERROR_CMD7, + SD_CARD_ERROR_CMD8, + SD_CARD_ERROR_CMD9, + SD_CARD_ERROR_CMD10, + SD_CARD_ERROR_CMD12, + SD_CARD_ERROR_CMD13, + + // Read, write, erase, and extension commands. + SD_CARD_ERROR_CMD17 = 0X30, + SD_CARD_ERROR_CMD18, + SD_CARD_ERROR_CMD24, + SD_CARD_ERROR_CMD25, + SD_CARD_ERROR_CMD32, + SD_CARD_ERROR_CMD33, + SD_CARD_ERROR_CMD38, + SD_CARD_ERROR_CMD58, + SD_CARD_ERROR_CMD59, + + // Application specific commands. + SD_CARD_ERROR_ACMD6 = 0X40, + SD_CARD_ERROR_ACMD13, + SD_CARD_ERROR_ACMD23, + SD_CARD_ERROR_ACMD41, + + // Read/write errors + SD_CARD_ERROR_READ = 0X50, + SD_CARD_ERROR_READ_CRC, + SD_CARD_ERROR_READ_FIFO, + SD_CARD_ERROR_READ_REG, + SD_CARD_ERROR_READ_START, + SD_CARD_ERROR_READ_TIMEOUT, + SD_CARD_ERROR_STOP_TRAN, + SD_CARD_ERROR_WRITE, + SD_CARD_ERROR_WRITE_FIFO, + SD_CARD_ERROR_WRITE_START, + SD_CARD_ERROR_FLASH_PROGRAMMING, + SD_CARD_ERROR_WRITE_TIMEOUT, + + // Misc errors. + SD_CARD_ERROR_DMA = 0X60, + SD_CARD_ERROR_ERASE, + SD_CARD_ERROR_ERASE_SINGLE_BLOCK, + SD_CARD_ERROR_ERASE_TIMEOUT, + SD_CARD_ERROR_INIT_NOT_CALLED, + SD_CARD_ERROR_FUNCTION_NOT_SUPPORTED +} sd_error_code_t; +//------------------------------------------------------------------------------ +// card types +/** Standard capacity V1 SD card */ +const uint8_t SD_CARD_TYPE_SD1 = 1; +/** Standard capacity V2 SD card */ +const uint8_t SD_CARD_TYPE_SD2 = 2; +/** High Capacity SD card */ +const uint8_t SD_CARD_TYPE_SDHC = 3; +//------------------------------------------------------------------------------ +#define SD_SCK_HZ(maxSpeed) SPISettings(maxSpeed, MSBFIRST, SPI_MODE0) +#define SD_SCK_MHZ(maxMhz) SPISettings(1000000UL*maxMhz, MSBFIRST, SPI_MODE0) +// SPI divisor constants +/** Set SCK to max rate of F_CPU/2. */ +#define SPI_FULL_SPEED SD_SCK_MHZ(50) +/** Set SCK rate to F_CPU/3 for Due */ +#define SPI_DIV3_SPEED SD_SCK_HZ(F_CPU/3) +/** Set SCK rate to F_CPU/4. */ +#define SPI_HALF_SPEED SD_SCK_HZ(F_CPU/4) +/** Set SCK rate to F_CPU/6 for Due */ +#define SPI_DIV6_SPEED SD_SCK_HZ(F_CPU/6) +/** Set SCK rate to F_CPU/8. */ +#define SPI_QUARTER_SPEED SD_SCK_HZ(F_CPU/8) +/** Set SCK rate to F_CPU/16. */ +#define SPI_EIGHTH_SPEED SD_SCK_HZ(F_CPU/16) +/** Set SCK rate to F_CPU/32. */ +#define SPI_SIXTEENTH_SPEED SD_SCK_HZ(F_CPU/32) +//------------------------------------------------------------------------------ +// SD operation timeouts +/** CMD0 retry count */ +const uint8_t SD_CMD0_RETRY = 10; +/** command timeout ms */ +const uint16_t SD_CMD_TIMEOUT = 300; +/** init timeout ms */ +const uint16_t SD_INIT_TIMEOUT = 2000; +/** erase timeout ms */ +const uint16_t SD_ERASE_TIMEOUT = 10000; +/** read timeout ms */ +const uint16_t SD_READ_TIMEOUT = 1000; +/** write time out ms */ +const uint16_t SD_WRITE_TIMEOUT = 2000; +//------------------------------------------------------------------------------ +// SD card commands +/** GO_IDLE_STATE - init card in spi mode if CS low */ +const uint8_t CMD0 = 0X00; +/** ALL_SEND_CID - Asks any card to send the CID. */ +const uint8_t CMD2 = 0X02; +/** SEND_RELATIVE_ADDR - Ask the card to publish a new RCA. */ +const uint8_t CMD3 = 0X03; +/** SWITCH_FUNC - Switch Function Command */ +const uint8_t CMD6 = 0X06; +/** SELECT/DESELECT_CARD - toggles between the stand-by and transfer states. */ +const uint8_t CMD7 = 0X07; +/** SEND_IF_COND - verify SD Memory Card interface operating condition.*/ +const uint8_t CMD8 = 0X08; +/** SEND_CSD - read the Card Specific Data (CSD register) */ +const uint8_t CMD9 = 0X09; +/** SEND_CID - read the card identification information (CID register) */ +const uint8_t CMD10 = 0X0A; +/** STOP_TRANSMISSION - end multiple block read sequence */ +const uint8_t CMD12 = 0X0C; +/** SEND_STATUS - read the card status register */ +const uint8_t CMD13 = 0X0D; +/** READ_SINGLE_BLOCK - read a single data block from the card */ +const uint8_t CMD17 = 0X11; +/** READ_MULTIPLE_BLOCK - read a multiple data blocks from the card */ +const uint8_t CMD18 = 0X12; +/** WRITE_BLOCK - write a single data block to the card */ +const uint8_t CMD24 = 0X18; +/** WRITE_MULTIPLE_BLOCK - write blocks of data until a STOP_TRANSMISSION */ +const uint8_t CMD25 = 0X19; +/** ERASE_WR_BLK_START - sets the address of the first block to be erased */ +const uint8_t CMD32 = 0X20; +/** ERASE_WR_BLK_END - sets the address of the last block of the continuous + range to be erased*/ +const uint8_t CMD33 = 0X21; +/** ERASE - erase all previously selected blocks */ +const uint8_t CMD38 = 0X26; +/** APP_CMD - escape for application specific command */ +const uint8_t CMD55 = 0X37; +/** READ_OCR - read the OCR register of a card */ +const uint8_t CMD58 = 0X3A; +/** CRC_ON_OFF - enable or disable CRC checking */ +const uint8_t CMD59 = 0X3B; +/** SET_BUS_WIDTH - Defines the data bus width for data transfer. */ +const uint8_t ACMD6 = 0X06; +/** SD_STATUS - Send the SD Status. */ +const uint8_t ACMD13 = 0X0D; +/** SET_WR_BLK_ERASE_COUNT - Set the number of write blocks to be + pre-erased before writing */ +const uint8_t ACMD23 = 0X17; +/** SD_SEND_OP_COMD - Sends host capacity support information and + activates the card's initialization process */ +const uint8_t ACMD41 = 0X29; +//============================================================================== +// CARD_STATUS +/** The command's argument was out of the allowed range for this card. */ +const uint32_t CARD_STATUS_OUT_OF_RANGE = 1UL << 31; +/** A misaligned address which did not match the block length. */ +const uint32_t CARD_STATUS_ADDRESS_ERROR = 1UL << 30; +/** The transferred block length is not allowed for this card. */ +const uint32_t CARD_STATUS_BLOCK_LEN_ERROR = 1UL << 29; +/** An error in the sequence of erase commands occurred. */ +const uint32_t CARD_STATUS_ERASE_SEQ_ERROR = 1UL <<28; +/** An invalid selection of write-blocks for erase occurred. */ +const uint32_t CARD_STATUS_ERASE_PARAM = 1UL << 27; +/** Set when the host attempts to write to a protected block. */ +const uint32_t CARD_STATUS_WP_VIOLATION = 1UL << 26; +/** When set, signals that the card is locked by the host. */ +const uint32_t CARD_STATUS_CARD_IS_LOCKED = 1UL << 25; +/** Set when a sequence or password error has been detected. */ +const uint32_t CARD_STATUS_LOCK_UNLOCK_FAILED = 1UL << 24; +/** The CRC check of the previous command failed. */ +const uint32_t CARD_STATUS_COM_CRC_ERROR = 1UL << 23; +/** Command not legal for the card state. */ +const uint32_t CARD_STATUS_ILLEGAL_COMMAND = 1UL << 22; +/** Card internal ECC was applied but failed to correct the data. */ +const uint32_t CARD_STATUS_CARD_ECC_FAILED = 1UL << 21; +/** Internal card controller error */ +const uint32_t CARD_STATUS_CC_ERROR = 1UL << 20; +/** A general or an unknown error occurred during the operation. */ +const uint32_t CARD_STATUS_ERROR = 1UL << 19; +// bits 19, 18, and 17 reserved. +/** Permanent WP set or attempt to change read only values of CSD. */ +const uint32_t CARD_STATUS_CSD_OVERWRITE = 1UL <<16; +/** partial address space was erased due to write protect. */ +const uint32_t CARD_STATUS_WP_ERASE_SKIP = 1UL << 15; +/** The command has been executed without using the internal ECC. */ +const uint32_t CARD_STATUS_CARD_ECC_DISABLED = 1UL << 14; +/** out of erase sequence command was received. */ +const uint32_t CARD_STATUS_ERASE_RESET = 1UL << 13; +/** The state of the card when receiving the command. + * 0 = idle + * 1 = ready + * 2 = ident + * 3 = stby + * 4 = tran + * 5 = data + * 6 = rcv + * 7 = prg + * 8 = dis + * 9-14 = reserved + * 15 = reserved for I/O mode + */ +const uint32_t CARD_STATUS_CURRENT_STATE = 0XF << 9; +/** Shift for current state. */ +const uint32_t CARD_STATUS_CURRENT_STATE_SHIFT = 9; +/** Corresponds to buffer empty signaling on the bus. */ +const uint32_t CARD_STATUS_READY_FOR_DATA = 1UL << 8; +// bit 7 reserved. +/** Extension Functions may set this bit to get host to deal with events. */ +const uint32_t CARD_STATUS_FX_EVENT = 1UL << 6; +/** The card will expect ACMD, or the command has been interpreted as ACMD */ +const uint32_t CARD_STATUS_APP_CMD = 1UL << 5; +// bit 4 reserved. +/** Error in the sequence of the authentication process. */ +const uint32_t CARD_STATUS_AKE_SEQ_ERROR = 1UL << 3; +// bits 2,1, and 0 reserved for manufacturer test mode. +//============================================================================== +/** status for card in the ready state */ +const uint8_t R1_READY_STATE = 0X00; +/** status for card in the idle state */ +const uint8_t R1_IDLE_STATE = 0X01; +/** status bit for illegal command */ +const uint8_t R1_ILLEGAL_COMMAND = 0X04; +/** start data token for read or write single block*/ +const uint8_t DATA_START_BLOCK = 0XFE; +/** stop token for write multiple blocks*/ +const uint8_t STOP_TRAN_TOKEN = 0XFD; +/** start data token for write multiple blocks*/ +const uint8_t WRITE_MULTIPLE_TOKEN = 0XFC; +/** mask for data response tokens after a write block operation */ +const uint8_t DATA_RES_MASK = 0X1F; +/** write data accepted token */ +const uint8_t DATA_RES_ACCEPTED = 0X05; +//============================================================================== +/** + * \class CID + * \brief Card IDentification (CID) register. + */ +typedef struct CID { + // byte 0 + /** Manufacturer ID */ + unsigned char mid; + // byte 1-2 + /** OEM/Application ID */ + char oid[2]; + // byte 3-7 + /** Product name */ + char pnm[5]; + // byte 8 + /** Product revision least significant digit */ + unsigned char prv_m : 4; + /** Product revision most significant digit */ + unsigned char prv_n : 4; + // byte 9-12 + /** Product serial number */ + uint32_t psn; + // byte 13 + /** Manufacturing date year low digit */ + unsigned char mdt_year_high : 4; + /** not used */ + unsigned char reserved : 4; + // byte 14 + /** Manufacturing date month */ + unsigned char mdt_month : 4; + /** Manufacturing date year low digit */ + unsigned char mdt_year_low : 4; + // byte 15 + /** not used always 1 */ + unsigned char always1 : 1; + /** CRC7 checksum */ + unsigned char crc : 7; +} __attribute__((packed)) cid_t; + +//============================================================================== +/** + * \class CSDV1 + * \brief CSD register for version 1.00 cards . + */ +typedef struct CSDV1 { + // byte 0 + unsigned char reserved1 : 6; + unsigned char csd_ver : 2; + // byte 1 + unsigned char taac; + // byte 2 + unsigned char nsac; + // byte 3 + unsigned char tran_speed; + // byte 4 + unsigned char ccc_high; + // byte 5 + unsigned char read_bl_len : 4; + unsigned char ccc_low : 4; + // byte 6 + unsigned char c_size_high : 2; + unsigned char reserved2 : 2; + unsigned char dsr_imp : 1; + unsigned char read_blk_misalign : 1; + unsigned char write_blk_misalign : 1; + unsigned char read_bl_partial : 1; + // byte 7 + unsigned char c_size_mid; + // byte 8 + unsigned char vdd_r_curr_max : 3; + unsigned char vdd_r_curr_min : 3; + unsigned char c_size_low : 2; + // byte 9 + unsigned char c_size_mult_high : 2; + unsigned char vdd_w_cur_max : 3; + unsigned char vdd_w_curr_min : 3; + // byte 10 + unsigned char sector_size_high : 6; + unsigned char erase_blk_en : 1; + unsigned char c_size_mult_low : 1; + // byte 11 + unsigned char wp_grp_size : 7; + unsigned char sector_size_low : 1; + // byte 12 + unsigned char write_bl_len_high : 2; + unsigned char r2w_factor : 3; + unsigned char reserved3 : 2; + unsigned char wp_grp_enable : 1; + // byte 13 + unsigned char reserved4 : 5; + unsigned char write_partial : 1; + unsigned char write_bl_len_low : 2; + // byte 14 + unsigned char reserved5: 2; + unsigned char file_format : 2; + unsigned char tmp_write_protect : 1; + unsigned char perm_write_protect : 1; + unsigned char copy : 1; + /** Indicates the file format on the card */ + unsigned char file_format_grp : 1; + // byte 15 + unsigned char always1 : 1; + unsigned char crc : 7; +} __attribute__((packed)) csd1_t; +//============================================================================== +/** + * \class CSDV2 + * \brief CSD register for version 2.00 cards. + */ +typedef struct CSDV2 { + // byte 0 + unsigned char reserved1 : 6; + unsigned char csd_ver : 2; + // byte 1 + /** fixed to 0X0E */ + unsigned char taac; + // byte 2 + /** fixed to 0 */ + unsigned char nsac; + // byte 3 + unsigned char tran_speed; + // byte 4 + unsigned char ccc_high; + // byte 5 + /** This field is fixed to 9h, which indicates READ_BL_LEN=512 Byte */ + unsigned char read_bl_len : 4; + unsigned char ccc_low : 4; + // byte 6 + /** not used */ + unsigned char reserved2 : 4; + unsigned char dsr_imp : 1; + /** fixed to 0 */ + unsigned char read_blk_misalign : 1; + /** fixed to 0 */ + unsigned char write_blk_misalign : 1; + /** fixed to 0 - no partial read */ + unsigned char read_bl_partial : 1; + // byte 7 + /** high part of card size */ + unsigned char c_size_high : 6; + /** not used */ + unsigned char reserved3 : 2; + // byte 8 + /** middle part of card size */ + unsigned char c_size_mid; + // byte 9 + /** low part of card size */ + unsigned char c_size_low; + // byte 10 + /** sector size is fixed at 64 KB */ + unsigned char sector_size_high : 6; + /** fixed to 1 - erase single is supported */ + unsigned char erase_blk_en : 1; + /** not used */ + unsigned char reserved4 : 1; + // byte 11 + unsigned char wp_grp_size : 7; + /** sector size is fixed at 64 KB */ + unsigned char sector_size_low : 1; + // byte 12 + /** write_bl_len fixed for 512 byte blocks */ + unsigned char write_bl_len_high : 2; + /** fixed value of 2 */ + unsigned char r2w_factor : 3; + /** not used */ + unsigned char reserved5 : 2; + /** fixed value of 0 - no write protect groups */ + unsigned char wp_grp_enable : 1; + // byte 13 + unsigned char reserved6 : 5; + /** always zero - no partial block read*/ + unsigned char write_partial : 1; + /** write_bl_len fixed for 512 byte blocks */ + unsigned char write_bl_len_low : 2; + // byte 14 + unsigned char reserved7: 2; + /** Do not use always 0 */ + unsigned char file_format : 2; + unsigned char tmp_write_protect : 1; + unsigned char perm_write_protect : 1; + unsigned char copy : 1; + /** Do not use always 0 */ + unsigned char file_format_grp : 1; + // byte 15 + /** not used always 1 */ + unsigned char always1 : 1; + /** checksum */ + unsigned char crc : 7; +} __attribute__((packed)) csd2_t; +//============================================================================== +/** + * \class csd_t + * \brief Union of old and new style CSD register. + */ +union csd_t { + csd1_t v1; + csd2_t v2; +}; +//----------------------------------------------------------------------------- +inline uint32_t sdCardCapacity(csd_t* csd) { + if (csd->v1.csd_ver == 0) { + uint8_t read_bl_len = csd->v1.read_bl_len; + uint16_t c_size = (csd->v1.c_size_high << 10) + | (csd->v1.c_size_mid << 2) | csd->v1.c_size_low; + uint8_t c_size_mult = (csd->v1.c_size_mult_high << 1) + | csd->v1.c_size_mult_low; + return (uint32_t)(c_size + 1) << (c_size_mult + read_bl_len - 7); + } else if (csd->v2.csd_ver == 1) { + uint32_t c_size = 0X10000L * csd->v2.c_size_high + 0X100L + * (uint32_t)csd->v2.c_size_mid + csd->v2.c_size_low; + return (c_size + 1) << 10; + } else { + return 0; + } +} +#endif // SdInfo_h diff --git a/hardware/_controller/src/SdFat/SdCard/SdSpiCard.cpp b/hardware/_controller/src/SdFat/SdCard/SdSpiCard.cpp new file mode 100644 index 0000000..fa1cae7 --- /dev/null +++ b/hardware/_controller/src/SdFat/SdCard/SdSpiCard.cpp @@ -0,0 +1,802 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "SdSpiCard.h" +//============================================================================== +// debug trace macro +#define SD_TRACE(m, b) +// #define SD_TRACE(m, b) Serial.print(m);Serial.println(b); +#define SD_CS_DBG(m) +// #define SD_CS_DBG(m) Serial.println(F(m)); + +#define DBG_PROFILE_STATS 0 +#if DBG_PROFILE_STATS + +#define DBG_TAG_LIST\ + DBG_TAG(DBG_CMD0_TIME, "CMD0 time")\ + DBG_TAG(DBG_ACMD41_TIME, "ACMD41 time")\ + DBG_TAG(DBG_CMD_BUSY, "cmd busy")\ + DBG_TAG(DBG_ERASE_BUSY, "erase busy")\ + DBG_TAG(DBG_WAIT_READ, "wait read")\ + DBG_TAG(DBG_WRITE_FLASH, "write flash")\ + DBG_TAG(DBG_WRITE_BUSY, "write busy")\ + DBG_TAG(DBG_WRITE_STOP, "write stop")\ + DBG_TAG(DBG_ACMD41_COUNT, "ACMD41 count")\ + DBG_TAG(DBG_CMD0_COUNT, "CMD0 count") + +#define DBG_TIME_DIM DBG_ACMD41_COUNT + +enum DbgTag { + #define DBG_TAG(tag, str) tag, + DBG_TAG_LIST + DBG_COUNT_DIM + #undef DBG_TAG +}; + +static uint32_t dbgCount[DBG_COUNT_DIM]; +static uint32_t dbgBgnTime[DBG_TIME_DIM]; +static uint32_t dbgMaxTime[DBG_TIME_DIM]; +static uint32_t dbgMinTime[DBG_TIME_DIM]; +static uint32_t dbgTotalTime[DBG_TIME_DIM]; +//------------------------------------------------------------------------------ +static void dbgBeginTime(DbgTag tag) { + dbgBgnTime[tag] = micros(); +} +//------------------------------------------------------------------------------ +static void dbgClearStats() { + for (int i = 0; i < DBG_COUNT_DIM; i++) { + dbgCount[i] = 0; + if (i < DBG_TIME_DIM) { + dbgMaxTime[i] = 0; + dbgMinTime[i] = 9999999; + dbgTotalTime[i] = 0; + } + } +} +//------------------------------------------------------------------------------ +static void dbgEndTime(DbgTag tag) { + uint32_t m = micros() - dbgBgnTime[tag]; + dbgTotalTime[tag] += m; + if (m > dbgMaxTime[tag]) { + dbgMaxTime[tag] = m; + } + if (m < dbgMinTime[tag]) { + dbgMinTime[tag] = m; + } + dbgCount[tag]++; +} +//------------------------------------------------------------------------------ +static void dbgEventCount(DbgTag tag) { + dbgCount[tag]++; +} +//------------------------------------------------------------------------------ +static void dbgPrintTagText(uint8_t tag) { + #define DBG_TAG(e, m) case e: Serial.print(F(m)); break; + switch (tag) { + DBG_TAG_LIST + } + #undef DBG_TAG +} +//------------------------------------------------------------------------------ +static void dbgPrintStats() { + Serial.println(); + Serial.println(F("=======================")); + Serial.println(F("item,event,min,max,avg")); + Serial.println(F("tag,count,usec,usec,usec")); + for (int i = 0; i < DBG_COUNT_DIM; i++) { + if (dbgCount[i]) { + dbgPrintTagText(i); + Serial.print(','); + Serial.print(dbgCount[i]); + if (i < DBG_TIME_DIM) { + Serial.print(','); + Serial.print(dbgMinTime[i]); + Serial.print(','); + Serial.print(dbgMaxTime[i]); + Serial.print(','); + Serial.print(dbgTotalTime[i]/dbgCount[i]); + } + Serial.println(); + } + } + Serial.println(F("=======================")); + Serial.println(); +} +#undef DBG_TAG_LIST +#define DBG_BEGIN_TIME(tag) dbgBeginTime(tag) +#define DBG_END_TIME(tag) dbgEndTime(tag) +#define DBG_EVENT_COUNT(tag) dbgEventCount(tag) +#else // DBG_PROFILE_STATS +#define DBG_BEGIN_TIME(tag) +#define DBG_END_TIME(tag) +#define DBG_EVENT_COUNT(tag) +static void dbgClearStats() {} +static void dbgPrintStats() {} +#endif // DBG_PROFILE_STATS +//============================================================================== +#if USE_SD_CRC +// CRC functions +//------------------------------------------------------------------------------ +static uint8_t CRC7(const uint8_t* data, uint8_t n) { + uint8_t crc = 0; + for (uint8_t i = 0; i < n; i++) { + uint8_t d = data[i]; + for (uint8_t j = 0; j < 8; j++) { + crc <<= 1; + if ((d & 0x80) ^ (crc & 0x80)) { + crc ^= 0x09; + } + d <<= 1; + } + } + return (crc << 1) | 1; +} +//------------------------------------------------------------------------------ +#if USE_SD_CRC == 1 +// Shift based CRC-CCITT +// uses the x^16,x^12,x^5,x^1 polynomial. +static uint16_t CRC_CCITT(const uint8_t *data, size_t n) { + uint16_t crc = 0; + for (size_t i = 0; i < n; i++) { + crc = (uint8_t)(crc >> 8) | (crc << 8); + crc ^= data[i]; + crc ^= (uint8_t)(crc & 0xff) >> 4; + crc ^= crc << 12; + crc ^= (crc & 0xff) << 5; + } + return crc; +} +#elif USE_SD_CRC > 1 // CRC_CCITT +//------------------------------------------------------------------------------ +// Table based CRC-CCITT +// uses the x^16,x^12,x^5,x^1 polynomial. +#ifdef __AVR__ +static const uint16_t crctab[] PROGMEM = { +#else // __AVR__ +static const uint16_t crctab[] = { +#endif // __AVR__ + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, + 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, + 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, + 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, + 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, + 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, + 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, + 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, + 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, + 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, + 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, + 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, + 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, + 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, + 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, + 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, + 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, + 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, + 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, + 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, + 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, + 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, + 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 +}; +static uint16_t CRC_CCITT(const uint8_t* data, size_t n) { + uint16_t crc = 0; + for (size_t i = 0; i < n; i++) { +#ifdef __AVR__ + crc = pgm_read_word(&crctab[(crc >> 8 ^ data[i]) & 0XFF]) ^ (crc << 8); +#else // __AVR__ + crc = crctab[(crc >> 8 ^ data[i]) & 0XFF] ^ (crc << 8); +#endif // __AVR__ + } + return crc; +} +#endif // CRC_CCITT +#endif // USE_SD_CRC +//============================================================================== +// SdSpiCard member functions +//------------------------------------------------------------------------------ +bool SdSpiCard::begin(SdSpiDriver* spi, uint8_t csPin, SPISettings settings) { + m_spiActive = false; + m_errorCode = SD_CARD_ERROR_NONE; + m_type = 0; + m_spiDriver = spi; + uint16_t t0 = curTimeMS(); + uint32_t arg; + + m_spiDriver->begin(csPin); + m_spiDriver->setSpiSettings(SD_SCK_HZ(250000)); + spiStart(); + + // must supply min of 74 clock cycles with CS high. + spiUnselect(); + for (uint8_t i = 0; i < 10; i++) { + spiSend(0XFF); + } + spiSelect(); + + DBG_BEGIN_TIME(DBG_CMD0_TIME); + // command to go idle in SPI mode + for (uint8_t i = 1;; i++) { + DBG_EVENT_COUNT(DBG_CMD0_COUNT); + if (cardCommand(CMD0, 0) == R1_IDLE_STATE) { + break; + } + if (i == SD_CMD0_RETRY) { + error(SD_CARD_ERROR_CMD0); + goto fail; + } + // stop multi-block write + spiSend(STOP_TRAN_TOKEN); + // finish block transfer + for (int i = 0; i < 520; i++) { + spiReceive(); + } + } + DBG_END_TIME(DBG_CMD0_TIME); +#if USE_SD_CRC + if (cardCommand(CMD59, 1) != R1_IDLE_STATE) { + error(SD_CARD_ERROR_CMD59); + goto fail; + } +#endif // USE_SD_CRC + // check SD version + if (cardCommand(CMD8, 0x1AA) == (R1_ILLEGAL_COMMAND | R1_IDLE_STATE)) { + type(SD_CARD_TYPE_SD1); + } else { + for (uint8_t i = 0; i < 4; i++) { + m_status = spiReceive(); + } + if (m_status == 0XAA) { + type(SD_CARD_TYPE_SD2); + } else { + error(SD_CARD_ERROR_CMD8); + goto fail; + } + } + // initialize card and send host supports SDHC if SD2 + arg = type() == SD_CARD_TYPE_SD2 ? 0X40000000 : 0; + DBG_BEGIN_TIME(DBG_ACMD41_TIME); + while (cardAcmd(ACMD41, arg) != R1_READY_STATE) { + DBG_EVENT_COUNT(DBG_ACMD41_COUNT); + // check for timeout + if (isTimedOut(t0, SD_INIT_TIMEOUT)) { + error(SD_CARD_ERROR_ACMD41); + goto fail; + } + } + DBG_END_TIME(DBG_ACMD41_TIME); + // if SD2 read OCR register to check for SDHC card + if (type() == SD_CARD_TYPE_SD2) { + if (cardCommand(CMD58, 0)) { + error(SD_CARD_ERROR_CMD58); + goto fail; + } + 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++) { + spiReceive(); + } + } + spiStop(); + m_spiDriver->setSpiSettings(settings); + return true; + +fail: + spiStop(); + return false; +} +//------------------------------------------------------------------------------ +// send command and return error code. Return zero for OK +uint8_t SdSpiCard::cardCommand(uint8_t cmd, uint32_t arg) { + // select card + if (!m_spiActive) { + spiStart(); + } + // wait if busy unless CMD0 + if (cmd != CMD0) { + DBG_BEGIN_TIME(DBG_CMD_BUSY); + waitNotBusy(SD_CMD_TIMEOUT); + DBG_END_TIME(DBG_CMD_BUSY); + } + +#if USE_SD_CRC + // form message + uint8_t buf[6]; + buf[0] = (uint8_t)0x40U | cmd; + buf[1] = (uint8_t)(arg >> 24U); + buf[2] = (uint8_t)(arg >> 16U); + buf[3] = (uint8_t)(arg >> 8U); + buf[4] = (uint8_t)arg; + + // add CRC + buf[5] = CRC7(buf, 5); + + // send message + spiSend(buf, 6); +#else // USE_SD_CRC + // send command + spiSend(cmd | 0x40); + + // send argument + uint8_t *pa = reinterpret_cast(&arg); + for (int8_t i = 3; i >= 0; i--) { + spiSend(pa[i]); + } + // send CRC - correct for CMD0 with arg zero or CMD8 with arg 0X1AA + spiSend(cmd == CMD0 ? 0X95 : 0X87); +#endif // USE_SD_CRC + + // discard first fill byte to avoid MISO pull-up problem. + spiReceive(); + + // there are 1-8 fill bytes before response. fill bytes should be 0XFF. + for (uint8_t i = 0; ((m_status = spiReceive()) & 0X80) && i < 10; i++) { + } + return m_status; +} +//------------------------------------------------------------------------------ +uint32_t SdSpiCard::cardCapacity() { + csd_t csd; + return readCSD(&csd) ? sdCardCapacity(&csd) : 0; +} +//------------------------------------------------------------------------------ +void SdSpiCard::dbgClearStats() {::dbgClearStats();} +//------------------------------------------------------------------------------ +void SdSpiCard::dbgPrintStats() {::dbgPrintStats();} +//------------------------------------------------------------------------------ +bool SdSpiCard::erase(uint32_t firstBlock, uint32_t lastBlock) { + csd_t csd; + if (!readCSD(&csd)) { + goto fail; + } + // check for single block erase + if (!csd.v1.erase_blk_en) { + // erase size mask + uint8_t m = (csd.v1.sector_size_high << 1) | csd.v1.sector_size_low; + if ((firstBlock & m) != 0 || ((lastBlock + 1) & m) != 0) { + // error card can't erase specified area + error(SD_CARD_ERROR_ERASE_SINGLE_BLOCK); + goto fail; + } + } + if (m_type != SD_CARD_TYPE_SDHC) { + firstBlock <<= 9; + lastBlock <<= 9; + } + if (cardCommand(CMD32, firstBlock) + || cardCommand(CMD33, lastBlock) + || cardCommand(CMD38, 0)) { + error(SD_CARD_ERROR_ERASE); + goto fail; + } + DBG_BEGIN_TIME(DBG_ERASE_BUSY); + if (!waitNotBusy(SD_ERASE_TIMEOUT)) { + error(SD_CARD_ERROR_ERASE_TIMEOUT); + goto fail; + } + DBG_END_TIME(DBG_ERASE_BUSY); + spiStop(); + return true; + +fail: + spiStop(); + return false; +} +//------------------------------------------------------------------------------ +bool SdSpiCard::eraseSingleBlockEnable() { + csd_t csd; + return readCSD(&csd) ? csd.v1.erase_blk_en : false; +} +//------------------------------------------------------------------------------ +bool SdSpiCard::isBusy() { + bool rtn = true; + bool spiActive = m_spiActive; + if (!spiActive) { + spiStart(); + } + for (uint8_t i = 0; i < 8; i++) { + if (0XFF == spiReceive()) { + rtn = false; + break; + } + } + if (!spiActive) { + spiStop(); + } + return rtn; +} +//------------------------------------------------------------------------------ +bool SdSpiCard::isTimedOut(uint16_t startMS, uint16_t timeoutMS) { +#if WDT_YIELD_TIME_MICROS + static uint32_t last; + if ((micros() - last) > WDT_YIELD_TIME_MICROS) { + SysCall::yield(); + last = micros(); + } +#endif // WDT_YIELD_TIME_MICROS + return (curTimeMS() - startMS) > timeoutMS; +} +//------------------------------------------------------------------------------ +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 (cardCommand(CMD17, blockNumber)) { + error(SD_CARD_ERROR_CMD17); + goto fail; + } + if (!readData(dst, 512)) { + goto fail; + } + spiStop(); + return true; + +fail: + spiStop(); + return false; +} +//------------------------------------------------------------------------------ +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, 512)) { + return false; + } + } + return readStop(); +} +//------------------------------------------------------------------------------ +bool SdSpiCard::readData(uint8_t *dst) { + return readData(dst, 512); +} +//------------------------------------------------------------------------------ +bool SdSpiCard::readData(uint8_t* dst, size_t count) { +#if USE_SD_CRC + uint16_t crc; +#endif // USE_SD_CRC + DBG_BEGIN_TIME(DBG_WAIT_READ); + // wait for start block token + uint16_t t0 = curTimeMS(); + while ((m_status = spiReceive()) == 0XFF) { + if (isTimedOut(t0, SD_READ_TIMEOUT)) { + error(SD_CARD_ERROR_READ_TIMEOUT); + goto fail; + } + } + DBG_END_TIME(DBG_WAIT_READ); + if (m_status != DATA_START_BLOCK) { + error(SD_CARD_ERROR_READ); + goto fail; + } + // transfer data + if ((m_status = spiReceive(dst, count))) { + error(SD_CARD_ERROR_DMA); + goto fail; + } + +#if USE_SD_CRC + // get crc + crc = (spiReceive() << 8) | spiReceive(); + if (crc != CRC_CCITT(dst, count)) { + error(SD_CARD_ERROR_READ_CRC); + goto fail; + } +#else + // discard crc + spiReceive(); + spiReceive(); +#endif // USE_SD_CRC + return true; + +fail: + spiStop(); + return false; +} +//------------------------------------------------------------------------------ +bool SdSpiCard::readOCR(uint32_t* ocr) { + uint8_t *p = reinterpret_cast(ocr); + if (cardCommand(CMD58, 0)) { + error(SD_CARD_ERROR_CMD58); + goto fail; + } + for (uint8_t i = 0; i < 4; i++) { + p[3 - i] = spiReceive(); + } + + spiStop(); + return true; + +fail: + spiStop(); + return false; +} +//------------------------------------------------------------------------------ +/** read CID or CSR register */ +bool SdSpiCard::readRegister(uint8_t cmd, void* buf) { + uint8_t* dst = reinterpret_cast(buf); + if (cardCommand(cmd, 0)) { + error(SD_CARD_ERROR_READ_REG); + goto fail; + } + if (!readData(dst, 16)) { + goto fail; + } + spiStop(); + return true; + +fail: + spiStop(); + return false; +} +//------------------------------------------------------------------------------ +bool SdSpiCard::readStart(uint32_t blockNumber) { + SD_TRACE("RS", blockNumber); + if (type() != SD_CARD_TYPE_SDHC) { + blockNumber <<= 9; + } + if (cardCommand(CMD18, blockNumber)) { + error(SD_CARD_ERROR_CMD18); + goto fail; + } +// spiStop(); + return true; + +fail: + spiStop(); + return false; +} +//----------------------------------------------------------------------------- +bool SdSpiCard::readStatus(uint8_t* status) { + // retrun is R2 so read extra status byte. + if (cardAcmd(ACMD13, 0) || spiReceive()) { + error(SD_CARD_ERROR_ACMD13); + goto fail; + } + if (!readData(status, 64)) { + goto fail; + } + spiStop(); + return true; + +fail: + spiStop(); + return false; +} +//----------------------------------------------------------------------------- +void SdSpiCard::spiStart() { + if (!m_spiActive) { + spiActivate(); + spiSelect(); + m_spiActive = true; + } +} +//----------------------------------------------------------------------------- +void SdSpiCard::spiStop() { + if (m_spiActive) { + spiUnselect(); + spiSend(0XFF); + spiDeactivate(); + m_spiActive = false; + } +} +//------------------------------------------------------------------------------ +bool SdSpiCard::readStop() { + if (cardCommand(CMD12, 0)) { + error(SD_CARD_ERROR_CMD12); + goto fail; + } + spiStop(); + return true; + +fail: + spiStop(); + return false; +} +//------------------------------------------------------------------------------ +// wait for card to go not busy +bool SdSpiCard::waitNotBusy(uint16_t timeoutMS) { + uint16_t t0 = curTimeMS(); +#if WDT_YIELD_TIME_MICROS + // Call isTimedOut first to insure yield is called. + while (!isTimedOut(t0, timeoutMS)) { + if (spiReceive() == 0XFF) { + return true; + } + } + return false; +#else // WDT_YIELD_TIME_MICROS + // Check not busy first since yield is not called in isTimedOut. + while (spiReceive() != 0XFF) { + if (isTimedOut(t0, timeoutMS)) { + return false; + } + } + return true; +#endif // WDT_YIELD_TIME_MICROS +} +//------------------------------------------------------------------------------ +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 (cardCommand(CMD24, blockNumber)) { + error(SD_CARD_ERROR_CMD24); + goto fail; + } + if (!writeData(DATA_START_BLOCK, src)) { + goto fail; + } + + +#if CHECK_FLASH_PROGRAMMING + // wait for flash programming to complete + DBG_BEGIN_TIME(DBG_WRITE_FLASH); + if (!waitNotBusy(SD_WRITE_TIMEOUT)) { + error(SD_CARD_ERROR_FLASH_PROGRAMMING); + goto fail; + } + DBG_END_TIME(DBG_WRITE_FLASH); + // response is r2 so get and check two bytes for nonzero + if (cardCommand(CMD13, 0) || spiReceive()) { + error(SD_CARD_ERROR_CMD13); + goto fail; + } +#endif // CHECK_PROGRAMMING + + spiStop(); + return true; + +fail: + spiStop(); + return false; +} +//------------------------------------------------------------------------------ +bool SdSpiCard::writeBlocks(uint32_t block, const uint8_t* src, size_t count) { + if (!writeStart(block)) { + goto fail; + } + for (size_t b = 0; b < count; b++, src += 512) { + if (!writeData(src)) { + goto fail; + } + } + return writeStop(); + + fail: + spiStop(); + return false; +} +//------------------------------------------------------------------------------ +bool SdSpiCard::writeData(const uint8_t* src) { + // wait for previous write to finish + DBG_BEGIN_TIME(DBG_WRITE_BUSY); + if (!waitNotBusy(SD_WRITE_TIMEOUT)) { + error(SD_CARD_ERROR_WRITE_TIMEOUT); + goto fail; + } + DBG_END_TIME(DBG_WRITE_BUSY); + if (!writeData(WRITE_MULTIPLE_TOKEN, src)) { + goto fail; + } + return true; + +fail: + spiStop(); + return false; +} +//------------------------------------------------------------------------------ +// send one block of data for write block or write multiple blocks +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 + spiSend(token); + spiSend(src, 512); + spiSend(crc >> 8); + spiSend(crc & 0XFF); + + m_status = spiReceive(); + if ((m_status & DATA_RES_MASK) != DATA_RES_ACCEPTED) { + error(SD_CARD_ERROR_WRITE); + goto fail; + } + return true; + +fail: + spiStop(); + return false; +} +//------------------------------------------------------------------------------ +bool SdSpiCard::writeStart(uint32_t blockNumber) { + // use address if not SDHC card + if (type() != SD_CARD_TYPE_SDHC) { + blockNumber <<= 9; + } + if (cardCommand(CMD25, blockNumber)) { + error(SD_CARD_ERROR_CMD25); + goto fail; + } + return true; + +fail: + spiStop(); + return false; +} +//------------------------------------------------------------------------------ +bool SdSpiCard::writeStart(uint32_t blockNumber, uint32_t eraseCount) { + SD_TRACE("WS", blockNumber); + // send pre-erase count + if (cardAcmd(ACMD23, eraseCount)) { + error(SD_CARD_ERROR_ACMD23); + goto fail; + } + // use address if not SDHC card + if (type() != SD_CARD_TYPE_SDHC) { + blockNumber <<= 9; + } + if (cardCommand(CMD25, blockNumber)) { + error(SD_CARD_ERROR_CMD25); + goto fail; + } + return true; + +fail: + spiStop(); + return false; +} +//------------------------------------------------------------------------------ +bool SdSpiCard::writeStop() { + DBG_BEGIN_TIME(DBG_WRITE_STOP); + if (!waitNotBusy(SD_WRITE_TIMEOUT)) { + goto fail; + } + DBG_END_TIME(DBG_WRITE_STOP); + spiSend(STOP_TRAN_TOKEN); + spiStop(); + return true; + +fail: + error(SD_CARD_ERROR_STOP_TRAN); + spiStop(); + return false; +} diff --git a/hardware/_controller/src/SdFat/SdCard/SdSpiCard.h b/hardware/_controller/src/SdFat/SdCard/SdSpiCard.h new file mode 100644 index 0000000..4d9a7d1 --- /dev/null +++ b/hardware/_controller/src/SdFat/SdCard/SdSpiCard.h @@ -0,0 +1,379 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef SdSpiCard_h +#define SdSpiCard_h +/** + * \file + * \brief SdSpiCard class for V2 SD/SDHC cards + */ +#include +#include "../SysCall.h" +#include "SdInfo.h" +#include "../FatLib/BaseBlockDriver.h" +#include "../SpiDriver/SdSpiDriver.h" +//============================================================================== +/** + * \class SdSpiCard + * \brief Raw access to SD and SDHC flash memory cards via SPI protocol. + */ +#if ENABLE_EXTENDED_TRANSFER_CLASS || ENABLE_SDIO_CLASS +class SdSpiCard : public BaseBlockDriver { +#else // ENABLE_EXTENDED_TRANSFER_CLASS || ENABLE_SDIO_CLASS +class SdSpiCard { +#endif // ENABLE_EXTENDED_TRANSFER_CLASS || ENABLE_SDIO_CLASS + public: + /** 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 driver for card. + * \param[in] csPin card chip select pin. + * \param[in] spiSettings SPI speed, mode, and bit order. + * \return true for success else false. + */ + bool begin(SdSpiDriver* spi, uint8_t csPin, SPISettings spiSettings); + /** + * Determine the size of an SD flash memory card. + * + * \return The number of 512 byte sectors in the card + * or zero if an error occurs. + */ + uint32_t cardCapacity(); + /** \return Card size in sectors or zero if an error occurs. */ + uint32_t cardSize() {return cardCapacity();} + /** Clear debug stats. */ + void dbgClearStats(); + /** Print debug stats. */ + void dbgPrintStats(); + /** 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 SdInfo.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] lba 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 lba, uint8_t* dst); + /** + * Read multiple 512 byte blocks from an SD card. + * + * \param[in] lba Logical block to be read. + * \param[in] nb 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 lba, uint8_t* dst, size_t nb); + /** + * 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); + /** Return the 64 byte card status + * \param[out] status location for 64 status bytes. + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool readStatus(uint8_t* status); + /** 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 success if sync successful. Not for user apps. */ + bool syncBlocks() {return true;} + /** 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] lba 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 lba, const uint8_t* src); + /** + * Write multiple 512 byte blocks to an SD card. + * + * \param[in] lba Logical block to be written. + * \param[in] nb 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 lba, const uint8_t* src, size_t nb); + /** 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. + * + * \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); + + /** Start a write multiple blocks sequence with pre-erase. + * + * \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(); + /** Set CS low and activate the card. */ + void spiStart(); + /** Set CS high and deactivate the card. */ + void spiStop(); + + 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 isTimedOut(uint16_t startMS, uint16_t timeoutMS); + bool readData(uint8_t* dst, size_t count); + bool readRegister(uint8_t cmd, void* buf); + + void type(uint8_t value) { + m_type = value; + } + + bool waitNotBusy(uint16_t timeoutMS); + bool writeData(uint8_t token, const uint8_t* src); + + //--------------------------------------------------------------------------- + // functions defined in SdSpiDriver.h + void spiActivate() { + m_spiDriver->activate(); + } + void spiDeactivate() { + m_spiDriver->deactivate(); + } + uint8_t spiReceive() { + return m_spiDriver->receive(); + } + uint8_t spiReceive(uint8_t* buf, size_t n) { + return m_spiDriver->receive(buf, n); + } + void spiSend(uint8_t data) { + m_spiDriver->send(data); + } + void spiSend(const uint8_t* buf, size_t n) { + m_spiDriver->send(buf, n); + } + void spiSelect() { + m_spiDriver->select(); + } + void spiUnselect() { + m_spiDriver->unselect(); + } + uint8_t m_errorCode; + SdSpiDriver *m_spiDriver; + bool m_spiActive; + uint8_t m_status; + uint8_t m_type; +}; +//============================================================================== +/** + * \class SdSpiCardEX + * \brief Extended SD I/O block driver. + */ +class SdSpiCardEX : public SdSpiCard { + public: + /** Initialize the SD card + * + * \param[in] spi SPI driver. + * \param[in] csPin Card chip select pin number. + * \param[in] spiSettings SPI speed, mode, and bit order. + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool begin(SdSpiDriver* spi, uint8_t csPin, SPISettings spiSettings) { + m_curState = IDLE_STATE; + return SdSpiCard::begin(spi, csPin, spiSettings); + } + /** + * 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); + /** End multi-block transfer and go to idle state. + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool syncBlocks(); + /** + * Writes a 512 byte block to an SD card. + * + * \param[in] block 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 block, const uint8_t* src); + /** + * Read multiple 512 byte blocks from an SD card. + * + * \param[in] block Logical block to be read. + * \param[in] nb 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 nb); + /** + * Write multiple 512 byte blocks to an SD card. + * + * \param[in] block Logical block to be written. + * \param[in] nb 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 nb); + + private: + static const uint32_t IDLE_STATE = 0; + static const uint32_t READ_STATE = 1; + static const uint32_t WRITE_STATE = 2; + uint32_t m_curBlock; + uint8_t m_curState; +}; +#endif // SdSpiCard_h diff --git a/hardware/_controller/src/SdFat/SdCard/SdSpiCardEX.cpp b/hardware/_controller/src/SdFat/SdCard/SdSpiCardEX.cpp new file mode 100644 index 0000000..b5dced4 --- /dev/null +++ b/hardware/_controller/src/SdFat/SdCard/SdSpiCardEX.cpp @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "SdSpiCard.h" +bool SdSpiCardEX::readBlock(uint32_t block, uint8_t* dst) { + if (m_curState != READ_STATE || block != m_curBlock) { + if (!syncBlocks()) { + return false; + } + if (!SdSpiCard::readStart(block)) { + return false; + } + m_curBlock = block; + m_curState = READ_STATE; + } + if (!SdSpiCard::readData(dst)) { + return false; + } + m_curBlock++; + return true; +} +//----------------------------------------------------------------------------- +bool SdSpiCardEX::readBlocks(uint32_t block, uint8_t* dst, size_t nb) { + for (size_t i = 0; i < nb; i++) { + if (!readBlock(block + i, dst + i*512UL)) { + return false; + } + } + return true; +} +//----------------------------------------------------------------------------- +bool SdSpiCardEX::syncBlocks() { + if (m_curState == READ_STATE) { + m_curState = IDLE_STATE; + if (!SdSpiCard::readStop()) { + return false; + } + } else if (m_curState == WRITE_STATE) { + m_curState = IDLE_STATE; + if (!SdSpiCard::writeStop()) { + return false; + } + } + return true; +} +//----------------------------------------------------------------------------- +bool SdSpiCardEX::writeBlock(uint32_t block, const uint8_t* src) { + if (m_curState != WRITE_STATE || m_curBlock != block) { + if (!syncBlocks()) { + return false; + } + if (!SdSpiCard::writeStart(block)) { + return false; + } + m_curBlock = block; + m_curState = WRITE_STATE; + } + if (!SdSpiCard::writeData(src)) { + return false; + } + m_curBlock++; + return true; +} +//----------------------------------------------------------------------------- +bool SdSpiCardEX::writeBlocks(uint32_t block, + const uint8_t* src, size_t nb) { + for (size_t i = 0; i < nb; i++) { + if (!writeBlock(block + i, src + i*512UL)) { + return false; + } + } + return true; +} diff --git a/hardware/_controller/src/SdFat/SdCard/SdioCard.h b/hardware/_controller/src/SdFat/SdCard/SdioCard.h new file mode 100644 index 0000000..b18b0a6 --- /dev/null +++ b/hardware/_controller/src/SdFat/SdCard/SdioCard.h @@ -0,0 +1,303 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef SdioCard_h +#define SdioCard_h +#include "../SysCall.h" +#include "../BlockDriver.h" +/** + * \class SdioCard + * \brief Raw SDIO access to SD and SDHC flash memory cards. + */ +class SdioCard : public BaseBlockDriver { + public: + /** Initialize the SD card. + * \return true for success else false. + */ + bool begin(); + /** + * Determine the size of an SD flash memory card. + * + * \return The number of 512 byte sectors in the card + * or zero if an error occurs. + */ + uint32_t cardCapacity(); + /** \return Card size in sectors or zero if an error occurs. */ + uint32_t cardSize() {return cardCapacity();} + /** 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); + /** + * \return code for the last error. See SdInfo.h for a list of error codes. + */ + uint8_t errorCode(); + /** \return error data for last error. */ + uint32_t errorData(); + /** \return error line for last error. Tmp function for debug. */ + uint32_t errorLine(); + /** + * Check for busy with CMD13. + * + * \return true if busy else false. + */ + bool isBusy(); + /** \return the SD clock frequency in kHz. */ + uint32_t kHzSdClk(); + /** + * Read a 512 byte block from an SD card. + * + * \param[in] lba 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 lba, uint8_t* dst); + /** + * Read multiple 512 byte blocks from an SD card. + * + * \param[in] lba Logical block to be read. + * \param[in] nb 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 lba, uint8_t* dst, size_t nb); + /** + * 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(void* 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(void* 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] lba 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 lba); + /** Start a read multiple blocks sequence. + * + * \param[in] lba Address of first block in sequence. + * \param[in] count Maximum block count. + * \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 lba, uint32_t count); + /** 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 success if sync successful. Not for user apps. */ + bool syncBlocks(); + /** Return the card type: SD V1, SD V2 or SDHC + * \return 0 - SD V1, 1 - SD V2, or 3 - SDHC. + */ + uint8_t type(); + /** + * Writes a 512 byte block to an SD card. + * + * \param[in] lba 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 lba, const uint8_t* src); + /** + * Write multiple 512 byte blocks to an SD card. + * + * \param[in] lba Logical block to be written. + * \param[in] nb 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 lba, const uint8_t* src, size_t nb); + /** 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] lba Address of first block in sequence. + * + * \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 lba); + /** Start a write multiple blocks sequence. + * + * \param[in] lba Address of first block in sequence. + * \param[in] count Maximum block count. + * \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 lba, uint32_t count); + + /** End a write multiple blocks sequence. + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool writeStop(); +}; +//============================================================================== +/** + * \class SdioCardEX + * \brief Extended SD I/O block driver. + */ +class SdioCardEX : public SdioCard { + public: + /** Initialize the SD card + * + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool begin() { + m_curState = IDLE_STATE; + return SdioCard::begin(); + } + /** 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) { + return syncBlocks() && SdioCard::erase(firstBlock, lastBlock); + } + /** + * 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); + /** End multi-block transfer and go to idle state. + * \return The value true is returned for success and + * the value false is returned for failure. + */ + bool syncBlocks(); + /** + * Writes a 512 byte block to an SD card. + * + * \param[in] block 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 block, const uint8_t* src); + /** + * Read multiple 512 byte blocks from an SD card. + * + * \param[in] block Logical block to be read. + * \param[in] nb 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 nb); + /** + * Write multiple 512 byte blocks to an SD card. + * + * \param[in] block Logical block to be written. + * \param[in] nb 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 nb); + + private: + static const uint32_t IDLE_STATE = 0; + static const uint32_t READ_STATE = 1; + static const uint32_t WRITE_STATE = 2; + uint32_t m_curLba; + uint32_t m_limitLba; + uint8_t m_curState; +}; +#endif // SdioCard_h diff --git a/hardware/_controller/src/SdFat/SdCard/SdioCardEX.cpp b/hardware/_controller/src/SdFat/SdCard/SdioCardEX.cpp new file mode 100644 index 0000000..ed4981d --- /dev/null +++ b/hardware/_controller/src/SdFat/SdCard/SdioCardEX.cpp @@ -0,0 +1,108 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "SdioCard.h" + +// limit of K66 due to errata KINETIS_K_0N65N. +const uint32_t MAX_SDHC_COUNT = 0XFFFF; + +// Max RU is 1024 blocks. +const uint32_t RU_MASK = 0X03FF; + +bool SdioCardEX::readBlock(uint32_t lba, uint8_t* dst) { + if (m_curState != READ_STATE || lba != m_curLba) { + if (!syncBlocks()) { + return false; + } + m_limitLba = (lba + MAX_SDHC_COUNT) & ~RU_MASK; + if (!SdioCard::readStart(lba, m_limitLba - lba)) { + return false; + } + m_curLba = lba; + m_curState = READ_STATE; + } + if (!SdioCard::readData(dst)) { + return false; + } + m_curLba++; + if (m_curLba >= m_limitLba) { + m_curState = IDLE_STATE; + } + return true; +} +//----------------------------------------------------------------------------- +bool SdioCardEX::readBlocks(uint32_t lba, uint8_t* dst, size_t nb) { + for (size_t i = 0; i < nb; i++) { + if (!readBlock(lba + i, dst + i*512UL)) { + return false; + } + } + return true; +} +//----------------------------------------------------------------------------- +bool SdioCardEX::syncBlocks() { + if (m_curState == READ_STATE) { + m_curState = IDLE_STATE; + if (!SdioCard::readStop()) { + return false; + } + } else if (m_curState == WRITE_STATE) { + m_curState = IDLE_STATE; + if (!SdioCard::writeStop()) { + return false; + } + } + return true; +} +//----------------------------------------------------------------------------- +bool SdioCardEX::writeBlock(uint32_t lba, const uint8_t* src) { + if (m_curState != WRITE_STATE || m_curLba != lba) { + if (!syncBlocks()) { + return false; + } + m_limitLba = (lba + MAX_SDHC_COUNT) & ~RU_MASK; + if (!SdioCard::writeStart(lba , m_limitLba - lba)) { + return false; + } + m_curLba = lba; + m_curState = WRITE_STATE; + } + if (!SdioCard::writeData(src)) { + return false; + } + m_curLba++; + if (m_curLba >= m_limitLba) { + m_curState = IDLE_STATE; + } + return true; +} +//----------------------------------------------------------------------------- +bool SdioCardEX::writeBlocks(uint32_t lba, const uint8_t* src, size_t nb) { + for (size_t i = 0; i < nb; i++) { + if (!writeBlock(lba + i, src + i*512UL)) { + return false; + } + } + return true; +} diff --git a/hardware/_controller/src/SdFat/SdCard/SdioTeensy.cpp b/hardware/_controller/src/SdFat/SdCard/SdioTeensy.cpp new file mode 100644 index 0000000..3ed3eb6 --- /dev/null +++ b/hardware/_controller/src/SdFat/SdCard/SdioTeensy.cpp @@ -0,0 +1,800 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#if defined(__MK64FX512__) || defined(__MK66FX1M0__) +#include "SdioCard.h" +//============================================================================== +#define SDHC_PROCTL_DTW_4BIT 0x01 +const uint32_t FIFO_WML = 16; +const uint32_t CMD8_RETRIES = 10; +const uint32_t BUSY_TIMEOUT_MICROS = 500000; +//============================================================================== +const uint32_t SDHC_IRQSTATEN_MASK = + SDHC_IRQSTATEN_DMAESEN | SDHC_IRQSTATEN_AC12ESEN | + SDHC_IRQSTATEN_DEBESEN | SDHC_IRQSTATEN_DCESEN | + SDHC_IRQSTATEN_DTOESEN | SDHC_IRQSTATEN_CIESEN | + SDHC_IRQSTATEN_CEBESEN | SDHC_IRQSTATEN_CCESEN | + SDHC_IRQSTATEN_CTOESEN | SDHC_IRQSTATEN_DINTSEN | + SDHC_IRQSTATEN_TCSEN | SDHC_IRQSTATEN_CCSEN; + +const uint32_t SDHC_IRQSTAT_CMD_ERROR = + SDHC_IRQSTAT_CIE | SDHC_IRQSTAT_CEBE | + SDHC_IRQSTAT_CCE | SDHC_IRQSTAT_CTOE; + +const uint32_t SDHC_IRQSTAT_DATA_ERROR = + SDHC_IRQSTAT_AC12E | SDHC_IRQSTAT_DEBE | + SDHC_IRQSTAT_DCE | SDHC_IRQSTAT_DTOE; + +const uint32_t SDHC_IRQSTAT_ERROR = + SDHC_IRQSTAT_DMAE | SDHC_IRQSTAT_CMD_ERROR | + SDHC_IRQSTAT_DATA_ERROR; + +const uint32_t SDHC_IRQSIGEN_MASK = + SDHC_IRQSIGEN_DMAEIEN | SDHC_IRQSIGEN_AC12EIEN | + SDHC_IRQSIGEN_DEBEIEN | SDHC_IRQSIGEN_DCEIEN | + SDHC_IRQSIGEN_DTOEIEN | SDHC_IRQSIGEN_CIEIEN | + SDHC_IRQSIGEN_CEBEIEN | SDHC_IRQSIGEN_CCEIEN | + SDHC_IRQSIGEN_CTOEIEN | SDHC_IRQSIGEN_TCIEN; +//============================================================================= +const uint32_t CMD_RESP_NONE = SDHC_XFERTYP_RSPTYP(0); + +const uint32_t CMD_RESP_R1 = SDHC_XFERTYP_CICEN | SDHC_XFERTYP_CCCEN | + SDHC_XFERTYP_RSPTYP(2); + +const uint32_t CMD_RESP_R1b = SDHC_XFERTYP_CICEN | SDHC_XFERTYP_CCCEN | + SDHC_XFERTYP_RSPTYP(3); + +const uint32_t CMD_RESP_R2 = SDHC_XFERTYP_CCCEN | SDHC_XFERTYP_RSPTYP(1); + +const uint32_t CMD_RESP_R3 = SDHC_XFERTYP_RSPTYP(2); + +const uint32_t CMD_RESP_R6 = CMD_RESP_R1; + +const uint32_t CMD_RESP_R7 = CMD_RESP_R1; + +const uint32_t DATA_READ = SDHC_XFERTYP_DTDSEL | SDHC_XFERTYP_DPSEL; + +const uint32_t DATA_READ_DMA = DATA_READ | SDHC_XFERTYP_DMAEN; + +const uint32_t DATA_READ_MULTI_DMA = DATA_READ_DMA | SDHC_XFERTYP_MSBSEL | + SDHC_XFERTYP_AC12EN | SDHC_XFERTYP_BCEN; + +const uint32_t DATA_READ_MULTI_PGM = DATA_READ | SDHC_XFERTYP_MSBSEL | + SDHC_XFERTYP_BCEN | SDHC_XFERTYP_AC12EN; + +const uint32_t DATA_WRITE_DMA = SDHC_XFERTYP_DPSEL | SDHC_XFERTYP_DMAEN; + +const uint32_t DATA_WRITE_MULTI_DMA = DATA_WRITE_DMA | SDHC_XFERTYP_MSBSEL | + SDHC_XFERTYP_AC12EN | SDHC_XFERTYP_BCEN; + +const uint32_t DATA_WRITE_MULTI_PGM = SDHC_XFERTYP_DPSEL | + SDHC_XFERTYP_MSBSEL | + SDHC_XFERTYP_BCEN | SDHC_XFERTYP_AC12EN; + +const uint32_t ACMD6_XFERTYP = SDHC_XFERTYP_CMDINX(ACMD6) | CMD_RESP_R1; + +const uint32_t ACMD41_XFERTYP = SDHC_XFERTYP_CMDINX(ACMD41) | CMD_RESP_R3; + +const uint32_t CMD0_XFERTYP = SDHC_XFERTYP_CMDINX(CMD0) | CMD_RESP_NONE; + +const uint32_t CMD2_XFERTYP = SDHC_XFERTYP_CMDINX(CMD2) | CMD_RESP_R2; + +const uint32_t CMD3_XFERTYP = SDHC_XFERTYP_CMDINX(CMD3) | CMD_RESP_R6; + +const uint32_t CMD6_XFERTYP = SDHC_XFERTYP_CMDINX(CMD6) | CMD_RESP_R1 | + DATA_READ_DMA; + +const uint32_t CMD7_XFERTYP = SDHC_XFERTYP_CMDINX(CMD7) | CMD_RESP_R1b; + +const uint32_t CMD8_XFERTYP = SDHC_XFERTYP_CMDINX(CMD8) | CMD_RESP_R7; + +const uint32_t CMD9_XFERTYP = SDHC_XFERTYP_CMDINX(CMD9) | CMD_RESP_R2; + +const uint32_t CMD10_XFERTYP = SDHC_XFERTYP_CMDINX(CMD10) | CMD_RESP_R2; + +const uint32_t CMD12_XFERTYP = SDHC_XFERTYP_CMDINX(CMD12) | CMD_RESP_R1b | + SDHC_XFERTYP_CMDTYP(3); + +const uint32_t CMD13_XFERTYP = SDHC_XFERTYP_CMDINX(CMD13) | CMD_RESP_R1; + +const uint32_t CMD17_DMA_XFERTYP = SDHC_XFERTYP_CMDINX(CMD17) | CMD_RESP_R1 | + DATA_READ_DMA; + +const uint32_t CMD18_DMA_XFERTYP = SDHC_XFERTYP_CMDINX(CMD18) | CMD_RESP_R1 | + DATA_READ_MULTI_DMA; + +const uint32_t CMD18_PGM_XFERTYP = SDHC_XFERTYP_CMDINX(CMD18) | CMD_RESP_R1 | + DATA_READ_MULTI_PGM; + +const uint32_t CMD24_DMA_XFERTYP = SDHC_XFERTYP_CMDINX(CMD24) | CMD_RESP_R1 | + DATA_WRITE_DMA; + +const uint32_t CMD25_DMA_XFERTYP = SDHC_XFERTYP_CMDINX(CMD25) | CMD_RESP_R1 | + DATA_WRITE_MULTI_DMA; + +const uint32_t CMD25_PGM_XFERTYP = SDHC_XFERTYP_CMDINX(CMD25) | CMD_RESP_R1 | + DATA_WRITE_MULTI_PGM; + +const uint32_t CMD32_XFERTYP = SDHC_XFERTYP_CMDINX(CMD32) | CMD_RESP_R1; + +const uint32_t CMD33_XFERTYP = SDHC_XFERTYP_CMDINX(CMD33) | CMD_RESP_R1; + +const uint32_t CMD38_XFERTYP = SDHC_XFERTYP_CMDINX(CMD38) | CMD_RESP_R1b; + +const uint32_t CMD55_XFERTYP = SDHC_XFERTYP_CMDINX(CMD55) | CMD_RESP_R1; + +//============================================================================= +static bool cardCommand(uint32_t xfertyp, uint32_t arg); +static void enableGPIO(bool enable); +static void enableDmaIrs(); +static void initSDHC(); +static bool isBusyCMD13(); +static bool isBusyCommandComplete(); +static bool isBusyCommandInhibit(); +static bool readReg16(uint32_t xfertyp, void* data); +static void setSdclk(uint32_t kHzMax); +static bool yieldTimeout(bool (*fcn)()); +static bool waitDmaStatus(); +static bool waitTimeout(bool (*fcn)()); +//----------------------------------------------------------------------------- +static bool (*m_busyFcn)() = 0; +static bool m_initDone = false; +static bool m_version2; +static bool m_highCapacity; +static uint8_t m_errorCode = SD_CARD_ERROR_INIT_NOT_CALLED; +static uint32_t m_errorLine = 0; +static uint32_t m_rca; +static volatile bool m_dmaBusy = false; +static volatile uint32_t m_irqstat; +static uint32_t m_sdClkKhz = 0; +static uint32_t m_ocr; +static cid_t m_cid; +static csd_t m_csd; +//============================================================================= +#define USE_DEBUG_MODE 0 +#if USE_DEBUG_MODE +#define DBG_IRQSTAT() if (SDHC_IRQSTAT) {Serial.print(__LINE__);\ + Serial.print(" IRQSTAT "); Serial.println(SDHC_IRQSTAT, HEX);} + +static void printRegs(uint32_t line) { + Serial.print(line); + Serial.print(" PRSSTAT "); + Serial.print(SDHC_PRSSTAT, HEX); + Serial.print(" PROCTL "); + Serial.print(SDHC_PROCTL, HEX); + Serial.print(" IRQSTAT "); + Serial.print(SDHC_IRQSTAT, HEX); + Serial.print(" m_irqstat "); + Serial.println(m_irqstat, HEX); +} +#else // USE_DEBUG_MODE +#define DBG_IRQSTAT() +#endif // USE_DEBUG_MODE +//============================================================================= +// Error function and macro. +#define sdError(code) setSdErrorCode(code, __LINE__) +inline bool setSdErrorCode(uint8_t code, uint32_t line) { + m_errorCode = code; + m_errorLine = line; + return false; // setSdErrorCode +} +//============================================================================= +// ISR +void sdhc_isr() { + SDHC_IRQSIGEN = 0; + m_irqstat = SDHC_IRQSTAT; + SDHC_IRQSTAT = m_irqstat; + m_dmaBusy = false; +} +//============================================================================= +// Static functions. +static bool cardAcmd(uint32_t rca, uint32_t xfertyp, uint32_t arg) { + return cardCommand(CMD55_XFERTYP, rca) && cardCommand (xfertyp, arg); +} +//----------------------------------------------------------------------------- +static bool cardCommand(uint32_t xfertyp, uint32_t arg) { + DBG_IRQSTAT(); + if (waitTimeout(isBusyCommandInhibit)) { + return false; // Caller will set errorCode. + } + SDHC_CMDARG = arg; + SDHC_XFERTYP = xfertyp; + if (waitTimeout(isBusyCommandComplete)) { + return false; // Caller will set errorCode. + } + m_irqstat = SDHC_IRQSTAT; + SDHC_IRQSTAT = m_irqstat; + + return (m_irqstat & SDHC_IRQSTAT_CC) && + !(m_irqstat & SDHC_IRQSTAT_CMD_ERROR); +} +//----------------------------------------------------------------------------- +static bool cardCMD6(uint32_t arg, uint8_t* status) { + // CMD6 returns 64 bytes. + if (waitTimeout(isBusyCMD13)) { + return sdError(SD_CARD_ERROR_CMD13); + } + enableDmaIrs(); + SDHC_DSADDR = (uint32_t)status; + SDHC_CMDARG = arg; + SDHC_BLKATTR = SDHC_BLKATTR_BLKCNT(1) | SDHC_BLKATTR_BLKSIZE(64); + SDHC_IRQSIGEN = SDHC_IRQSIGEN_MASK; + SDHC_XFERTYP = CMD6_XFERTYP; + + if (!waitDmaStatus()) { + return sdError(SD_CARD_ERROR_CMD6); + } + return true; +} +//----------------------------------------------------------------------------- +static void enableGPIO(bool enable) { + const uint32_t PORT_CLK = PORT_PCR_MUX(4) | PORT_PCR_DSE; + const uint32_t PORT_CMD_DATA = PORT_CLK | PORT_PCR_PS | PORT_PCR_PE; + + PORTE_PCR0 = enable ? PORT_CMD_DATA : 0; // SDHC_D1 + PORTE_PCR1 = enable ? PORT_CMD_DATA : 0; // SDHC_D0 + PORTE_PCR2 = enable ? PORT_CLK : 0; // SDHC_CLK + PORTE_PCR3 = enable ? PORT_CMD_DATA : 0; // SDHC_CMD + PORTE_PCR4 = enable ? PORT_CMD_DATA : 0; // SDHC_D3 + PORTE_PCR5 = enable ? PORT_CMD_DATA : 0; // SDHC_D2 +} +//----------------------------------------------------------------------------- +static void enableDmaIrs() { + m_dmaBusy = true; + m_irqstat = 0; +} +//----------------------------------------------------------------------------- +static void initSDHC() { +#ifdef HAS_KINETIS_MPU + // Allow SDHC Bus Master access. + MPU_RGDAAC0 |= 0x0C000000; +#endif + // Enable SDHC clock. + SIM_SCGC3 |= SIM_SCGC3_SDHC; + + // Disable GPIO clock. + enableGPIO(false); + + // Reset SDHC. Use default Water Mark Level of 16. + SDHC_SYSCTL = SDHC_SYSCTL_RSTA; + while (SDHC_SYSCTL & SDHC_SYSCTL_RSTA) { + } + // Set initial SCK rate. + setSdclk(400); + + enableGPIO(true); + + // Enable desired IRQSTAT bits. + SDHC_IRQSTATEN = SDHC_IRQSTATEN_MASK; + + NVIC_SET_PRIORITY(IRQ_SDHC, 6*16); + NVIC_ENABLE_IRQ(IRQ_SDHC); + + // Send 80 clocks to card. + SDHC_SYSCTL |= SDHC_SYSCTL_INITA; + while (SDHC_SYSCTL & SDHC_SYSCTL_INITA) { + } +} +//----------------------------------------------------------------------------- +static bool isBusyCMD13() { + if (!cardCommand(CMD13_XFERTYP, m_rca)) { + // Caller will timeout. + return true; + } + return !(SDHC_CMDRSP0 & CARD_STATUS_READY_FOR_DATA); +} +//----------------------------------------------------------------------------- +static bool isBusyCommandComplete() { + return !(SDHC_IRQSTAT &(SDHC_IRQSTAT_CC | SDHC_IRQSTAT_CMD_ERROR)); +} +//----------------------------------------------------------------------------- +static bool isBusyCommandInhibit() { + return SDHC_PRSSTAT & SDHC_PRSSTAT_CIHB; +} +//----------------------------------------------------------------------------- +static bool isBusyDMA() { + return m_dmaBusy; +} +//----------------------------------------------------------------------------- +static bool isBusyFifoRead() { + return !(SDHC_PRSSTAT & SDHC_PRSSTAT_BREN); +} +//----------------------------------------------------------------------------- +static bool isBusyFifoWrite() { + return !(SDHC_PRSSTAT & SDHC_PRSSTAT_BWEN); +} +//----------------------------------------------------------------------------- +static bool isBusyTransferComplete() { + return !(SDHC_IRQSTAT & (SDHC_IRQSTAT_TC | SDHC_IRQSTAT_ERROR)); +} +//----------------------------------------------------------------------------- +static bool rdWrBlocks(uint32_t xfertyp, + uint32_t lba, uint8_t* buf, size_t n) { + if ((3 & (uint32_t)buf) || n == 0) { + return sdError(SD_CARD_ERROR_DMA); + } + if (yieldTimeout(isBusyCMD13)) { + return sdError(SD_CARD_ERROR_CMD13); + } + enableDmaIrs(); + SDHC_DSADDR = (uint32_t)buf; + SDHC_CMDARG = m_highCapacity ? lba : 512*lba; + SDHC_BLKATTR = SDHC_BLKATTR_BLKCNT(n) | SDHC_BLKATTR_BLKSIZE(512); + SDHC_IRQSIGEN = SDHC_IRQSIGEN_MASK; + SDHC_XFERTYP = xfertyp; + + return waitDmaStatus(); +} +//----------------------------------------------------------------------------- +// Read 16 byte CID or CSD register. +static bool readReg16(uint32_t xfertyp, void* data) { + uint8_t* d = reinterpret_cast(data); + if (!cardCommand(xfertyp, m_rca)) { + return false; // Caller will set errorCode. + } + uint32_t sr[] = {SDHC_CMDRSP0, SDHC_CMDRSP1, SDHC_CMDRSP2, SDHC_CMDRSP3}; + for (int i = 0; i < 15; i++) { + d[14 - i] = sr[i/4] >> 8*(i%4); + } + d[15] = 0; + return true; +} +//----------------------------------------------------------------------------- +static void setSdclk(uint32_t kHzMax) { + const uint32_t DVS_LIMIT = 0X10; + const uint32_t SDCLKFS_LIMIT = 0X100; + uint32_t dvs = 1; + uint32_t sdclkfs = 1; + uint32_t maxSdclk = 1000*kHzMax; + + while ((F_CPU/(sdclkfs*DVS_LIMIT) > maxSdclk) && (sdclkfs < SDCLKFS_LIMIT)) { + sdclkfs <<= 1; + } + while ((F_CPU/(sdclkfs*dvs) > maxSdclk) && (dvs < DVS_LIMIT)) { + dvs++; + } + m_sdClkKhz = F_CPU/(1000*sdclkfs*dvs); + sdclkfs >>= 1; + dvs--; + + // Disable SDHC clock. + SDHC_SYSCTL &= ~SDHC_SYSCTL_SDCLKEN; + + // Change dividers. + uint32_t sysctl = SDHC_SYSCTL & ~(SDHC_SYSCTL_DTOCV_MASK + | SDHC_SYSCTL_DVS_MASK | SDHC_SYSCTL_SDCLKFS_MASK); + + SDHC_SYSCTL = sysctl | SDHC_SYSCTL_DTOCV(0x0E) | SDHC_SYSCTL_DVS(dvs) + | SDHC_SYSCTL_SDCLKFS(sdclkfs); + + // Wait until the SDHC clock is stable. + while (!(SDHC_PRSSTAT & SDHC_PRSSTAT_SDSTB)) { + } + // Enable the SDHC clock. + SDHC_SYSCTL |= SDHC_SYSCTL_SDCLKEN; +} +//----------------------------------------------------------------------------- +static bool transferStop() { + DBG_IRQSTAT(); + + if (!cardCommand(CMD12_XFERTYP, 0)) { + return sdError(SD_CARD_ERROR_CMD12); + } + if (yieldTimeout(isBusyCMD13)) { + return sdError(SD_CARD_ERROR_CMD13); + } + // Save registers before reset DAT lines. + uint32_t irqsststen = SDHC_IRQSTATEN; + uint32_t proctl = SDHC_PROCTL & ~SDHC_PROCTL_SABGREQ; + + // Do reset to clear CDIHB. Should be a better way! + SDHC_SYSCTL |= SDHC_SYSCTL_RSTD; + + // Restore registers. + SDHC_IRQSTATEN = irqsststen; + SDHC_PROCTL = proctl; + + return true; +} +//----------------------------------------------------------------------------- +// Return true if timeout occurs. +static bool yieldTimeout(bool (*fcn)()) { + m_busyFcn = fcn; + uint32_t m = micros(); + while (fcn()) { + if ((micros() - m) > BUSY_TIMEOUT_MICROS) { + m_busyFcn = 0; + return true; + } + yield(); + } + m_busyFcn = 0; + return false; // Caller will set errorCode. +} +//----------------------------------------------------------------------------- +static bool waitDmaStatus() { + if (yieldTimeout(isBusyDMA)) { + return false; // Caller will set errorCode. + } + return (m_irqstat & SDHC_IRQSTAT_TC) && !(m_irqstat & SDHC_IRQSTAT_ERROR); +} +//----------------------------------------------------------------------------- +// Return true if timeout occurs. +static bool waitTimeout(bool (*fcn)()) { + uint32_t m = micros(); + while (fcn()) { + if ((micros() - m) > BUSY_TIMEOUT_MICROS) { + return true; + } + } + return false; // Caller will set errorCode. +} +//============================================================================= +bool SdioCard::begin() { + uint32_t kHzSdClk; + uint32_t arg; + m_initDone = false; + m_errorCode = SD_CARD_ERROR_NONE; + m_highCapacity = false; + m_version2 = false; + + // initialize controller. + initSDHC(); + + if (!cardCommand(CMD0_XFERTYP, 0)) { + return sdError(SD_CARD_ERROR_CMD0); + } + // Try several times for case of reset delay. + for (uint32_t i = 0; i < CMD8_RETRIES; i++) { + if (cardCommand(CMD8_XFERTYP, 0X1AA)) { + if (SDHC_CMDRSP0 != 0X1AA) { + return sdError(SD_CARD_ERROR_CMD8); + } + m_version2 = true; + break; + } + } + arg = m_version2 ? 0X40300000 : 0x00300000; + uint32_t m = micros(); + do { + if (!cardAcmd(0, ACMD41_XFERTYP, arg) || + ((micros() - m) > BUSY_TIMEOUT_MICROS)) { + return sdError(SD_CARD_ERROR_ACMD41); + } + } while ((SDHC_CMDRSP0 & 0x80000000) == 0); + + m_ocr = SDHC_CMDRSP0; + if (SDHC_CMDRSP0 & 0x40000000) { + // Is high capacity. + m_highCapacity = true; + } + if (!cardCommand(CMD2_XFERTYP, 0)) { + return sdError(SD_CARD_ERROR_CMD2); + } + if (!cardCommand(CMD3_XFERTYP, 0)) { + return sdError(SD_CARD_ERROR_CMD3); + } + m_rca = SDHC_CMDRSP0 & 0xFFFF0000; + + if (!readReg16(CMD9_XFERTYP, &m_csd)) { + return sdError(SD_CARD_ERROR_CMD9); + } + if (!readReg16(CMD10_XFERTYP, &m_cid)) { + return sdError(SD_CARD_ERROR_CMD10); + } + if (!cardCommand(CMD7_XFERTYP, m_rca)) { + return sdError(SD_CARD_ERROR_CMD7); + } + // Set card to bus width four. + if (!cardAcmd(m_rca, ACMD6_XFERTYP, 2)) { + return sdError(SD_CARD_ERROR_ACMD6); + } + // Set SDHC to bus width four. + SDHC_PROCTL &= ~SDHC_PROCTL_DTW_MASK; + SDHC_PROCTL |= SDHC_PROCTL_DTW(SDHC_PROCTL_DTW_4BIT); + + SDHC_WML = SDHC_WML_RDWML(FIFO_WML) | SDHC_WML_WRWML(FIFO_WML); + + // Determine if High Speed mode is supported and set frequency. + uint8_t status[64]; + if (cardCMD6(0X00FFFFFF, status) && (2 & status[13]) && + cardCMD6(0X80FFFFF1, status) && (status[16] & 0XF) == 1) { + kHzSdClk = 50000; + } else { + kHzSdClk = 25000; + } + // disable GPIO + enableGPIO(false); + + // Set the SDHC SCK frequency. + setSdclk(kHzSdClk); + + // enable GPIO + enableGPIO(true); + m_initDone = true; + return true; +} +//----------------------------------------------------------------------------- +uint32_t SdioCard::cardCapacity() { + return sdCardCapacity(&m_csd); +} +//----------------------------------------------------------------------------- +bool SdioCard::erase(uint32_t firstBlock, uint32_t lastBlock) { + // check for single block erase + if (!m_csd.v1.erase_blk_en) { + // erase size mask + uint8_t m = (m_csd.v1.sector_size_high << 1) | m_csd.v1.sector_size_low; + if ((firstBlock & m) != 0 || ((lastBlock + 1) & m) != 0) { + // error card can't erase specified area + return sdError(SD_CARD_ERROR_ERASE_SINGLE_BLOCK); + } + } + if (!m_highCapacity) { + firstBlock <<= 9; + lastBlock <<= 9; + } + if (!cardCommand(CMD32_XFERTYP, firstBlock)) { + return sdError(SD_CARD_ERROR_CMD32); + } + if (!cardCommand(CMD33_XFERTYP, lastBlock)) { + return sdError(SD_CARD_ERROR_CMD33); + } + if (!cardCommand(CMD38_XFERTYP, 0)) { + return sdError(SD_CARD_ERROR_CMD38); + } + if (waitTimeout(isBusyCMD13)) { + return sdError(SD_CARD_ERROR_ERASE_TIMEOUT); + } + return true; +} +//----------------------------------------------------------------------------- +uint8_t SdioCard::errorCode() { + return m_errorCode; +} +//----------------------------------------------------------------------------- +uint32_t SdioCard::errorData() { + return m_irqstat; +} +//----------------------------------------------------------------------------- +uint32_t SdioCard::errorLine() { + return m_errorLine; +} +//----------------------------------------------------------------------------- +bool SdioCard::isBusy() { + return m_busyFcn ? m_busyFcn() : m_initDone && isBusyCMD13(); +} +//----------------------------------------------------------------------------- +uint32_t SdioCard::kHzSdClk() { + return m_sdClkKhz; +} +//----------------------------------------------------------------------------- +bool SdioCard::readBlock(uint32_t lba, uint8_t* buf) { + uint8_t aligned[512]; + + uint8_t* ptr = (uint32_t)buf & 3 ? aligned : buf; + + if (!rdWrBlocks(CMD17_DMA_XFERTYP, lba, ptr, 1)) { + return sdError(SD_CARD_ERROR_CMD18); + } + if (ptr != buf) { + memcpy(buf, aligned, 512); + } + return true; +} +//----------------------------------------------------------------------------- +bool SdioCard::readBlocks(uint32_t lba, uint8_t* buf, size_t n) { + if ((uint32_t)buf & 3) { + for (size_t i = 0; i < n; i++, lba++, buf += 512) { + if (!readBlock(lba, buf)) { + return false; // readBlock will set errorCode. + } + } + return true; + } + if (!rdWrBlocks(CMD18_DMA_XFERTYP, lba, buf, n)) { + return sdError(SD_CARD_ERROR_CMD18); + } + return true; +} +//----------------------------------------------------------------------------- +bool SdioCard::readCID(void* cid) { + memcpy(cid, &m_cid, 16); + return true; +} +//----------------------------------------------------------------------------- +bool SdioCard::readCSD(void* csd) { + memcpy(csd, &m_csd, 16); + return true; +} +//----------------------------------------------------------------------------- +bool SdioCard::readData(uint8_t *dst) { + DBG_IRQSTAT(); + uint32_t *p32 = reinterpret_cast(dst); + + if (!(SDHC_PRSSTAT & SDHC_PRSSTAT_RTA)) { + SDHC_PROCTL &= ~SDHC_PROCTL_SABGREQ; + if ((SDHC_BLKATTR & 0XFFFF0000) == 0X10000) { + // Don't stop at block gap if last block. Allows auto CMD12. + SDHC_PROCTL |= SDHC_PROCTL_CREQ; + } else { + noInterrupts(); + SDHC_PROCTL |= SDHC_PROCTL_CREQ; + SDHC_PROCTL |= SDHC_PROCTL_SABGREQ; + interrupts(); + } + } + if (waitTimeout(isBusyFifoRead)) { + return sdError(SD_CARD_ERROR_READ_FIFO); + } + for (uint32_t iw = 0 ; iw < 512/(4*FIFO_WML); iw++) { + while (0 == (SDHC_PRSSTAT & SDHC_PRSSTAT_BREN)) { + } + for (uint32_t i = 0; i < FIFO_WML; i++) { + p32[i] = SDHC_DATPORT; + } + p32 += FIFO_WML; + } + if (waitTimeout(isBusyTransferComplete)) { + return sdError(SD_CARD_ERROR_READ_TIMEOUT); + } + m_irqstat = SDHC_IRQSTAT; + SDHC_IRQSTAT = m_irqstat; + return (m_irqstat & SDHC_IRQSTAT_TC) && !(m_irqstat & SDHC_IRQSTAT_ERROR); +} +//----------------------------------------------------------------------------- +bool SdioCard::readOCR(uint32_t* ocr) { + *ocr = m_ocr; + return true; +} +//----------------------------------------------------------------------------- +bool SdioCard::readStart(uint32_t lba) { + // K66/K65 Errata - SDHC: Does not support Infinite Block Transfer Mode. + return sdError(SD_CARD_ERROR_FUNCTION_NOT_SUPPORTED); +} +//----------------------------------------------------------------------------- +// SDHC will do Auto CMD12 after count blocks. +bool SdioCard::readStart(uint32_t lba, uint32_t count) { + DBG_IRQSTAT(); + if (count > 0XFFFF) { + return sdError(SD_CARD_ERROR_READ_START); + } + if (yieldTimeout(isBusyCMD13)) { + return sdError(SD_CARD_ERROR_CMD13); + } + if (count > 1) { + SDHC_PROCTL |= SDHC_PROCTL_SABGREQ; + } + SDHC_BLKATTR = SDHC_BLKATTR_BLKCNT(count) | SDHC_BLKATTR_BLKSIZE(512); + if (!cardCommand(CMD18_PGM_XFERTYP, m_highCapacity ? lba : 512*lba)) { + return sdError(SD_CARD_ERROR_CMD18); + } + return true; +} +//----------------------------------------------------------------------------- +bool SdioCard::readStop() { + return transferStop(); +} +//----------------------------------------------------------------------------- +bool SdioCard::syncBlocks() { + return true; +} +//----------------------------------------------------------------------------- +uint8_t SdioCard::type() { + return m_version2 ? m_highCapacity ? + SD_CARD_TYPE_SDHC : SD_CARD_TYPE_SD2 : SD_CARD_TYPE_SD1; +} +//----------------------------------------------------------------------------- +bool SdioCard::writeBlock(uint32_t lba, const uint8_t* buf) { + uint8_t *ptr; + uint8_t aligned[512]; + if (3 & (uint32_t)buf) { + ptr = aligned; + memcpy(aligned, buf, 512); + } else { + ptr = const_cast(buf); + } + if (!rdWrBlocks(CMD24_DMA_XFERTYP, lba, ptr, 1)) { + return sdError(SD_CARD_ERROR_CMD24); + } + return true; +} +//----------------------------------------------------------------------------- +bool SdioCard::writeBlocks(uint32_t lba, const uint8_t* buf, size_t n) { + uint8_t* ptr = const_cast(buf); + if (3 & (uint32_t)ptr) { + for (size_t i = 0; i < n; i++, lba++, ptr += 512) { + if (!writeBlock(lba, ptr)) { + return false; // writeBlock will set errorCode. + } + } + return true; + } + if (!rdWrBlocks(CMD25_DMA_XFERTYP, lba, ptr, n)) { + return sdError(SD_CARD_ERROR_CMD25); + } + return true; +} +//----------------------------------------------------------------------------- +bool SdioCard::writeData(const uint8_t* src) { + DBG_IRQSTAT(); + const uint32_t* p32 = reinterpret_cast(src); + + if (!(SDHC_PRSSTAT & SDHC_PRSSTAT_WTA)) { + SDHC_PROCTL &= ~SDHC_PROCTL_SABGREQ; + // Don't stop at block gap if last block. Allows auto CMD12. + if ((SDHC_BLKATTR & 0XFFFF0000) == 0X10000) { + SDHC_PROCTL |= SDHC_PROCTL_CREQ; + } else { + SDHC_PROCTL |= SDHC_PROCTL_CREQ; + SDHC_PROCTL |= SDHC_PROCTL_SABGREQ; + } + } + if (waitTimeout(isBusyFifoWrite)) { + return sdError(SD_CARD_ERROR_WRITE_FIFO); + } + for (uint32_t iw = 0 ; iw < 512/(4*FIFO_WML); iw++) { + while (0 == (SDHC_PRSSTAT & SDHC_PRSSTAT_BWEN)) { + } + for (uint32_t i = 0; i < FIFO_WML; i++) { + SDHC_DATPORT = p32[i]; + } + p32 += FIFO_WML; + } + if (waitTimeout(isBusyTransferComplete)) { + return sdError(SD_CARD_ERROR_WRITE_TIMEOUT); + } + m_irqstat = SDHC_IRQSTAT; + SDHC_IRQSTAT = m_irqstat; + return (m_irqstat & SDHC_IRQSTAT_TC) && !(m_irqstat & SDHC_IRQSTAT_ERROR); +} +//----------------------------------------------------------------------------- +bool SdioCard::writeStart(uint32_t lba) { + // K66/K65 Errata - SDHC: Does not support Infinite Block Transfer Mode. + return sdError(SD_CARD_ERROR_FUNCTION_NOT_SUPPORTED); +} +//----------------------------------------------------------------------------- +// SDHC will do Auto CMD12 after count blocks. +bool SdioCard::writeStart(uint32_t lba, uint32_t count) { + if (count > 0XFFFF) { + return sdError(SD_CARD_ERROR_WRITE_START); + } + DBG_IRQSTAT(); + if (yieldTimeout(isBusyCMD13)) { + return sdError(SD_CARD_ERROR_CMD13); + } + if (count > 1) { + SDHC_PROCTL |= SDHC_PROCTL_SABGREQ; + } + SDHC_BLKATTR = SDHC_BLKATTR_BLKCNT(count) | SDHC_BLKATTR_BLKSIZE(512); + + if (!cardCommand(CMD25_PGM_XFERTYP, m_highCapacity ? lba : 512*lba)) { + return sdError(SD_CARD_ERROR_CMD25); + } + return true; +} +//----------------------------------------------------------------------------- +bool SdioCard::writeStop() { + return transferStop(); +} +#endif // defined(__MK64FX512__) || defined(__MK66FX1M0__) diff --git a/hardware/_controller/src/SdFat/SdFat.h b/hardware/_controller/src/SdFat/SdFat.h new file mode 100644 index 0000000..4b7216f --- /dev/null +++ b/hardware/_controller/src/SdFat/SdFat.h @@ -0,0 +1,512 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef SdFat_h +#define SdFat_h +/** + * \file + * \brief SdFat class + */ +#include "SysCall.h" +#include "BlockDriver.h" +#include "FatLib/FatLib.h" +#include "SdCard/SdioCard.h" +#if INCLUDE_SDIOS +#include "sdios.h" +#endif // INCLUDE_SDIOS +//------------------------------------------------------------------------------ +/** SdFat version 1.1.2 */ +#define SD_FAT_VERSION 10102 +//============================================================================== +/** + * \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*, oflag_t). + */ + SdBaseFile(const char* path, oflag_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*, oflag_t). + */ + SdFile(const char* path, oflag_t oflag) : PrintFile(path, oflag) {} +}; +#endif // #if ENABLE_ARDUINO_FEATURES +//----------------------------------------------------------------------------- +/** + * \class SdFileSystem + * \brief Virtual base class for %SdFat library. + */ +template +class SdFileSystem : public FatFileSystem { + public: + /** Initialize file system. + * \return true for success else false. + */ + bool begin() { + return FatFileSystem::begin(&m_card); + } + /** \return Pointer to SD card object */ + SdDriverClass *card() { + m_card.syncBlocks(); + return &m_card; + } + /** %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) { + errorPrint(pr); + SysCall::halt(); + } + /** %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) { + errorPrint(pr, msg); + SysCall::halt(); + } + /** %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) { + if (!cardErrorCode()) { + return; + } + pr->print(F("SD errorCode: 0X")); + pr->print(cardErrorCode(), HEX); + pr->print(F(",0X")); + pr->println(cardErrorData(), HEX); + } + /** %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) { + pr->print(F("error: ")); + pr->println(msg); + errorPrint(pr); + } + /** %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) { + initErrorPrint(pr); + SysCall::halt(); + } + /**Print message, error details, and halt after begin() fails. + * + * \param[in] msg Message to print. + */ + void initErrorHalt(char const *msg) { + initErrorHalt(&Serial, msg); + } + /**Print message, error details, and halt after begin() fails. + * \param[in] pr Print device. + * \param[in] msg Message to print. + */ + void initErrorHalt(Print* pr, char const *msg) { + pr->println(msg); + initErrorHalt(pr); + } + + /** Print error details after begin() fails. */ + void initErrorPrint() { + initErrorPrint(&Serial); + } + /** Print error details after begin() fails. + * + * \param[in] pr Print destination. + */ + void 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.")); + } + } + /**Print message and error details and halt after begin() fails. + * + * \param[in] msg Message to print. + */ + void initErrorPrint(char const *msg) { + initErrorPrint(&Serial, msg); + } + /**Print message and error details and halt after begin() fails. + * + * \param[in] pr Print destination. + * \param[in] msg Message to print. + */ + void initErrorPrint(Print* pr, char const *msg) { + pr->println(msg); + initErrorPrint(pr); + } +#if defined(ARDUINO) || defined(DOXYGEN) + /** %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) { + errorPrint(pr, msg); + SysCall::halt(); + } + + /** %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) { + pr->print(F("error: ")); + pr->println(msg); + errorPrint(pr); + } + /**Print message, error details, and halt after begin() fails. + * + * \param[in] msg Message to print. + */ + void initErrorHalt(const __FlashStringHelper* msg) { + initErrorHalt(&Serial, msg); + } + /**Print message, error details, and halt after begin() fails. + * \param[in] pr Print device for message. + * \param[in] msg Message to print. + */ + void initErrorHalt(Print* pr, const __FlashStringHelper* msg) { + pr->println(msg); + initErrorHalt(pr); + } + /**Print message and error details and halt after begin() fails. + * + * \param[in] msg Message to print. + */ + void initErrorPrint(const __FlashStringHelper* msg) { + initErrorPrint(&Serial, msg); + } + /**Print message and error details and halt after begin() fails. + * + * \param[in] pr Print destination. + * \param[in] msg Message to print. + */ + void initErrorPrint(Print* pr, const __FlashStringHelper* msg) { + pr->println(msg); + initErrorPrint(pr); + } +#endif // defined(ARDUINO) || defined(DOXYGEN) + /** \return The card error code */ + uint8_t cardErrorCode() { + return m_card.errorCode(); + } + /** \return the card error data */ + uint32_t cardErrorData() { + return m_card.errorData(); + } + + protected: + SdDriverClass m_card; +}; +//============================================================================== +/** + * \class SdFat + * \brief Main file system class for %SdFat library. + */ +class SdFat : public SdFileSystem { + public: +#if IMPLEMENT_SPI_PORT_SELECTION || defined(DOXYGEN) + SdFat() { + m_spi.setPort(nullptr); + } + /** Constructor with SPI port selection. + * \param[in] spiPort SPI port number. + */ + explicit SdFat(SPIClass* spiPort) { + m_spi.setPort(spiPort); + } +#endif // IMPLEMENT_SPI_PORT_SELECTION + /** Initialize SD card and file system. + * + * \param[in] csPin SD card chip select pin. + * \param[in] spiSettings SPI speed, mode, and bit order. + * \return true for success else false. + */ + bool begin(uint8_t csPin = SS, SPISettings spiSettings = SPI_FULL_SPEED) { + return m_card.begin(&m_spi, csPin, spiSettings) && + SdFileSystem::begin(); + } + /** Initialize SD card for diagnostic use only. + * + * \param[in] csPin SD card chip select pin. + * \param[in] settings SPI speed, mode, and bit order. + * \return true for success else false. + */ + bool cardBegin(uint8_t csPin = SS, SPISettings settings = SPI_FULL_SPEED) { + return m_card.begin(&m_spi, csPin, settings); + } + /** Initialize file system for diagnostic use only. + * \return true for success else false. + */ + bool fsBegin() { + return FatFileSystem::begin(card()); + } + + private: + SdFatSpiDriver m_spi; +}; +//============================================================================== +#if ENABLE_SDIO_CLASS || defined(DOXYGEN) +/** + * \class SdFatSdio + * \brief SdFat class using SDIO. + */ +class SdFatSdio : public SdFileSystem { + public: + /** Initialize SD card and file system. + * \return true for success else false. + */ + bool begin() { + return m_card.begin() && SdFileSystem::begin(); + } + /** Initialize SD card for diagnostic use only. + * + * \return true for success else false. + */ + bool cardBegin() { + return m_card.begin(); + } + /** Initialize file system for diagnostic use only. + * \return true for success else false. + */ + bool fsBegin() { + return SdFileSystem::begin(); + } +}; +#if ENABLE_SDIOEX_CLASS || defined(DOXYGEN) +//----------------------------------------------------------------------------- +/** + * \class SdFatSdioEX + * \brief SdFat class using SDIO. + */ +class SdFatSdioEX : public SdFileSystem { + public: + /** Initialize SD card and file system. + * \return true for success else false. + */ + bool begin() { + return m_card.begin() && SdFileSystem::begin(); + } + /** \return Pointer to SD card object */ + SdioCardEX* card() { + return &m_card; + } + /** Initialize SD card for diagnostic use only. + * + * \return true for success else false. + */ + bool cardBegin() { + return m_card.begin(); + } + /** Initialize file system for diagnostic use only. + * \return true for success else false. + */ + bool fsBegin() { + return SdFileSystem::begin(); + } +}; +#endif // ENABLE_SDIOEX_CLASS || defined(DOXYGEN) +#endif // ENABLE_SDIO_CLASS || defined(DOXYGEN) +//============================================================================= +#if ENABLE_SOFTWARE_SPI_CLASS || defined(DOXYGEN) +/** + * \class SdFatSoftSpi + * \brief SdFat class using software SPI. + */ +template +class SdFatSoftSpi : public SdFileSystem { + public: + /** Initialize SD card and file system. + * + * \param[in] csPin SD card chip select pin. + * \param[in] spiSettings ignored for software SPI.. + * \return true for success else false. + */ + bool begin(uint8_t csPin = SS, SPISettings spiSettings = SPI_FULL_SPEED) { + return m_card.begin(&m_spi, csPin, spiSettings) && + SdFileSystem::begin(); + } + private: + SdSpiSoftDriver m_spi; +}; +#endif // #if ENABLE_SOFTWARE_SPI_CLASS || defined(DOXYGEN) +//============================================================================== +#if ENABLE_EXTENDED_TRANSFER_CLASS || defined(DOXYGEN) +/** + * \class SdFatEX + * \brief SdFat class with extended SD I/O. + */ +class SdFatEX : public SdFileSystem { + public: +#if IMPLEMENT_SPI_PORT_SELECTION || defined(DOXYGEN) + SdFatEX() { + m_spi.setPort(nullptr); + } + /** Constructor with SPI port selection. + * \param[in] spiPort SPI port number. + */ + explicit SdFatEX(SPIClass* spiPort) { + m_spi.setPort(spiPort); + } +#endif // IMPLEMENT_SPI_PORT_SELECTION + /** Initialize SD card and file system. + * + * \param[in] csPin SD card chip select pin. + * \param[in] spiSettings SPI speed, mode, and bit order. + * \return true for success else false. + */ + bool begin(uint8_t csPin = SS, SPISettings spiSettings = SPI_FULL_SPEED) { + return m_card.begin(&m_spi, csPin, spiSettings) && + SdFileSystem::begin(); + } + + private: + SdFatSpiDriver m_spi; +}; +//============================================================================== +#if ENABLE_SOFTWARE_SPI_CLASS || defined(DOXYGEN) +/** + * \class SdFatSoftSpiEX + * \brief SdFat class using software SPI and extended SD I/O. + */ +template +class SdFatSoftSpiEX : public SdFileSystem { + public: + /** Initialize SD card and file system. + * + * \param[in] csPin SD card chip select pin. + * \param[in] spiSettings ignored for software SPI. + * \return true for success else false. + */ + bool begin(uint8_t csPin = SS, SPISettings spiSettings = SPI_FULL_SPEED) { + return m_card.begin(&m_spi, csPin, spiSettings) && + SdFileSystem::begin(); + } + private: + SdSpiSoftDriver m_spi; +}; +#endif // #if ENABLE_SOFTWARE_SPI_CLASS || defined(DOXYGEN) +#endif // ENABLE_EXTENDED_TRANSFER_CLASS || defined(DOXYGEN) +//============================================================================= +/** + * \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] csPin SD chip select pin. + * \param[in] settings SPI speed, mode, and bit order. + * \return true for success else false. + */ + bool begin(uint8_t csPin = SS, SPISettings settings = SD_SCK_MHZ(50)) { + return SdSpiCard::begin(&m_spi, csPin, settings); + } + private: + SdFatSpiDriver m_spi; +}; +#endif // SdFat_h diff --git a/hardware/_controller/src/SdFat/SdFatConfig.h b/hardware/_controller/src/SdFat/SdFatConfig.h new file mode 100644 index 0000000..d43a42c --- /dev/null +++ b/hardware/_controller/src/SdFat/SdFatConfig.h @@ -0,0 +1,235 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +/** + * \file + * \brief configuration definitions + */ +#ifndef SdFatConfig_h +#define SdFatConfig_h +#include +#include +#ifdef __AVR__ +#include +#endif // __AVR__ +//------------------------------------------------------------------------------ +/** + * Set INCLUDE_SDIOS nonzero to include sdios.h in SdFat.h. + * sdios.h provides C++ style IO Streams. + */ +#define INCLUDE_SDIOS 1 +//------------------------------------------------------------------------------ +/** + * 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 USE_LONG_FILE_NAMES 1 +//------------------------------------------------------------------------------ +/** + * If the symbol ENABLE_EXTENDED_TRANSFER_CLASS is nonzero, the class SdFatEX + * will be defined. If the symbol ENABLE_SOFTWARE_SPI_CLASS is also nonzero, + * the class SdFatSoftSpiEX will be defined. + * + * These classes used extended multi-block SD I/O for better performance. + * the SPI bus may not be shared with other devices in this mode. + */ +#define ENABLE_EXTENDED_TRANSFER_CLASS 0 +//------------------------------------------------------------------------------ +/** + * If the symbol USE_STANDARD_SPI_LIBRARY is zero, an optimized custom SPI + * driver is used if it exists. If the symbol USE_STANDARD_SPI_LIBRARY is + * one, the standard Arduino SPI.h library is used with SPI. If the symbol + * USE_STANDARD_SPI_LIBRARY is two, the SPI port can be selected with the + * constructors SdFat(SPIClass* spiPort) and SdFatEX(SPIClass* spiPort). + */ +#define USE_STANDARD_SPI_LIBRARY 0 +//------------------------------------------------------------------------------ +/** + * If the symbol ENABLE_SOFTWARE_SPI_CLASS is nonzero, the class SdFatSoftSpi + * will be defined. If ENABLE_EXTENDED_TRANSFER_CLASS is also nonzero, + * the class SdFatSoftSpiEX will be defined. + */ +#define ENABLE_SOFTWARE_SPI_CLASS 1 +//------------------------------------------------------------------------------ +/** If the symbol USE_FCNTL_H is nonzero, open flags for access modes O_RDONLY, + * O_WRONLY, O_RDWR and the open modifiers O_APPEND, O_CREAT, O_EXCL, O_SYNC + * will be defined by including the system file fcntl.h. + */ +#if defined(__AVR__) +// AVR fcntl.h does not define open flags. +#define USE_FCNTL_H 0 +#elif defined(PLATFORM_ID) +// Particle boards - use fcntl.h. +#define USE_FCNTL_H 1 +#elif defined(__arm__) +// ARM gcc defines open flags. +#define USE_FCNTL_H 1 +#elif defined(ESP32) +#define USE_FCNTL_H 1 +#else // defined(__AVR__) +#define USE_FCNTL_H 0 +#endif // defined(__AVR__) +//------------------------------------------------------------------------------ +/** + * If CHECK_FLASH_PROGRAMMING is zero, overlap of single sector flash + * programming and other operations will be allowed for faster write + * performance. + * + * Some cards will not sleep in low power mode unless CHECK_FLASH_PROGRAMMING + * is non-zero. + */ +#define CHECK_FLASH_PROGRAMMING 1 +//------------------------------------------------------------------------------ +/** + * Set MAINTAIN_FREE_CLUSTER_COUNT nonzero to keep the count of free clusters + * updated. This will increase the speed of the freeClusterCount() call + * after the first call. Extra flash will be required. + */ +#define MAINTAIN_FREE_CLUSTER_COUNT 0 +//------------------------------------------------------------------------------ +/** + * To enable SD card CRC checking set USE_SD_CRC nonzero. + * + * Set USE_SD_CRC to 1 to use a smaller CRC-CCITT function. This function + * is slower for AVR but may be fast for ARM and other processors. + * + * Set USE_SD_CRC to 2 to used a larger table driven CRC-CCITT function. This + * function is faster for AVR but may be slower for ARM and other processors. + */ +#define USE_SD_CRC 1 +//------------------------------------------------------------------------------ +/** + * Handle Watchdog Timer for WiFi modules. + * + * Yield will be called before accessing the SPI bus if it has been more + * than WDT_YIELD_TIME_MICROS microseconds since the last yield call by SdFat. + */ +#if defined(PLATFORM_ID) || defined(ESP8266) +// If Particle device or ESP8266 call yield. +#define WDT_YIELD_TIME_MICROS 100000 +#else +#define WDT_YIELD_TIME_MICROS 0 +#endif +//------------------------------------------------------------------------------ +/** + * Set FAT12_SUPPORT nonzero to enable use of FAT12 volumes. + * FAT12 has not been well tested and requires additional flash. + */ +#define FAT12_SUPPORT 0 +//------------------------------------------------------------------------------ +/** + * Set DESTRUCTOR_CLOSES_FILE nonzero to close a file in its destructor. + * + * Causes use of lots of heap in ARM. + */ +#define DESTRUCTOR_CLOSES_FILE 1 +//------------------------------------------------------------------------------ +/** + * Call flush for endl if ENDL_CALLS_FLUSH is nonzero + * + * 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 SD 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. + */ +#define ENDL_CALLS_FLUSH 0 +//------------------------------------------------------------------------------ +/** + * Set USE_SEPARATE_FAT_CACHE nonzero to use a second 512 byte cache + * 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 +#else // __arm__ +#define USE_SEPARATE_FAT_CACHE 0 +#endif // __arm__ +//------------------------------------------------------------------------------ +/** + * 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_IO 0 +#else // RAMEND +#define USE_MULTI_BLOCK_IO 1 +#endif // RAMEND +//----------------------------------------------------------------------------- +/** Enable SDIO driver if available. */ +#if defined(__MK64FX512__) || defined(__MK66FX1M0__) +#define ENABLE_SDIO_CLASS 1 +#define ENABLE_SDIOEX_CLASS 1 +#else // ENABLE_SDIO_CLASS +#define ENABLE_SDIO_CLASS 0 +#endif // ENABLE_SDIO_CLASS +//------------------------------------------------------------------------------ +/** + * Determine the default SPI configuration. + */ +#if defined(__STM32F1__) || defined(__STM32F4__) || defined(PLATFORM_ID) +// has multiple SPI ports +#define SD_HAS_CUSTOM_SPI 2 +#elif defined(__AVR__)\ + || defined(__SAM3X8E__) || defined(__SAM3X8H__)\ + || (defined(__arm__) && defined(CORE_TEENSY))\ + || defined(ESP8266) +#define SD_HAS_CUSTOM_SPI 1 +#else // SD_HAS_CUSTOM_SPI +// Use standard SPI library. +#define SD_HAS_CUSTOM_SPI 0 +#endif // SD_HAS_CUSTOM_SPI +//------------------------------------------------------------------------------ +/** + * Check if API to select HW SPI port is needed. + */ +#if USE_STANDARD_SPI_LIBRARY > 1 || SD_HAS_CUSTOM_SPI > 1 +#define IMPLEMENT_SPI_PORT_SELECTION 1 +#else // IMPLEMENT_SPI_PORT_SELECTION +#define IMPLEMENT_SPI_PORT_SELECTION 0 +#endif // IMPLEMENT_SPI_PORT_SELECTION +#endif // SdFatConfig_h diff --git a/hardware/_controller/src/SdFat/SpiDriver/DigitalPin.h b/hardware/_controller/src/SdFat/SpiDriver/DigitalPin.h new file mode 100644 index 0000000..9f8b8e4 --- /dev/null +++ b/hardware/_controller/src/SdFat/SpiDriver/DigitalPin.h @@ -0,0 +1,386 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +/** + * @file + * @brief Fast Digital Pin functions + * + * @defgroup digitalPin Fast Pin I/O + * @details Fast Digital I/O functions and template class. + * @{ + */ +#ifndef DigitalPin_h +#define DigitalPin_h +#if defined(__AVR__) || defined(DOXYGEN) +#include +/** GpioPinMap type */ +struct GpioPinMap_t { + volatile uint8_t* pin; /**< address of PIN for this pin */ + volatile uint8_t* ddr; /**< address of DDR for this pin */ + volatile uint8_t* port; /**< address of PORT for this pin */ + uint8_t mask; /**< bit mask for this pin */ +}; + +/** Initializer macro. */ +#define GPIO_PIN(reg, bit) {&PIN##reg, &DDR##reg, &PORT##reg, 1 << bit} + +// Include pin map for current board. +#include "boards/GpioPinMap.h" +//------------------------------------------------------------------------------ +/** generate bad pin number error */ +void badPinNumber(void) + __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. + */ +static inline __attribute__((always_inline)) +void badPinCheck(uint8_t pin) { + if (!__builtin_constant_p(pin) || pin >= NUM_DIGITAL_PINS) { + badPinNumber(); + } +} +//------------------------------------------------------------------------------ +/** DDR register address + * @param[in] pin Arduino pin number + * @return register address + */ +static inline __attribute__((always_inline)) +volatile uint8_t* ddrReg(uint8_t pin) { + badPinCheck(pin); + return GpioPinMap[pin].ddr; +} +//------------------------------------------------------------------------------ +/** Bit mask for pin + * @param[in] pin Arduino pin number + * @return mask + */ +static inline __attribute__((always_inline)) +uint8_t pinMask(uint8_t pin) { + badPinCheck(pin); + return GpioPinMap[pin].mask; +} +//------------------------------------------------------------------------------ +/** PIN register address + * @param[in] pin Arduino pin number + * @return register address + */ +static inline __attribute__((always_inline)) +volatile uint8_t* pinReg(uint8_t pin) { + badPinCheck(pin); + return GpioPinMap[pin].pin; +} +//------------------------------------------------------------------------------ +/** PORT register address + * @param[in] pin Arduino pin number + * @return register address + */ +static inline __attribute__((always_inline)) +volatile uint8_t* portReg(uint8_t pin) { + badPinCheck(pin); + return GpioPinMap[pin].port; +} +//------------------------------------------------------------------------------ +/** Fast write helper. + * @param[in] address I/O register address + * @param[in] mask bit mask for pin + * @param[in] level value for bit + */ +static inline __attribute__((always_inline)) +void fastBitWriteSafe(volatile uint8_t* address, uint8_t mask, bool level) { + uint8_t s; + if (address > reinterpret_cast(0X3F)) { + s = SREG; + cli(); + } + if (level) { + *address |= mask; + } else { + *address &= ~mask; + } + if (address > reinterpret_cast(0X3F)) { + SREG = s; + } +} +//------------------------------------------------------------------------------ +/** Read pin value. + * @param[in] pin Arduino pin number + * @return value read + */ +static inline __attribute__((always_inline)) +bool fastDigitalRead(uint8_t pin) { + return *pinReg(pin) & pinMask(pin); +} +//------------------------------------------------------------------------------ +/** Toggle a 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. + */ +static inline __attribute__((always_inline)) +void fastDigitalToggle(uint8_t pin) { + if (pinReg(pin) > reinterpret_cast(0X3F)) { + // must write bit to high address port + *pinReg(pin) = pinMask(pin); + } else { + // will compile to sbi and PIN register will not be read. + *pinReg(pin) |= pinMask(pin); + } +} +//------------------------------------------------------------------------------ +/** Set pin value. + * @param[in] pin Arduino pin number + * @param[in] level value to write + */ +static inline __attribute__((always_inline)) +void fastDigitalWrite(uint8_t pin, bool level) { + fastBitWriteSafe(portReg(pin), pinMask(pin), level); +} +//------------------------------------------------------------------------------ +/** Write the DDR register. + * @param[in] pin Arduino pin number + * @param[in] level value to write + */ +static inline __attribute__((always_inline)) +void fastDdrWrite(uint8_t pin, bool level) { + fastBitWriteSafe(ddrReg(pin), pinMask(pin), level); +} +//------------------------------------------------------------------------------ +/** Set pin mode. + * @param[in] pin Arduino pin number + * @param[in] mode INPUT, OUTPUT, or INPUT_PULLUP. + * + * The internal pullup resistors will be enabled if mode is INPUT_PULLUP + * and disabled if the mode is INPUT. + */ +static inline __attribute__((always_inline)) +void fastPinMode(uint8_t pin, uint8_t mode) { + fastDdrWrite(pin, mode == OUTPUT); + if (mode != OUTPUT) { + fastDigitalWrite(pin, mode == INPUT_PULLUP); + } +} +#else // defined(__AVR__) +#if defined(CORE_TEENSY) +//------------------------------------------------------------------------------ +/** read pin value + * @param[in] pin Arduino pin number + * @return value read + */ +static inline __attribute__((always_inline)) +bool fastDigitalRead(uint8_t pin) { + return *portInputRegister(pin); +} +//------------------------------------------------------------------------------ +/** Set pin value + * @param[in] pin Arduino pin number + * @param[in] level value to write + */ +static inline __attribute__((always_inline)) +void fastDigitalWrite(uint8_t pin, bool value) { + if (value) { + *portSetRegister(pin) = 1; + } else { + *portClearRegister(pin) = 1; + } +} +#elif defined(__SAM3X8E__) || defined(__SAM3X8H__) +//------------------------------------------------------------------------------ +/** read pin value + * @param[in] pin Arduino pin number + * @return value read + */ +static inline __attribute__((always_inline)) +bool fastDigitalRead(uint8_t pin) { + return g_APinDescription[pin].pPort->PIO_PDSR & g_APinDescription[pin].ulPin; +} +//------------------------------------------------------------------------------ +/** Set pin value + * @param[in] pin Arduino pin number + * @param[in] level value to write + */ +static inline __attribute__((always_inline)) +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; + } +} +#elif defined(ESP8266) +//------------------------------------------------------------------------------ +/** Set pin value + * @param[in] pin Arduino pin number + * @param[in] val value to write + */ +static inline __attribute__((always_inline)) +void fastDigitalWrite(uint8_t pin, uint8_t val) { + if (pin < 16) { + if (val) { + GPOS = (1 << pin); + } else { + GPOC = (1 << pin); + } + } else if (pin == 16) { + if (val) { + GP16O |= 1; + } else { + GP16O &= ~1; + } + } +} +//------------------------------------------------------------------------------ +/** Read pin value + * @param[in] pin Arduino pin number + * @return value read + */ +static inline __attribute__((always_inline)) +bool fastDigitalRead(uint8_t pin) { + if (pin < 16) { + return GPIP(pin); + } else if (pin == 16) { + return GP16I & 0x01; + } + return 0; +} +#else // CORE_TEENSY +//------------------------------------------------------------------------------ +inline void fastDigitalWrite(uint8_t pin, bool value) { + digitalWrite(pin, value); +} +//------------------------------------------------------------------------------ +inline bool fastDigitalRead(uint8_t pin) { + return digitalRead(pin); +} +#endif // CORE_TEENSY +//------------------------------------------------------------------------------ +inline void fastDigitalToggle(uint8_t pin) { + fastDigitalWrite(pin, !fastDigitalRead(pin)); +} +//------------------------------------------------------------------------------ +inline void fastPinMode(uint8_t pin, uint8_t mode) { + pinMode(pin, mode); +} +#endif // __AVR__ +//------------------------------------------------------------------------------ +/** set pin configuration + * @param[in] pin Arduino pin number + * @param[in] mode mode INPUT or OUTPUT. + * @param[in] level If mode is output, set level high/low. + * If mode is input, enable or disable the pin's 20K pullup. + */ +#define fastPinConfig(pin, mode, level)\ + {fastPinMode(pin, mode); fastDigitalWrite(pin, level);} +//============================================================================== +/** + * @class DigitalPin + * @brief Fast digital port I/O + */ +template +class DigitalPin { + public: + //---------------------------------------------------------------------------- + /** Constructor */ + DigitalPin() {} + //---------------------------------------------------------------------------- + /** Asignment operator. + * @param[in] value If true set the pin's level high else set the + * pin's level low. + * + * @return This DigitalPin instance. + */ + inline DigitalPin & operator = (bool value) __attribute__((always_inline)) { + write(value); + return *this; + } + //---------------------------------------------------------------------------- + /** Parenthesis operator. + * @return Pin's level + */ + inline operator bool () const __attribute__((always_inline)) { + return read(); + } + //---------------------------------------------------------------------------- + /** Set pin configuration. + * @param[in] mode: INPUT or OUTPUT. + * @param[in] level If mode is OUTPUT, set level high/low. + * If mode is INPUT, enable or disable the pin's 20K pullup. + */ + inline __attribute__((always_inline)) + void config(uint8_t mode, bool level) { + fastPinConfig(PinNumber, mode, level); + } + //---------------------------------------------------------------------------- + /** + * Set pin level high if output mode or enable 20K pullup if input mode. + */ + inline __attribute__((always_inline)) + void high() {write(true);} + //---------------------------------------------------------------------------- + /** + * Set pin level low if output mode or disable 20K pullup if input mode. + */ + inline __attribute__((always_inline)) + void low() {write(false);} + //---------------------------------------------------------------------------- + /** + * Set pin mode. + * @param[in] mode: INPUT, OUTPUT, or INPUT_PULLUP. + * + * The internal pullup resistors will be enabled if mode is INPUT_PULLUP + * and disabled if the mode is INPUT. + */ + inline __attribute__((always_inline)) + void mode(uint8_t mode) { + fastPinMode(PinNumber, mode); + } + //---------------------------------------------------------------------------- + /** @return Pin's level. */ + inline __attribute__((always_inline)) + bool read() const { + return fastDigitalRead(PinNumber); + } + //---------------------------------------------------------------------------- + /** 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. + */ + inline __attribute__((always_inline)) + void toggle() { + fastDigitalToggle(PinNumber); + } + //---------------------------------------------------------------------------- + /** Write the pin's level. + * @param[in] value If true set the pin's level high else set the + * pin's level low. + */ + inline __attribute__((always_inline)) + void write(bool value) { + fastDigitalWrite(PinNumber, value); + } +}; +#endif // DigitalPin_h +/** @} */ diff --git a/hardware/_controller/src/SdFat/SpiDriver/SdSpiBaseDriver.h b/hardware/_controller/src/SdFat/SpiDriver/SdSpiBaseDriver.h new file mode 100644 index 0000000..8a33a46 --- /dev/null +++ b/hardware/_controller/src/SdFat/SpiDriver/SdSpiBaseDriver.h @@ -0,0 +1,79 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef SdSpiBaseDriver_h +#define SdSpiBaseDriver_h +/** + * \class SdSpiBaseDriver + * \brief SPI base driver. + */ +class SdSpiBaseDriver { + public: + /** Set SPI options for access to SD/SDHC cards. + * + */ + virtual void activate() = 0; + /** Initialize the SPI bus. + * + * \param[in] chipSelectPin SD card chip select pin. + */ + virtual void begin(uint8_t chipSelectPin) = 0; + /** + * End SPI transaction. + */ + virtual void deactivate() = 0; + /** 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; + /** Set CS low. */ + virtual void select() = 0; + /** Save SPI settings. + * \param[in] spiSettings SPI speed, mode, and bit order. + */ + virtual void setSpiSettings(SPISettings spiSettings) = 0; + /** Set CS high. */ + virtual void unselect() = 0; +}; +#endif // SdSpiBaseDriver_h diff --git a/hardware/_controller/src/SdFat/SpiDriver/SdSpiDriver.h b/hardware/_controller/src/SdFat/SpiDriver/SdSpiDriver.h new file mode 100644 index 0000000..c697605 --- /dev/null +++ b/hardware/_controller/src/SdFat/SpiDriver/SdSpiDriver.h @@ -0,0 +1,438 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +/** + * \file + * \brief SpiDriver classes + */ +#ifndef SdSpiDriver_h +#define SdSpiDriver_h +#include +#include "SPI.h" +#include "SdSpiBaseDriver.h" +#include "../SdFatConfig.h" +//------------------------------------------------------------------------------ +/** SDCARD_SPI is defined if board has built-in SD card socket */ +#ifndef SDCARD_SPI +#define SDCARD_SPI SPI +#endif // SDCARD_SPI +//------------------------------------------------------------------------------ +/** + * \class SdSpiLibDriver + * \brief SdSpiLibDriver - use standard SPI library. + */ +#if ENABLE_SOFTWARE_SPI_CLASS +class SdSpiLibDriver : public SdSpiBaseDriver { +#else // ENABLE_SOFTWARE_SPI_CLASS +class SdSpiLibDriver { +#endif // ENABLE_SOFTWARE_SPI_CLASS + public: +#if IMPLEMENT_SPI_PORT_SELECTION + /** Activate SPI hardware. */ + void activate() { + m_spi->beginTransaction(m_spiSettings); + } + /** Deactivate SPI hardware. */ + void deactivate() { + m_spi->endTransaction(); + } + /** Initialize the SPI bus. + * + * \param[in] csPin SD card chip select pin. + */ + void begin(uint8_t csPin) { + m_csPin = csPin; + digitalWrite(csPin, HIGH); + pinMode(csPin, OUTPUT); + m_spi->begin(); + } + /** Receive a byte. + * + * \return The byte. + */ + uint8_t receive() { + return m_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] = m_spi->transfer(0XFF); + } + return 0; + } + /** Send a byte. + * + * \param[in] data Byte to send + */ + void send(uint8_t data) { + m_spi->transfer(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++) { + m_spi->transfer(buf[i]); + } + } +#else // IMPLEMENT_SPI_PORT_SELECTION + /** Activate SPI hardware. */ + void activate() { + SDCARD_SPI.beginTransaction(m_spiSettings); + } + /** Deactivate SPI hardware. */ + void deactivate() { + SDCARD_SPI.endTransaction(); + } + /** Initialize the SPI bus. + * + * \param[in] csPin SD card chip select pin. + */ + void begin(uint8_t csPin) { + m_csPin = csPin; + digitalWrite(csPin, HIGH); + pinMode(csPin, OUTPUT); + SDCARD_SPI.begin(); + } + /** Receive a byte. + * + * \return The byte. + */ + uint8_t receive() { + return SDCARD_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] = SDCARD_SPI.transfer(0XFF); + } + return 0; + } + /** Send a byte. + * + * \param[in] data Byte to send + */ + void send(uint8_t data) { + SDCARD_SPI.transfer(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++) { + SDCARD_SPI.transfer(buf[i]); + } + } +#endif // IMPLEMENT_SPI_PORT_SELECTION + /** Set CS low. */ + void select() { + digitalWrite(m_csPin, LOW); + } + /** Save SPISettings. + * + * \param[in] spiSettings SPI speed, mode, and byte order. + */ + void setSpiSettings(SPISettings spiSettings) { + m_spiSettings = spiSettings; + } + /** Set CS high. */ + void unselect() { + digitalWrite(m_csPin, HIGH); + } +#if IMPLEMENT_SPI_PORT_SELECTION || defined(DOXYGEN) + /** Set SPI port. + * \param[in] spiPort Hardware SPI port. + */ + void setPort(SPIClass* spiPort) { + m_spi = spiPort ? spiPort : &SDCARD_SPI; + } + private: + SPIClass* m_spi; +#else // IMPLEMENT_SPI_PORT_SELECTION + private: +#endif // IMPLEMENT_SPI_PORT_SELECTION + SPISettings m_spiSettings; + uint8_t m_csPin; +}; +//------------------------------------------------------------------------------ +/** + * \class SdSpiAltDriver + * \brief Optimized SPI class for access to SD and SDHC flash memory cards. + */ +#if ENABLE_SOFTWARE_SPI_CLASS +class SdSpiAltDriver : public SdSpiBaseDriver { +#else // ENABLE_SOFTWARE_SPI_CLASS +class SdSpiAltDriver { +#endif // ENABLE_SOFTWARE_SPI_CLASS + public: + /** Activate SPI hardware. */ + void activate(); + /** Deactivate SPI hardware. */ + void deactivate(); + /** Initialize the SPI bus. + * + * \param[in] csPin SD card chip select pin. + */ + void begin(uint8_t csPin); + /** Receive a byte. + * + * \return The byte. + */ + uint8_t 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); + /** Send a byte. + * + * \param[in] data Byte to send + */ + void send(uint8_t 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); + /** Set CS low. */ + void select() { + digitalWrite(m_csPin, LOW); + } + /** Save SPISettings. + * + * \param[in] spiSettings SPI speed, mode, and byte order. + */ + void setSpiSettings(SPISettings spiSettings) { + m_spiSettings = spiSettings; + } + /** Set CS high. */ + void unselect() { + digitalWrite(m_csPin, HIGH); + } +#if IMPLEMENT_SPI_PORT_SELECTION || defined(DOXYGEN) + /** Set SPI port number. + * \param[in] spiPort Hardware SPI port. + */ + void setPort(SPIClass* spiPort) { + m_spi = spiPort ? spiPort : &SDCARD_SPI; + } + private: + SPIClass* m_spi; +#else // IMPLEMENT_SPI_PORT_SELECTION + private: +#endif // IMPLEMENT_SPI_PORT_SELECTION + SPISettings m_spiSettings; + uint8_t m_csPin; +}; +//------------------------------------------------------------------------------ +#if ENABLE_SOFTWARE_SPI_CLASS || defined(DOXYGEN) +#ifdef ARDUINO +#include "SoftSPI.h" +#elif defined(PLATFORM_ID) // Only defined if a Particle device +#include "SoftSPIParticle.h" +#endif // ARDUINO +/** + * \class SdSpiSoftDriver + * \brief Software SPI class for access to SD and SDHC flash memory cards. + */ +template +class SdSpiSoftDriver : public SdSpiBaseDriver { + public: + /** Dummy activate SPI hardware for software SPI */ + void activate() {} + /** Dummy deactivate SPI hardware for software SPI */ + void deactivate() {} + /** Initialize the SPI bus. + * + * \param[in] csPin SD card chip select pin. + */ + void begin(uint8_t csPin) { + m_csPin = csPin; + pinMode(m_csPin, OUTPUT); + digitalWrite(m_csPin, HIGH); + m_spi.begin(); + } + /** 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]); + } + } + /** Set CS low. */ + void select() { + digitalWrite(m_csPin, LOW); + } + /** Save SPISettings. + * + * \param[in] spiSettings SPI speed, mode, and byte order. + */ + void setSpiSettings(SPISettings spiSettings) { + (void)spiSettings; + } + /** Set CS high. */ + void unselect() { + digitalWrite(m_csPin, HIGH); + } + + private: + uint8_t m_csPin; + SoftSPI m_spi; +}; +#endif // ENABLE_SOFTWARE_SPI_CLASS || defined(DOXYGEN) +//------------------------------------------------------------------------------ +// Choose SPI driver for SdFat and SdFatEX classes. +#if USE_STANDARD_SPI_LIBRARY || !SD_HAS_CUSTOM_SPI +/** SdFat uses Arduino library SPI. */ +typedef SdSpiLibDriver SdFatSpiDriver; +#else // USE_STANDARD_SPI_LIBRARY || !SD_HAS_CUSTOM_SPI +/** SdFat uses custom fast SPI. */ +typedef SdSpiAltDriver SdFatSpiDriver; +#endif // USE_STANDARD_SPI_LIBRARY || !SD_HAS_CUSTOM_SPI + +/** typedef for for SdSpiCard class. */ +#if ENABLE_SOFTWARE_SPI_CLASS +// Need virtual driver. +typedef SdSpiBaseDriver SdSpiDriver; +#else // ENABLE_SOFTWARE_SPI_CLASS +// Don't need virtual driver. +typedef SdFatSpiDriver SdSpiDriver; +#endif // ENABLE_SOFTWARE_SPI_CLASS +//============================================================================== +// Use of in-line for AVR to save flash. +#ifdef __AVR__ +//------------------------------------------------------------------------------ +inline void SdSpiAltDriver::begin(uint8_t csPin) { + m_csPin = csPin; + pinMode(m_csPin, OUTPUT); + digitalWrite(m_csPin, HIGH); + SPI.begin(); +} +//------------------------------------------------------------------------------ +inline void SdSpiAltDriver::activate() { + SPI.beginTransaction(m_spiSettings); +} +//------------------------------------------------------------------------------ +inline void SdSpiAltDriver::deactivate() { + SPI.endTransaction(); +} +//------------------------------------------------------------------------------ +inline uint8_t SdSpiAltDriver::receive() { + SPDR = 0XFF; + while (!(SPSR & (1 << SPIF))) {} + return SPDR; +} +//------------------------------------------------------------------------------ +inline uint8_t SdSpiAltDriver::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; +} +//------------------------------------------------------------------------------ +inline void SdSpiAltDriver::send(uint8_t data) { + SPDR = data; + while (!(SPSR & (1 << SPIF))) {} +} +//------------------------------------------------------------------------------ +inline void SdSpiAltDriver::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 // __AVR__ +#endif // SdSpiDriver_h diff --git a/hardware/_controller/src/SdFat/SpiDriver/SdSpiESP8266.cpp b/hardware/_controller/src/SdFat/SpiDriver/SdSpiESP8266.cpp new file mode 100644 index 0000000..5594a45 --- /dev/null +++ b/hardware/_controller/src/SdFat/SpiDriver/SdSpiESP8266.cpp @@ -0,0 +1,104 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#if defined(ESP8266) +#include "SdSpiDriver.h" +//------------------------------------------------------------------------------ +/** Initialize the SPI bus. + * + * \param[in] chipSelectPin SD card chip select pin. + */ +void SdSpiAltDriver::begin(uint8_t csPin) { + m_csPin = csPin; + pinMode(m_csPin, OUTPUT); + digitalWrite(m_csPin, HIGH); + SPI.begin(); +} +//------------------------------------------------------------------------------ +/** Set SPI options for access to SD/SDHC cards. + * + */ +void SdSpiAltDriver::activate() { + SPI.beginTransaction(m_spiSettings); +} +//------------------------------------------------------------------------------ +void SdSpiAltDriver::deactivate() { + // Note: endTransaction is an empty function on ESP8266. + SPI.endTransaction(); +} +//------------------------------------------------------------------------------ +/** Receive a byte. + * + * \return The byte. + */ +uint8_t SdSpiAltDriver::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 SdSpiAltDriver::receive(uint8_t* buf, size_t n) { + // Adjust to 32-bit alignment. + while ((reinterpret_cast(buf) & 0X3) && n) { + *buf++ = SPI.transfer(0xff); + n--; + } + // Do multiple of four byte transfers. + size_t n4 = 4*(n/4); + SPI.transferBytes(0, buf, n4); + + // Transfer up to three remaining bytes. + for (buf += n4, n -= n4; n; n--) { + *buf++ = SPI.transfer(0xff); + } + return 0; +} +//------------------------------------------------------------------------------ +/** Send a byte. + * + * \param[in] b Byte to send + */ +void SdSpiAltDriver::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 SdSpiAltDriver::send(const uint8_t* buf , size_t n) { + // Adjust to 32-bit alignment. + while ((reinterpret_cast(buf) & 0X3) && n) { + SPI.transfer(*buf++); + n--; + } + SPI.transferBytes(const_cast(buf), 0, n); +} +#endif // defined(ESP8266) diff --git a/hardware/_controller/src/SdFat/SpiDriver/SdSpiParticle.cpp b/hardware/_controller/src/SdFat/SpiDriver/SdSpiParticle.cpp new file mode 100644 index 0000000..ee75bbf --- /dev/null +++ b/hardware/_controller/src/SdFat/SpiDriver/SdSpiParticle.cpp @@ -0,0 +1,102 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#if defined(PLATFORM_ID) +#include "SdSpiDriver.h" +static volatile bool SPI_DMA_TransferCompleted = false; +//----------------------------------------------------------------------------- +static void SD_SPI_DMA_TransferComplete_Callback(void) { + SPI_DMA_TransferCompleted = true; +} +//------------------------------------------------------------------------------ +/** Set SPI options for access to SD/SDHC cards. + * + * \param[in] divisor SCK clock divider relative to the APB1 or APB2 clock. + */ +void SdSpiAltDriver::activate() { + m_spi->beginTransaction(m_spiSettings); +} +//------------------------------------------------------------------------------ +/** Initialize the SPI bus. + * + * \param[in] chipSelectPin SD card chip select pin. + */ +void SdSpiAltDriver::begin(uint8_t csPin) { + m_csPin = csPin; + m_spi->begin(); + pinMode(m_csPin, OUTPUT); + digitalWrite(m_csPin, HIGH); +} +//------------------------------------------------------------------------------ +/** + * End SPI transaction. + */ +void SdSpiAltDriver::deactivate() { + m_spi->endTransaction(); +} +//------------------------------------------------------------------------------ +/** Receive a byte. + * + * \return The byte. + */ +uint8_t SdSpiAltDriver::receive() { + return m_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 SdSpiAltDriver::receive(uint8_t* buf, size_t n) { + SPI_DMA_TransferCompleted = false; + m_spi->transfer(nullptr, buf, n, SD_SPI_DMA_TransferComplete_Callback); + while (!SPI_DMA_TransferCompleted) {} + return 0; +} +//------------------------------------------------------------------------------ +/** Send a byte. + * + * \param[in] b Byte to send + */ +void SdSpiAltDriver::send(uint8_t b) { + m_spi->transfer(b); +} +//------------------------------------------------------------------------------ +/** Send multiple bytes. + * + * \param[in] buf Buffer for data to be sent. + * \param[in] n Number of bytes to send. + */ +void SdSpiAltDriver::send(const uint8_t* buf , size_t n) { + SPI_DMA_TransferCompleted = false; + + m_spi->transfer(const_cast(buf), nullptr, n, + SD_SPI_DMA_TransferComplete_Callback); + + while (!SPI_DMA_TransferCompleted) {} +} +#endif // defined(PLATFORM_ID) diff --git a/hardware/_controller/src/SdFat/SpiDriver/SdSpiSAM3X.cpp b/hardware/_controller/src/SdFat/SpiDriver/SdSpiSAM3X.cpp new file mode 100644 index 0000000..2da85cd --- /dev/null +++ b/hardware/_controller/src/SdFat/SpiDriver/SdSpiSAM3X.cpp @@ -0,0 +1,218 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "SdSpiDriver.h" +#if defined(__SAM3X8E__) || defined(__SAM3X8H__) +/** Use SAM3X DMAC if nonzero */ +#define USE_SAM3X_DMAC 1 +/** Use extra Bus Matrix arbitration fix if nonzero */ +#define USE_SAM3X_BUS_MATRIX_FIX 0 +/** Time in ms for DMA receive timeout */ +#define SAM3X_DMA_TIMEOUT 100 +/** chip select register number */ +#define SPI_CHIP_SEL 3 +/** DMAC receive channel */ +#define SPI_DMAC_RX_CH 1 +/** DMAC transmit channel */ +#define SPI_DMAC_TX_CH 0 +/** DMAC Channel HW Interface Number for SPI TX. */ +#define SPI_TX_IDX 1 +/** DMAC Channel HW Interface Number for SPI RX. */ +#define SPI_RX_IDX 2 +//------------------------------------------------------------------------------ +/** Disable DMA Controller. */ +static void dmac_disable() { + DMAC->DMAC_EN &= (~DMAC_EN_ENABLE); +} +/** Enable DMA Controller. */ +static void dmac_enable() { + DMAC->DMAC_EN = DMAC_EN_ENABLE; +} +/** Disable DMA Channel. */ +static void dmac_channel_disable(uint32_t ul_num) { + DMAC->DMAC_CHDR = DMAC_CHDR_DIS0 << ul_num; +} +/** Enable DMA Channel. */ +static void dmac_channel_enable(uint32_t ul_num) { + DMAC->DMAC_CHER = DMAC_CHER_ENA0 << ul_num; +} +/** Poll for transfer complete. */ +static bool dmac_channel_transfer_done(uint32_t ul_num) { + return (DMAC->DMAC_CHSR & (DMAC_CHSR_ENA0 << ul_num)) ? false : true; +} +//------------------------------------------------------------------------------ +void SdSpiAltDriver::begin(uint8_t csPin) { + m_csPin = csPin; + pinMode(m_csPin, OUTPUT); + digitalWrite(m_csPin, HIGH); +SPI.begin(); +#if USE_SAM3X_DMAC + pmc_enable_periph_clk(ID_DMAC); + dmac_disable(); + DMAC->DMAC_GCFG = DMAC_GCFG_ARB_CFG_FIXED; + dmac_enable(); +#if USE_SAM3X_BUS_MATRIX_FIX + MATRIX->MATRIX_WPMR = 0x4d415400; + MATRIX->MATRIX_MCFG[1] = 1; + MATRIX->MATRIX_MCFG[2] = 1; + MATRIX->MATRIX_SCFG[0] = 0x01000010; + MATRIX->MATRIX_SCFG[1] = 0x01000010; + MATRIX->MATRIX_SCFG[7] = 0x01000010; +#endif // USE_SAM3X_BUS_MATRIX_FIX +#endif // USE_SAM3X_DMAC +} +//------------------------------------------------------------------------------ +// start RX DMA +static void spiDmaRX(uint8_t* dst, uint16_t count) { + dmac_channel_disable(SPI_DMAC_RX_CH); + DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_SADDR = (uint32_t)&SPI0->SPI_RDR; + 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->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->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_channel_enable(SPI_DMAC_RX_CH); +} +//------------------------------------------------------------------------------ +// start TX DMA +static void spiDmaTX(const uint8_t* src, uint16_t count) { + static uint8_t ff = 0XFF; + uint32_t src_incr = DMAC_CTRLB_SRC_INCR_INCREMENTING; + if (!src) { + src = &ff; + src_incr = DMAC_CTRLB_SRC_INCR_FIXED; + } + dmac_channel_disable(SPI_DMAC_TX_CH); + DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_SADDR = (uint32_t)src; + 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->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->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; + + dmac_channel_enable(SPI_DMAC_TX_CH); +} +//------------------------------------------------------------------------------ +// initialize SPI controller +void SdSpiAltDriver::activate() { + SPI.beginTransaction(m_spiSettings); + + Spi* pSpi = SPI0; + // Save the divisor + uint32_t scbr = pSpi->SPI_CSR[SPI_CHIP_SEL] & 0XFF00; + // Disable SPI + pSpi->SPI_CR = SPI_CR_SPIDIS; + // reset SPI + pSpi->SPI_CR = SPI_CR_SWRST; + // no mode fault detection, set master mode + pSpi->SPI_MR = SPI_PCS(SPI_CHIP_SEL) | SPI_MR_MODFDIS | SPI_MR_MSTR; + // mode 0, 8-bit, + pSpi->SPI_CSR[SPI_CHIP_SEL] = scbr | SPI_CSR_CSAAT | SPI_CSR_NCPHA; + // enable SPI + pSpi->SPI_CR |= SPI_CR_SPIEN; +} +//------------------------------------------------------------------------------ +void SdSpiAltDriver::deactivate() { + SPI.endTransaction(); +} +//------------------------------------------------------------------------------ +static inline uint8_t spiTransfer(uint8_t b) { + Spi* pSpi = SPI0; + + pSpi->SPI_TDR = b; + while ((pSpi->SPI_SR & SPI_SR_RDRF) == 0) {} + b = pSpi->SPI_RDR; + return b; +} +//------------------------------------------------------------------------------ +/** SPI receive a byte */ +uint8_t SdSpiAltDriver::receive() { + return spiTransfer(0XFF); +} +//------------------------------------------------------------------------------ +/** SPI receive multiple bytes */ +uint8_t SdSpiAltDriver::receive(uint8_t* buf, size_t n) { + Spi* pSpi = SPI0; + int rtn = 0; +#if USE_SAM3X_DMAC + // clear overrun error + pSpi->SPI_SR; + + spiDmaRX(buf, n); + spiDmaTX(0, n); + + uint32_t m = millis(); + while (!dmac_channel_transfer_done(SPI_DMAC_RX_CH)) { + if ((millis() - m) > SAM3X_DMA_TIMEOUT) { + dmac_channel_disable(SPI_DMAC_RX_CH); + dmac_channel_disable(SPI_DMAC_TX_CH); + rtn = 2; + break; + } + } + 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; + while ((pSpi->SPI_SR & SPI_SR_RDRF) == 0) {} + buf[i] = pSpi->SPI_RDR; + } +#endif // USE_SAM3X_DMAC + return rtn; +} +//------------------------------------------------------------------------------ +/** SPI send a byte */ +void SdSpiAltDriver::send(uint8_t b) { + spiTransfer(b); +} +//------------------------------------------------------------------------------ +void SdSpiAltDriver::send(const uint8_t* buf , size_t n) { + Spi* pSpi = SPI0; +#if USE_SAM3X_DMAC + spiDmaTX(buf, n); + while (!dmac_channel_transfer_done(SPI_DMAC_TX_CH)) {} +#else // #if USE_SAM3X_DMAC + while ((pSpi->SPI_SR & SPI_SR_TXEMPTY) == 0) {} + for (size_t i = 0; i < n; i++) { + pSpi->SPI_TDR = buf[i]; + while ((pSpi->SPI_SR & SPI_SR_TDRE) == 0) {} + } +#endif // #if USE_SAM3X_DMAC + while ((pSpi->SPI_SR & SPI_SR_TXEMPTY) == 0) {} + // leave RDR empty + pSpi->SPI_RDR; +} +#endif // defined(__SAM3X8E__) || defined(__SAM3X8H__) diff --git a/hardware/_controller/src/SdFat/SpiDriver/SdSpiSTM32.cpp b/hardware/_controller/src/SdFat/SpiDriver/SdSpiSTM32.cpp new file mode 100644 index 0000000..629775e --- /dev/null +++ b/hardware/_controller/src/SdFat/SpiDriver/SdSpiSTM32.cpp @@ -0,0 +1,105 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#if defined(__STM32F1__) || defined(__STM32F4__) +#include "SdSpiDriver.h" +#if defined(__STM32F1__) +#define USE_STM32_DMA 1 +#elif defined(__STM32F4__) +#define USE_STM32_DMA 1 +#else // defined(__STM32F1__) +#error Unknown STM32 type +#endif // defined(__STM32F1__) +//------------------------------------------------------------------------------ +/** Set SPI options for access to SD/SDHC cards. + * + * \param[in] divisor SCK clock divider relative to the APB1 or APB2 clock. + */ +void SdSpiAltDriver::activate() { + m_spi->beginTransaction(m_spiSettings); +} +//------------------------------------------------------------------------------ +/** Initialize the SPI bus. + * + * \param[in] chipSelectPin SD card chip select pin. + */ +void SdSpiAltDriver::begin(uint8_t csPin) { + m_csPin = csPin; + pinMode(m_csPin, OUTPUT); + digitalWrite(m_csPin, HIGH); + m_spi->begin(); +} +//------------------------------------------------------------------------------ +/** + * End SPI transaction. + */ +void SdSpiAltDriver::deactivate() { + m_spi->endTransaction(); +} +//------------------------------------------------------------------------------ +/** Receive a byte. + * + * \return The byte. + */ +uint8_t SdSpiAltDriver::receive() { + return m_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 SdSpiAltDriver::receive(uint8_t* buf, size_t n) { +#if USE_STM32_DMA + return m_spi->dmaTransfer(nullptr, buf, n); +#else // USE_STM32_DMA + m_spi->read(buf, n); + return 0; +#endif // USE_STM32_DMA +} +//------------------------------------------------------------------------------ +/** Send a byte. + * + * \param[in] b Byte to send + */ +void SdSpiAltDriver::send(uint8_t b) { + m_spi->transfer(b); +} +//------------------------------------------------------------------------------ +/** Send multiple bytes. + * + * \param[in] buf Buffer for data to be sent. + * \param[in] n Number of bytes to send. + */ +void SdSpiAltDriver::send(const uint8_t* buf , size_t n) { +#if USE_STM32_DMA + m_spi->dmaTransfer(const_cast(buf), nullptr, n); +#else // USE_STM32_DMA + m_spi->write(const_cast(buf), n); +#endif // USE_STM32_DMA +} +#endif // defined(__STM32F1__) || defined(__STM32F4__) diff --git a/hardware/_controller/src/SdFat/SpiDriver/SdSpiTeensy3.cpp b/hardware/_controller/src/SdFat/SpiDriver/SdSpiTeensy3.cpp new file mode 100644 index 0000000..d703774 --- /dev/null +++ b/hardware/_controller/src/SdFat/SpiDriver/SdSpiTeensy3.cpp @@ -0,0 +1,233 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "SdSpiDriver.h" +#if defined(__arm__) && defined(CORE_TEENSY) +// SPI definitions +// #include "kinetis.h" + +//------------------------------------------------------------------------------ +void SdSpiAltDriver::activate() { + SPI.beginTransaction(m_spiSettings); +} +//------------------------------------------------------------------------------ +void SdSpiAltDriver::begin(uint8_t chipSelectPin) { + m_csPin = chipSelectPin; + pinMode(m_csPin, OUTPUT); + digitalWrite(m_csPin, HIGH); + SPI.begin(); +} +//------------------------------------------------------------------------------ +void SdSpiAltDriver::deactivate() { + SPI.endTransaction(); +} +//============================================================================== +#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 +#define SPI_INITIAL_FIFO_DEPTH 3 +// define some symbols that are not in mk20dx128.h +#ifndef SPI_SR_RXCTR +#define SPI_SR_RXCTR 0XF0 +#endif // SPI_SR_RXCTR +#ifndef SPI_PUSHR_CONT +#define SPI_PUSHR_CONT 0X80000000 +#endif // SPI_PUSHR_CONT +#ifndef SPI_PUSHR_CTAS +#define SPI_PUSHR_CTAS(n) (((n) & 7) << 28) +#endif // SPI_PUSHR_CTAS +//------------------------------------------------------------------------------ +/** SPI receive a byte */ +uint8_t SdSpiAltDriver::receive() { + SPI0_MCR |= SPI_MCR_CLR_RXF; + SPI0_SR = SPI_SR_TCF; + SPI0_PUSHR = 0xFF; + while (!(SPI0_SR & SPI_SR_TCF)) {} + return SPI0_POPR; +} +//------------------------------------------------------------------------------ +/** SPI receive multiple bytes */ +uint8_t SdSpiAltDriver::receive(uint8_t* buf, size_t n) { + // clear any data in RX FIFO + SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_CLR_RXF | SPI_MCR_PCSIS(0x1F); +#if SPI_USE_8BIT_FRAME + // initial number of bytes to push into TX FIFO + int nf = n < SPI_INITIAL_FIFO_DEPTH ? n : SPI_INITIAL_FIFO_DEPTH; + for (int i = 0; i < nf; i++) { + SPI0_PUSHR = 0XFF; + } + // limit for pushing dummy data into TX FIFO + uint8_t* limit = buf + n - nf; + while (buf < limit) { + while (!(SPI0_SR & SPI_SR_RXCTR)) {} + SPI0_PUSHR = 0XFF; + *buf++ = SPI0_POPR; + } + // limit for rest of RX data + limit += nf; + while (buf < limit) { + while (!(SPI0_SR & SPI_SR_RXCTR)) {} + *buf++ = SPI0_POPR; + } +#else // SPI_USE_8BIT_FRAME + // use 16 bit frame to avoid TD delay between frames + // get one byte if n is odd + if (n & 1) { + *buf++ = receive(); + n--; + } + // initial number of words to push into TX FIFO + int nf = n/2 < SPI_INITIAL_FIFO_DEPTH ? n/2 : SPI_INITIAL_FIFO_DEPTH; + for (int i = 0; i < nf; i++) { + SPI0_PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | 0XFFFF; + } + uint8_t* limit = buf + n - 2*nf; + while (buf < limit) { + while (!(SPI0_SR & SPI_SR_RXCTR)) {} + SPI0_PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | 0XFFFF; + uint16_t w = SPI0_POPR; + *buf++ = w >> 8; + *buf++ = w & 0XFF; + } + // limit for rest of RX data + limit += 2*nf; + while (buf < limit) { + while (!(SPI0_SR & SPI_SR_RXCTR)) {} + uint16_t w = SPI0_POPR; + *buf++ = w >> 8; + *buf++ = w & 0XFF; + } +#endif // SPI_USE_8BIT_FRAME + return 0; +} +//------------------------------------------------------------------------------ +/** SPI send a byte */ +void SdSpiAltDriver::send(uint8_t b) { + SPI0_MCR |= SPI_MCR_CLR_RXF; + SPI0_SR = SPI_SR_TCF; + SPI0_PUSHR = b; + while (!(SPI0_SR & SPI_SR_TCF)) {} +} +//------------------------------------------------------------------------------ +/** SPI send multiple bytes */ +void SdSpiAltDriver::send(const uint8_t* buf , size_t n) { + // clear any data in RX FIFO + SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_CLR_RXF | SPI_MCR_PCSIS(0x1F); +#if SPI_USE_8BIT_FRAME + // initial number of bytes to push into TX FIFO + int nf = n < SPI_INITIAL_FIFO_DEPTH ? n : SPI_INITIAL_FIFO_DEPTH; + // limit for pushing data into TX fifo + const uint8_t* limit = buf + n; + for (int i = 0; i < nf; i++) { + SPI0_PUSHR = *buf++; + } + // write data to TX FIFO + while (buf < limit) { + while (!(SPI0_SR & SPI_SR_RXCTR)) {} + SPI0_PUSHR = *buf++; + SPI0_POPR; + } + // wait for data to be sent + while (nf) { + while (!(SPI0_SR & SPI_SR_RXCTR)) {} + SPI0_POPR; + nf--; + } +#else // SPI_USE_8BIT_FRAME + // use 16 bit frame to avoid TD delay between frames + // send one byte if n is odd + if (n & 1) { + send(*buf++); + n--; + } + // initial number of words to push into TX FIFO + int nf = n/2 < SPI_INITIAL_FIFO_DEPTH ? n/2 : SPI_INITIAL_FIFO_DEPTH; + // limit for pushing data into TX fifo + const uint8_t* limit = buf + n; + for (int i = 0; i < nf; i++) { + uint16_t w = (*buf++) << 8; + w |= *buf++; + SPI0_PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | w; + } + // write data to TX FIFO + while (buf < limit) { + uint16_t w = *buf++ << 8; + w |= *buf++; + while (!(SPI0_SR & SPI_SR_RXCTR)) {} + SPI0_PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | w; + SPI0_POPR; + } + // wait for data to be sent + while (nf) { + while (!(SPI0_SR & SPI_SR_RXCTR)) {} + SPI0_POPR; + nf--; + } +#endif // SPI_USE_8BIT_FRAME +} +#else // KINETISK +//============================================================================== +// Use standard SPI library if not KINETISK +//------------------------------------------------------------------------------ +/** Receive a byte. + * + * \return The byte. + */ +uint8_t SdSpiAltDriver::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 SdSpiAltDriver::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 SdSpiAltDriver::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 SdSpiAltDriver::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) diff --git a/hardware/_controller/src/SdFat/SpiDriver/SoftSPI.h b/hardware/_controller/src/SdFat/SpiDriver/SoftSPI.h new file mode 100644 index 0000000..ee21b23 --- /dev/null +++ b/hardware/_controller/src/SdFat/SpiDriver/SoftSPI.h @@ -0,0 +1,167 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +/** + * @file + * @brief Software SPI. + * + * @defgroup softSPI Software SPI + * @details Software SPI Template Class. + * @{ + */ + +#ifndef SoftSPI_h +#define SoftSPI_h +#include "DigitalPin.h" +//------------------------------------------------------------------------------ +/** Nop for timing. */ +#define nop asm volatile ("nop\n\t") +//------------------------------------------------------------------------------ +/** Pin Mode for MISO is input.*/ +#define MISO_MODE INPUT +/** Pullups disabled for MISO are disabled. */ +#define MISO_LEVEL false +/** Pin Mode for MOSI is output.*/ +#define MOSI_MODE OUTPUT +/** Pin Mode for SCK is output. */ +#define SCK_MODE OUTPUT +//------------------------------------------------------------------------------ +/** + * @class SoftSPI + * @brief Fast software SPI. + */ +template +class SoftSPI { + public: + //---------------------------------------------------------------------------- + /** Initialize SoftSPI pins. */ + void begin() { + fastPinConfig(MisoPin, MISO_MODE, MISO_LEVEL); + fastPinConfig(MosiPin, MOSI_MODE, !MODE_CPHA(Mode)); + fastPinConfig(SckPin, SCK_MODE, MODE_CPOL(Mode)); + } + //---------------------------------------------------------------------------- + /** Soft SPI receive byte. + * @return Data byte received. + */ + inline __attribute__((always_inline)) + uint8_t receive() { + uint8_t data = 0; + receiveBit(7, &data); + receiveBit(6, &data); + receiveBit(5, &data); + receiveBit(4, &data); + receiveBit(3, &data); + receiveBit(2, &data); + receiveBit(1, &data); + receiveBit(0, &data); + return data; + } + //---------------------------------------------------------------------------- + /** Soft SPI send byte. + * @param[in] data Data byte to send. + */ + inline __attribute__((always_inline)) + void send(uint8_t data) { + sendBit(7, data); + sendBit(6, data); + sendBit(5, data); + sendBit(4, data); + sendBit(3, data); + sendBit(2, data); + sendBit(1, data); + sendBit(0, data); + } + //---------------------------------------------------------------------------- + /** Soft SPI transfer byte. + * @param[in] txData Data byte to send. + * @return Data byte received. + */ + inline __attribute__((always_inline)) + uint8_t transfer(uint8_t txData) { + uint8_t rxData = 0; + transferBit(7, &rxData, txData); + transferBit(6, &rxData, txData); + transferBit(5, &rxData, txData); + transferBit(4, &rxData, txData); + transferBit(3, &rxData, txData); + transferBit(2, &rxData, txData); + transferBit(1, &rxData, txData); + transferBit(0, &rxData, txData); + return rxData; + } + + private: + //---------------------------------------------------------------------------- + inline __attribute__((always_inline)) + bool MODE_CPHA(uint8_t mode) {return (mode & 1) != 0;} + inline __attribute__((always_inline)) + bool MODE_CPOL(uint8_t mode) {return (mode & 2) != 0;} + inline __attribute__((always_inline)) + void receiveBit(uint8_t bit, uint8_t* data) { + if (MODE_CPHA(Mode)) { + fastDigitalWrite(SckPin, !MODE_CPOL(Mode)); + } + nop; + nop; + fastDigitalWrite(SckPin, + MODE_CPHA(Mode) ? MODE_CPOL(Mode) : !MODE_CPOL(Mode)); + if (fastDigitalRead(MisoPin)) *data |= 1 << bit; + if (!MODE_CPHA(Mode)) { + fastDigitalWrite(SckPin, MODE_CPOL(Mode)); + } + } + //---------------------------------------------------------------------------- + inline __attribute__((always_inline)) + void sendBit(uint8_t bit, uint8_t data) { + if (MODE_CPHA(Mode)) { + fastDigitalWrite(SckPin, !MODE_CPOL(Mode)); + } + fastDigitalWrite(MosiPin, data & (1 << bit)); + fastDigitalWrite(SckPin, + MODE_CPHA(Mode) ? MODE_CPOL(Mode) : !MODE_CPOL(Mode)); + nop; + nop; + if (!MODE_CPHA(Mode)) { + fastDigitalWrite(SckPin, MODE_CPOL(Mode)); + } + } + //---------------------------------------------------------------------------- + inline __attribute__((always_inline)) + void transferBit(uint8_t bit, uint8_t* rxData, uint8_t txData) { + if (MODE_CPHA(Mode)) { + fastDigitalWrite(SckPin, !MODE_CPOL(Mode)); + } + fastDigitalWrite(MosiPin, txData & (1 << bit)); + fastDigitalWrite(SckPin, + MODE_CPHA(Mode) ? MODE_CPOL(Mode) : !MODE_CPOL(Mode)); + if (fastDigitalRead(MisoPin)) *rxData |= 1 << bit; + if (!MODE_CPHA(Mode)) { + fastDigitalWrite(SckPin, MODE_CPOL(Mode)); + } + } + //---------------------------------------------------------------------------- +}; +#endif // SoftSPI_h +/** @} */ diff --git a/hardware/_controller/src/SdFat/SpiDriver/boards/AvrDevelopersGpioPinMap.h b/hardware/_controller/src/SdFat/SpiDriver/boards/AvrDevelopersGpioPinMap.h new file mode 100644 index 0000000..67a8ec2 --- /dev/null +++ b/hardware/_controller/src/SdFat/SpiDriver/boards/AvrDevelopersGpioPinMap.h @@ -0,0 +1,37 @@ +#ifndef AvrDevelopersGpioPinMap_h +#define AvrDevelopersGpioPinMap_h +static const GpioPinMap_t GpioPinMap[] = { + GPIO_PIN(B, 0), // D0 + GPIO_PIN(B, 1), // D1 + GPIO_PIN(B, 2), // D2 + GPIO_PIN(B, 3), // D3 + GPIO_PIN(B, 4), // D4 + GPIO_PIN(B, 5), // D5 + GPIO_PIN(B, 6), // D6 + GPIO_PIN(B, 7), // D7 + GPIO_PIN(D, 0), // D8 + GPIO_PIN(D, 1), // D9 + GPIO_PIN(D, 2), // D10 + GPIO_PIN(D, 3), // D11 + GPIO_PIN(D, 4), // D12 + GPIO_PIN(D, 5), // D13 + GPIO_PIN(D, 6), // D14 + GPIO_PIN(D, 7), // D15 + GPIO_PIN(C, 0), // D16 + GPIO_PIN(C, 1), // D17 + GPIO_PIN(C, 2), // D18 + GPIO_PIN(C, 3), // D19 + GPIO_PIN(C, 4), // D20 + GPIO_PIN(C, 5), // D21 + GPIO_PIN(C, 6), // D22 + GPIO_PIN(C, 7), // D23 + GPIO_PIN(A, 7), // D24 + GPIO_PIN(A, 6), // D25 + GPIO_PIN(A, 5), // D26 + GPIO_PIN(A, 4), // D27 + GPIO_PIN(A, 3), // D28 + GPIO_PIN(A, 2), // D29 + GPIO_PIN(A, 1), // D30 + GPIO_PIN(A, 0) // D31 +}; +#endif // AvrDevelopersGpioPinMap_h \ No newline at end of file diff --git a/hardware/_controller/src/SdFat/SpiDriver/boards/BobuinoGpioPinMap.h b/hardware/_controller/src/SdFat/SpiDriver/boards/BobuinoGpioPinMap.h new file mode 100644 index 0000000..2d19944 --- /dev/null +++ b/hardware/_controller/src/SdFat/SpiDriver/boards/BobuinoGpioPinMap.h @@ -0,0 +1,37 @@ +#ifndef BobuinoGpioPinMap_h +#define BobuinoGpioPinMap_h +static const GpioPinMap_t GpioPinMap[] = { + GPIO_PIN(B, 0), // D0 + GPIO_PIN(B, 1), // D1 + GPIO_PIN(B, 2), // D2 + GPIO_PIN(B, 3), // D3 + GPIO_PIN(B, 4), // D4 + GPIO_PIN(B, 5), // D5 + GPIO_PIN(B, 6), // D6 + GPIO_PIN(B, 7), // D7 + GPIO_PIN(D, 0), // D8 + GPIO_PIN(D, 1), // D9 + GPIO_PIN(D, 2), // D10 + GPIO_PIN(D, 3), // D11 + GPIO_PIN(D, 4), // D12 + GPIO_PIN(D, 5), // D13 + GPIO_PIN(D, 6), // D14 + GPIO_PIN(D, 7), // D15 + GPIO_PIN(C, 0), // D16 + GPIO_PIN(C, 1), // D17 + GPIO_PIN(C, 2), // D18 + GPIO_PIN(C, 3), // D19 + GPIO_PIN(C, 4), // D20 + GPIO_PIN(C, 5), // D21 + GPIO_PIN(C, 6), // D22 + GPIO_PIN(C, 7), // D23 + GPIO_PIN(A, 0), // D24 + GPIO_PIN(A, 1), // D25 + GPIO_PIN(A, 2), // D26 + GPIO_PIN(A, 3), // D27 + GPIO_PIN(A, 4), // D28 + GPIO_PIN(A, 5), // D29 + GPIO_PIN(A, 6), // D30 + GPIO_PIN(A, 7) // D31 +}; +#endif // BobuinoGpioPinMap_h \ No newline at end of file diff --git a/hardware/_controller/src/SdFat/SpiDriver/boards/GpioPinMap.h b/hardware/_controller/src/SdFat/SpiDriver/boards/GpioPinMap.h new file mode 100644 index 0000000..326b762 --- /dev/null +++ b/hardware/_controller/src/SdFat/SpiDriver/boards/GpioPinMap.h @@ -0,0 +1,45 @@ +#ifndef GpioPinMap_h +#define GpioPinMap_h +#if defined(__AVR_ATmega168__)\ +||defined(__AVR_ATmega168P__)\ +||defined(__AVR_ATmega328P__) +// 168 and 328 Arduinos +#include "UnoGpioPinMap.h" +#elif defined(__AVR_ATmega1280__)\ +|| defined(__AVR_ATmega2560__) +// Mega ADK +#include "MegaGpioPinMap.h" +#elif defined(__AVR_ATmega32U4__) +#ifdef CORE_TEENSY +#include "Teensy2GpioPinMap.h" +#else // CORE_TEENSY +// Leonardo or Yun +#include "LeonardoGpioPinMap.h" +#endif // CORE_TEENSY +#elif defined(__AVR_AT90USB646__)\ +|| defined(__AVR_AT90USB1286__) +// Teensy++ 1.0 & 2.0 +#include "Teensy2ppGpioPinMap.h" +#elif defined(__AVR_ATmega1284P__)\ +|| defined(__AVR_ATmega1284__)\ +|| defined(__AVR_ATmega644P__)\ +|| defined(__AVR_ATmega644__)\ +|| defined(__AVR_ATmega64__)\ +|| defined(__AVR_ATmega32__)\ +|| defined(__AVR_ATmega324__)\ +|| defined(__AVR_ATmega16__) +#ifdef ARDUINO_1284P_AVR_DEVELOPERS +#include "AvrDevelopersGpioPinMap.h" +#elif defined(BOBUINO_PINOUT) || defined(ARDUINO_1284P_BOBUINO) +#include "BobuinoGpioPinMap.h" +#elif defined(ARDUINO_1284P_SLEEPINGBEAUTY) +#include "SleepingBeautyGpioPinMap.h" +#elif defined(STANDARD_PINOUT) || defined(ARDUINO_1284P_STANDARD) +#include "Standard1284GpioPinMap.h" +#else // ARDUINO_1284P_AVR_DEVELOPERS +#error Undefined variant 1284, 644, 324 +#endif // ARDUINO_1284P_AVR_DEVELOPERS +#else // 1284P, 1284, 644 +#error Unknown board type. +#endif // end all boards +#endif // GpioPinMap_h diff --git a/hardware/_controller/src/SdFat/SpiDriver/boards/LeonardoGpioPinMap.h b/hardware/_controller/src/SdFat/SpiDriver/boards/LeonardoGpioPinMap.h new file mode 100644 index 0000000..73544e6 --- /dev/null +++ b/hardware/_controller/src/SdFat/SpiDriver/boards/LeonardoGpioPinMap.h @@ -0,0 +1,35 @@ +#ifndef LeonardoGpioPinMap_h +#define LeonardoGpioPinMap_h +static const GpioPinMap_t GpioPinMap[] = { + GPIO_PIN(D, 2), // D0 + GPIO_PIN(D, 3), // D1 + GPIO_PIN(D, 1), // D2 + GPIO_PIN(D, 0), // D3 + GPIO_PIN(D, 4), // D4 + GPIO_PIN(C, 6), // D5 + GPIO_PIN(D, 7), // D6 + GPIO_PIN(E, 6), // D7 + GPIO_PIN(B, 4), // D8 + GPIO_PIN(B, 5), // D9 + GPIO_PIN(B, 6), // D10 + GPIO_PIN(B, 7), // D11 + GPIO_PIN(D, 6), // D12 + GPIO_PIN(C, 7), // D13 + GPIO_PIN(B, 3), // D14 + GPIO_PIN(B, 1), // D15 + GPIO_PIN(B, 2), // D16 + GPIO_PIN(B, 0), // D17 + GPIO_PIN(F, 7), // D18 + GPIO_PIN(F, 6), // D19 + GPIO_PIN(F, 5), // D20 + GPIO_PIN(F, 4), // D21 + GPIO_PIN(F, 1), // D22 + GPIO_PIN(F, 0), // D23 + GPIO_PIN(D, 4), // D24 + GPIO_PIN(D, 7), // D25 + GPIO_PIN(B, 4), // D26 + GPIO_PIN(B, 5), // D27 + GPIO_PIN(B, 6), // D28 + GPIO_PIN(D, 6) // D29 +}; +#endif // LeonardoGpioPinMap_h diff --git a/hardware/_controller/src/SdFat/SpiDriver/boards/MegaGpioPinMap.h b/hardware/_controller/src/SdFat/SpiDriver/boards/MegaGpioPinMap.h new file mode 100644 index 0000000..c041343 --- /dev/null +++ b/hardware/_controller/src/SdFat/SpiDriver/boards/MegaGpioPinMap.h @@ -0,0 +1,75 @@ +#ifndef MegaGpioPinMap_h +#define MegaGpioPinMap_h +static const GpioPinMap_t GpioPinMap[] = { + GPIO_PIN(E, 0), // D0 + GPIO_PIN(E, 1), // D1 + GPIO_PIN(E, 4), // D2 + GPIO_PIN(E, 5), // D3 + GPIO_PIN(G, 5), // D4 + GPIO_PIN(E, 3), // D5 + GPIO_PIN(H, 3), // D6 + GPIO_PIN(H, 4), // D7 + GPIO_PIN(H, 5), // D8 + GPIO_PIN(H, 6), // D9 + GPIO_PIN(B, 4), // D10 + GPIO_PIN(B, 5), // D11 + GPIO_PIN(B, 6), // D12 + GPIO_PIN(B, 7), // D13 + GPIO_PIN(J, 1), // D14 + GPIO_PIN(J, 0), // D15 + GPIO_PIN(H, 1), // D16 + GPIO_PIN(H, 0), // D17 + GPIO_PIN(D, 3), // D18 + GPIO_PIN(D, 2), // D19 + GPIO_PIN(D, 1), // D20 + GPIO_PIN(D, 0), // D21 + GPIO_PIN(A, 0), // D22 + GPIO_PIN(A, 1), // D23 + GPIO_PIN(A, 2), // D24 + GPIO_PIN(A, 3), // D25 + GPIO_PIN(A, 4), // D26 + GPIO_PIN(A, 5), // D27 + GPIO_PIN(A, 6), // D28 + GPIO_PIN(A, 7), // D29 + GPIO_PIN(C, 7), // D30 + GPIO_PIN(C, 6), // D31 + GPIO_PIN(C, 5), // D32 + GPIO_PIN(C, 4), // D33 + GPIO_PIN(C, 3), // D34 + GPIO_PIN(C, 2), // D35 + GPIO_PIN(C, 1), // D36 + GPIO_PIN(C, 0), // D37 + GPIO_PIN(D, 7), // D38 + GPIO_PIN(G, 2), // D39 + GPIO_PIN(G, 1), // D40 + GPIO_PIN(G, 0), // D41 + GPIO_PIN(L, 7), // D42 + GPIO_PIN(L, 6), // D43 + GPIO_PIN(L, 5), // D44 + GPIO_PIN(L, 4), // D45 + GPIO_PIN(L, 3), // D46 + GPIO_PIN(L, 2), // D47 + GPIO_PIN(L, 1), // D48 + GPIO_PIN(L, 0), // D49 + GPIO_PIN(B, 3), // D50 + GPIO_PIN(B, 2), // D51 + GPIO_PIN(B, 1), // D52 + GPIO_PIN(B, 0), // D53 + GPIO_PIN(F, 0), // D54 + GPIO_PIN(F, 1), // D55 + GPIO_PIN(F, 2), // D56 + GPIO_PIN(F, 3), // D57 + GPIO_PIN(F, 4), // D58 + GPIO_PIN(F, 5), // D59 + GPIO_PIN(F, 6), // D60 + GPIO_PIN(F, 7), // D61 + GPIO_PIN(K, 0), // D62 + GPIO_PIN(K, 1), // D63 + GPIO_PIN(K, 2), // D64 + GPIO_PIN(K, 3), // D65 + GPIO_PIN(K, 4), // D66 + GPIO_PIN(K, 5), // D67 + GPIO_PIN(K, 6), // D68 + GPIO_PIN(K, 7) // D69 +}; +#endif // MegaGpioPinMap_h diff --git a/hardware/_controller/src/SdFat/SpiDriver/boards/SleepingBeautyGpioPinMap.h b/hardware/_controller/src/SdFat/SpiDriver/boards/SleepingBeautyGpioPinMap.h new file mode 100644 index 0000000..bf040d9 --- /dev/null +++ b/hardware/_controller/src/SdFat/SpiDriver/boards/SleepingBeautyGpioPinMap.h @@ -0,0 +1,37 @@ +#ifndef SleepingBeautyGpioPinMap_h +#define SleepingBeautyGpioPinMap_h +static const GpioPinMap_t GpioPinMap[] = { + GPIO_PIN(D, 0), // D0 + GPIO_PIN(D, 1), // D1 + GPIO_PIN(D, 2), // D2 + GPIO_PIN(D, 3), // D3 + GPIO_PIN(B, 0), // D4 + GPIO_PIN(B, 1), // D5 + GPIO_PIN(B, 2), // D6 + GPIO_PIN(B, 3), // D7 + GPIO_PIN(D, 6), // D8 + GPIO_PIN(D, 5), // D9 + GPIO_PIN(B, 4), // D10 + GPIO_PIN(B, 5), // D11 + GPIO_PIN(B, 6), // D12 + GPIO_PIN(B, 7), // D13 + GPIO_PIN(C, 7), // D14 + GPIO_PIN(C, 6), // D15 + GPIO_PIN(A, 5), // D16 + GPIO_PIN(A, 4), // D17 + GPIO_PIN(A, 3), // D18 + GPIO_PIN(A, 2), // D19 + GPIO_PIN(A, 1), // D20 + GPIO_PIN(A, 0), // D21 + GPIO_PIN(D, 4), // D22 + GPIO_PIN(D, 7), // D23 + GPIO_PIN(C, 2), // D24 + GPIO_PIN(C, 3), // D25 + GPIO_PIN(C, 4), // D26 + GPIO_PIN(C, 5), // D27 + GPIO_PIN(C, 1), // D28 + GPIO_PIN(C, 0), // D29 + GPIO_PIN(A, 6), // D30 + GPIO_PIN(A, 7) // D31 +}; +#endif // SleepingBeautyGpioPinMap_h \ No newline at end of file diff --git a/hardware/_controller/src/SdFat/SpiDriver/boards/Standard1284GpioPinMap.h b/hardware/_controller/src/SdFat/SpiDriver/boards/Standard1284GpioPinMap.h new file mode 100644 index 0000000..d38ff0c --- /dev/null +++ b/hardware/_controller/src/SdFat/SpiDriver/boards/Standard1284GpioPinMap.h @@ -0,0 +1,37 @@ +#ifndef Standard1284GpioPinMap_h +#define Standard1284GpioPinMap_h +static const GpioPinMap_t GpioPinMap[] = { + GPIO_PIN(B, 0), // D0 + GPIO_PIN(B, 1), // D1 + GPIO_PIN(B, 2), // D2 + GPIO_PIN(B, 3), // D3 + GPIO_PIN(B, 4), // D4 + GPIO_PIN(B, 5), // D5 + GPIO_PIN(B, 6), // D6 + GPIO_PIN(B, 7), // D7 + GPIO_PIN(D, 0), // D8 + GPIO_PIN(D, 1), // D9 + GPIO_PIN(D, 2), // D10 + GPIO_PIN(D, 3), // D11 + GPIO_PIN(D, 4), // D12 + GPIO_PIN(D, 5), // D13 + GPIO_PIN(D, 6), // D14 + GPIO_PIN(D, 7), // D15 + GPIO_PIN(C, 0), // D16 + GPIO_PIN(C, 1), // D17 + GPIO_PIN(C, 2), // D18 + GPIO_PIN(C, 3), // D19 + GPIO_PIN(C, 4), // D20 + GPIO_PIN(C, 5), // D21 + GPIO_PIN(C, 6), // D22 + GPIO_PIN(C, 7), // D23 + GPIO_PIN(A, 0), // D24 + GPIO_PIN(A, 1), // D25 + GPIO_PIN(A, 2), // D26 + GPIO_PIN(A, 3), // D27 + GPIO_PIN(A, 4), // D28 + GPIO_PIN(A, 5), // D29 + GPIO_PIN(A, 6), // D30 + GPIO_PIN(A, 7) // D31 +}; +#endif // Standard1284GpioPinMap_h \ No newline at end of file diff --git a/hardware/_controller/src/SdFat/SpiDriver/boards/Teensy2GpioPinMap.h b/hardware/_controller/src/SdFat/SpiDriver/boards/Teensy2GpioPinMap.h new file mode 100644 index 0000000..00aa437 --- /dev/null +++ b/hardware/_controller/src/SdFat/SpiDriver/boards/Teensy2GpioPinMap.h @@ -0,0 +1,30 @@ +#ifndef Teensy2GpioPinMap_h +#define Teensy2GpioPinMap_h +static const GpioPinMap_t GpioPinMap[] = { + GPIO_PIN(B, 0), // D0 + GPIO_PIN(B, 1), // D1 + GPIO_PIN(B, 2), // D2 + GPIO_PIN(B, 3), // D3 + GPIO_PIN(B, 7), // D4 + GPIO_PIN(D, 0), // D5 + GPIO_PIN(D, 1), // D6 + GPIO_PIN(D, 2), // D7 + GPIO_PIN(D, 3), // D8 + GPIO_PIN(C, 6), // D9 + GPIO_PIN(C, 7), // D10 + GPIO_PIN(D, 6), // D11 + GPIO_PIN(D, 7), // D12 + GPIO_PIN(B, 4), // D13 + GPIO_PIN(B, 5), // D14 + GPIO_PIN(B, 6), // D15 + GPIO_PIN(F, 7), // D16 + GPIO_PIN(F, 6), // D17 + GPIO_PIN(F, 5), // D18 + GPIO_PIN(F, 4), // D19 + GPIO_PIN(F, 1), // D20 + GPIO_PIN(F, 0), // D21 + GPIO_PIN(D, 4), // D22 + GPIO_PIN(D, 5), // D23 + GPIO_PIN(E, 6), // D24 +}; +#endif // Teensy2GpioPinMap_h diff --git a/hardware/_controller/src/SdFat/SpiDriver/boards/Teensy2ppGpioPinMap.h b/hardware/_controller/src/SdFat/SpiDriver/boards/Teensy2ppGpioPinMap.h new file mode 100644 index 0000000..68c51d7 --- /dev/null +++ b/hardware/_controller/src/SdFat/SpiDriver/boards/Teensy2ppGpioPinMap.h @@ -0,0 +1,51 @@ +#ifndef Teensypp2GpioPinMap_h +#define Teensypp2GpioPinMap_h +static const GpioPinMap_t GpioPinMap[] = { + GPIO_PIN(D, 0), // D0 + GPIO_PIN(D, 1), // D1 + GPIO_PIN(D, 2), // D2 + GPIO_PIN(D, 3), // D3 + GPIO_PIN(D, 4), // D4 + GPIO_PIN(D, 5), // D5 + GPIO_PIN(D, 6), // D6 + GPIO_PIN(D, 7), // D7 + GPIO_PIN(E, 0), // D8 + GPIO_PIN(E, 1), // D9 + GPIO_PIN(C, 0), // D10 + GPIO_PIN(C, 1), // D11 + GPIO_PIN(C, 2), // D12 + GPIO_PIN(C, 3), // D13 + GPIO_PIN(C, 4), // D14 + GPIO_PIN(C, 5), // D15 + GPIO_PIN(C, 6), // D16 + GPIO_PIN(C, 7), // D17 + GPIO_PIN(E, 6), // D18 + GPIO_PIN(E, 7), // D19 + GPIO_PIN(B, 0), // D20 + GPIO_PIN(B, 1), // D21 + GPIO_PIN(B, 2), // D22 + GPIO_PIN(B, 3), // D23 + GPIO_PIN(B, 4), // D24 + GPIO_PIN(B, 5), // D25 + GPIO_PIN(B, 6), // D26 + GPIO_PIN(B, 7), // D27 + GPIO_PIN(A, 0), // D28 + GPIO_PIN(A, 1), // D29 + GPIO_PIN(A, 2), // D30 + GPIO_PIN(A, 3), // D31 + GPIO_PIN(A, 4), // D32 + GPIO_PIN(A, 5), // D33 + GPIO_PIN(A, 6), // D34 + GPIO_PIN(A, 7), // D35 + GPIO_PIN(E, 4), // D36 + GPIO_PIN(E, 5), // D37 + GPIO_PIN(F, 0), // D38 + GPIO_PIN(F, 1), // D39 + GPIO_PIN(F, 2), // D40 + GPIO_PIN(F, 3), // D41 + GPIO_PIN(F, 4), // D42 + GPIO_PIN(F, 5), // D43 + GPIO_PIN(F, 6), // D44 + GPIO_PIN(F, 7), // D45 +}; +#endif // Teensypp2GpioPinMap_h diff --git a/hardware/_controller/src/SdFat/SpiDriver/boards/UnoGpioPinMap.h b/hardware/_controller/src/SdFat/SpiDriver/boards/UnoGpioPinMap.h new file mode 100644 index 0000000..21ec75d --- /dev/null +++ b/hardware/_controller/src/SdFat/SpiDriver/boards/UnoGpioPinMap.h @@ -0,0 +1,25 @@ +#ifndef UnoGpioPinMap_h +#define UnoGpioPinMap_h +static const GpioPinMap_t GpioPinMap[] = { + GPIO_PIN(D, 0), // D0 + GPIO_PIN(D, 1), // D1 + GPIO_PIN(D, 2), // D2 + GPIO_PIN(D, 3), // D3 + GPIO_PIN(D, 4), // D4 + GPIO_PIN(D, 5), // D5 + GPIO_PIN(D, 6), // D6 + GPIO_PIN(D, 7), // D7 + GPIO_PIN(B, 0), // D8 + GPIO_PIN(B, 1), // D9 + GPIO_PIN(B, 2), // D10 + GPIO_PIN(B, 3), // D11 + GPIO_PIN(B, 4), // D12 + GPIO_PIN(B, 5), // D13 + GPIO_PIN(C, 0), // D14 + GPIO_PIN(C, 1), // D15 + GPIO_PIN(C, 2), // D16 + GPIO_PIN(C, 3), // D17 + GPIO_PIN(C, 4), // D18 + GPIO_PIN(C, 5) // D19 +}; +#endif // UnoGpioPinMap_h \ No newline at end of file diff --git a/hardware/_controller/src/SdFat/SysCall.h b/hardware/_controller/src/SdFat/SysCall.h new file mode 100644 index 0000000..5847155 --- /dev/null +++ b/hardware/_controller/src/SdFat/SysCall.h @@ -0,0 +1,88 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef SysCall_h +#define SysCall_h +/** + * \file + * \brief SysCall class + */ +#if defined(ARDUINO) +#include +#include +#elif defined(PLATFORM_ID) // Only defined if a Particle device +#include "application.h" +#else // defined(ARDUINO) +#error "Unknown system" +#endif // defined(ARDUINO) +//------------------------------------------------------------------------------ +#ifdef ESP8266 +// undefine F macro if ESP8266. +#undef F +#endif // ESP8266 +//------------------------------------------------------------------------------ +#ifndef F +/** Define macro for strings stored in flash. */ +#define F(str) (str) +#endif // F +//------------------------------------------------------------------------------ +/** \return the time in milliseconds. */ +inline uint16_t curTimeMS() { + return millis(); +} +//------------------------------------------------------------------------------ +/** + * \class SysCall + * \brief SysCall - Class to wrap system calls. + */ +class SysCall { + public: + /** Halt execution of this thread. */ + static void halt() { + while (1) { + yield(); + } + } + /** Yield to other threads. */ + static void yield(); +}; + +#if defined(ESP8266) +inline void SysCall::yield() { + // Avoid ESP8266 bug + delay(0); +} +#elif defined(ARDUINO) +inline void SysCall::yield() { + // Use the external Arduino yield() function. + ::yield(); +} +#elif defined(PLATFORM_ID) // Only defined if a Particle device +inline void SysCall::yield() { + Particle.process(); +} +#else // ESP8266 +inline void SysCall::yield() {} +#endif // ESP8266 +#endif // SysCall_h diff --git a/hardware/_controller/src/SdFat/sdios.h b/hardware/_controller/src/SdFat/sdios.h new file mode 100644 index 0000000..6054623 --- /dev/null +++ b/hardware/_controller/src/SdFat/sdios.h @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2011-2018 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef sdios_h +#define sdios_h +/** + * \file + * \brief C++ IO Streams features. + */ +#include "FatLib/fstream.h" +#include "FatLib/ArduinoStream.h" +#endif // sdios_h diff --git a/hardware/_controller/src/guislice/GUIslice_drv_adagfx.cpp b/hardware/_controller/src/guislice/GUIslice_drv_adagfx.cpp index 56834ee..ca1ccd1 100644 --- a/hardware/_controller/src/guislice/GUIslice_drv_adagfx.cpp +++ b/hardware/_controller/src/guislice/GUIslice_drv_adagfx.cpp @@ -177,7 +177,7 @@ // - To support SW SPI interface, need to make mod to SdFat lib: // - Arduino\libraries\SdFat\src\SdFatConfig.h: // - #define ENABLE_SOFTWARE_SPI_CLASS 1 // Change default from 0 to 1 - #include + #include "../SdFat/SdFat.h" SdFatSoftSpi<12, 11, 13> SD; // FIXME: Add configurability #endif #endif diff --git a/hardware/_controller/src/guislice/ard-adagfx-ili9341-notouch.h b/hardware/_controller/src/guislice/ard-adagfx-ili9341-notouch.h index 8306c09..5f06541 100644 --- a/hardware/_controller/src/guislice/ard-adagfx-ili9341-notouch.h +++ b/hardware/_controller/src/guislice/ard-adagfx-ili9341-notouch.h @@ -146,7 +146,7 @@ extern "C" { // - Note that the inclusion of the SD library consumes considerable // RAM and flash memory which could be problematic for Arduino models // with limited resources. - #define GSLC_SD_EN 0 + #define GSLC_SD_EN 2 // ============================================================================= diff --git a/hardware/_controller/src/guislice/ard-adagfx-ili9341-stmpe610.h b/hardware/_controller/src/guislice/ard-adagfx-ili9341-stmpe610.h index ec61a60..68b1d05 100644 --- a/hardware/_controller/src/guislice/ard-adagfx-ili9341-stmpe610.h +++ b/hardware/_controller/src/guislice/ard-adagfx-ili9341-stmpe610.h @@ -185,7 +185,7 @@ extern "C" { // - Note that the inclusion of the SD library consumes considerable // RAM and flash memory which could be problematic for Arduino models // with limited resources. - #define GSLC_SD_EN 0 + #define GSLC_SD_EN 2 // =============================================================================