diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..a27eaf3 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,89 @@ +# default language +language: generic + +env: + global: + - CLI_VERSION=latest + +matrix: + include: + # compile example sketches for the following boards + - env: + - BOARD='arduino:avr:mega:cpu=atmega2560' + install: + - arduino-cli core install arduino:avr + + - env: + - BOARD='arduino:samd:mkrzero' + install: + - arduino-cli core install arduino:samd + + - env: + - BOARD='arduino:megaavr:uno2018:mode=on' + install: + - arduino-cli core install arduino:megaavr + + - env: + - BOARD='arduino:sam:arduino_due_x' + install: + - arduino-cli core install arduino:sam + + # check all code files for compliance with the Arduino code formatting style + - env: + - NAME='Code Formatting Check' + language: minimal + before_install: + # install Artistic Style code formatter tool: http://astyle.sourceforge.net + - | + mkdir "${HOME}/astyle"; + wget --no-verbose --output-document="${HOME}/astyle/astyle.tar.gz" "https://iweb.dl.sourceforge.net/project/astyle/astyle/astyle%203.1/astyle_3.1_linux.tar.gz"; + tar --extract --file="${HOME}/astyle/astyle.tar.gz" --directory="${HOME}/astyle"; + cd "${HOME}/astyle/astyle/build/gcc"; + make; + export PATH="$PWD/bin:$PATH"; + cd "$TRAVIS_BUILD_DIR" + # download Arduino's Artistic Style configuration file + - wget --directory-prefix="${HOME}/astyle" https://raw.githubusercontent.com/arduino/Arduino/master/build/shared/examples_formatter.conf + script: + # check code formatting + - find . -regextype posix-extended -path './.git' -prune -or \( -iregex '.*\.((ino)|(h)|(hpp)|(hh)|(hxx)|(h\+\+)|(cpp)|(cc)|(cxx)|(c\+\+)|(cp)|(c)|(ipp)|(ii)|(ixx)|(inl)|(tpp)|(txx)|(tpl))$' -and -type f \) -print0 | xargs -0 -L1 bash -c 'if ! diff "$0" <(astyle --options=${HOME}/astyle/examples_formatter.conf --dry-run < "$0"); then echo "Non-compliant code formatting in $0"; false; fi' + + # check all files for commonly misspelled words + - env: + - NAME='Spell Check' + language: python + python: 3.6 + before_install: + # https://github.com/codespell-project/codespell + - pip install codespell + script: + # codespell will ignore any words in extras/codespell-ignore-words-list.txt, which may be used to fix false positives + - codespell --skip="${TRAVIS_BUILD_DIR}/.git" --ignore-words="${TRAVIS_BUILD_DIR}/extras/codespell-ignore-words-list.txt" "${TRAVIS_BUILD_DIR}" + +# default phases shared by the compilation tests +before_install: + - wget http://downloads.arduino.cc/arduino-cli/arduino-cli-$CLI_VERSION-linux64.tar.bz2 + - tar xf arduino-cli-$CLI_VERSION-linux64.tar.bz2 + - mkdir -p "$HOME/bin" + - mv arduino-cli-*-linux64 $HOME/bin/arduino-cli + - export PATH="$PATH:$HOME/bin" + - arduino-cli core update-index + - buildExampleSketch() { arduino-cli compile --verbose --warnings all --fqbn $BOARD "$PWD/examples/$1"; } + - mkdir -p "$HOME/Arduino/libraries" + - ln -s "$PWD" "$HOME/Arduino/libraries/." + +script: + - buildExampleSketch CardInfo + - buildExampleSketch Datalogger + - buildExampleSketch DumpFile + - buildExampleSketch Files + - buildExampleSketch listfiles + - buildExampleSketch ReadWrite + +notifications: + webhooks: + # use TravisBuddy to comment on any pull request that results in a failed CI build + urls: + - https://www.travisbuddy.com/ + on_success: never + on_failure: always diff --git a/README.adoc b/README.adoc index fabff56..fb3ed03 100644 --- a/README.adoc +++ b/README.adoc @@ -1,5 +1,7 @@ = SD Library for Arduino = +image:https://travis-ci.org/arduino-libraries/SD.svg?branch=master[Build Status, link=https://travis-ci.org/arduino-libraries/SD] + The SD library allows for reading from and writing to SD cards. For more information about this library please visit us at diff --git a/examples/Datalogger/Datalogger.ino b/examples/Datalogger/Datalogger.ino index a3e3c11..c2631c0 100644 --- a/examples/Datalogger/Datalogger.ino +++ b/examples/Datalogger/Datalogger.ino @@ -1,24 +1,24 @@ /* SD card datalogger - This example shows how to log data from three analog sensors - to an SD card using the SD library. + This example shows how to log data from three analog sensors + to an SD card using the SD library. - The circuit: - * analog sensors on analog ins 0, 1, and 2 - * SD card attached to SPI bus as follows: + The circuit: + analog sensors on analog ins 0, 1, and 2 + SD card attached to SPI bus as follows: ** MOSI - pin 11 ** MISO - pin 12 ** CLK - pin 13 ** CS - pin 4 (for MKRZero SD: SDCARD_SS_PIN) - created 24 Nov 2010 - modified 9 Apr 2012 - by Tom Igoe + created 24 Nov 2010 + modified 9 Apr 2012 + by Tom Igoe - This example code is in the public domain. + This example code is in the public domain. - */ +*/ #include #include diff --git a/examples/DumpFile/DumpFile.ino b/examples/DumpFile/DumpFile.ino index 421f2d9..7491999 100644 --- a/examples/DumpFile/DumpFile.ino +++ b/examples/DumpFile/DumpFile.ino @@ -1,24 +1,24 @@ /* SD card file dump - This example shows how to read a file from the SD card using the - SD library and send it over the serial port. + This example shows how to read a file from the SD card using the + SD library and send it over the serial port. - The circuit: - * SD card attached to SPI bus as follows: + The circuit: + SD card attached to SPI bus as follows: ** MOSI - pin 11 ** MISO - pin 12 ** CLK - pin 13 ** CS - pin 4 (for MKRZero SD: SDCARD_SS_PIN) - created 22 December 2010 - by Limor Fried - modified 9 Apr 2012 - by Tom Igoe + created 22 December 2010 + by Limor Fried + modified 9 Apr 2012 + by Tom Igoe - This example code is in the public domain. + This example code is in the public domain. - */ +*/ #include #include diff --git a/examples/Files/Files.ino b/examples/Files/Files.ino index 9b1b19d..cabebba 100644 --- a/examples/Files/Files.ino +++ b/examples/Files/Files.ino @@ -1,22 +1,22 @@ /* SD card basic file example - This example shows how to create and destroy an SD card file - The circuit: - * SD card attached to SPI bus as follows: + This example shows how to create and destroy an SD card file + The circuit: + SD card attached to SPI bus as follows: ** MOSI - pin 11 ** MISO - pin 12 ** CLK - pin 13 ** CS - pin 4 (for MKRZero SD: SDCARD_SS_PIN) - created Nov 2010 - by David A. Mellis - modified 9 Apr 2012 - by Tom Igoe + created Nov 2010 + by David A. Mellis + modified 9 Apr 2012 + by Tom Igoe - This example code is in the public domain. + This example code is in the public domain. - */ +*/ #include #include diff --git a/examples/ReadWrite/ReadWrite.ino b/examples/ReadWrite/ReadWrite.ino index 8e4bd82..d964668 100644 --- a/examples/ReadWrite/ReadWrite.ino +++ b/examples/ReadWrite/ReadWrite.ino @@ -1,22 +1,22 @@ /* SD card read/write - This example shows how to read and write data to and from an SD card file - The circuit: - * SD card attached to SPI bus as follows: + This example shows how to read and write data to and from an SD card file + The circuit: + SD card attached to SPI bus as follows: ** MOSI - pin 11 ** MISO - pin 12 ** CLK - pin 13 ** CS - pin 4 (for MKRZero SD: SDCARD_SS_PIN) - created Nov 2010 - by David A. Mellis - modified 9 Apr 2012 - by Tom Igoe + created Nov 2010 + by David A. Mellis + modified 9 Apr 2012 + by Tom Igoe - This example code is in the public domain. + This example code is in the public domain. - */ +*/ #include #include diff --git a/examples/listfiles/listfiles.ino b/examples/listfiles/listfiles.ino index 7c4ee5c..48f84fb 100644 --- a/examples/listfiles/listfiles.ino +++ b/examples/listfiles/listfiles.ino @@ -1,26 +1,26 @@ /* Listfiles - This example shows how print out the files in a - directory on a SD card + This example shows how print out the files in a + directory on a SD card - The circuit: - * SD card attached to SPI bus as follows: + The circuit: + SD card attached to SPI bus as follows: ** MOSI - pin 11 ** MISO - pin 12 ** CLK - pin 13 ** CS - pin 4 (for MKRZero SD: SDCARD_SS_PIN) - created Nov 2010 - by David A. Mellis - modified 9 Apr 2012 - by Tom Igoe - modified 2 Feb 2014 - by Scott Fitzgerald + created Nov 2010 + by David A. Mellis + modified 9 Apr 2012 + by Tom Igoe + modified 2 Feb 2014 + by Scott Fitzgerald - This example code is in the public domain. + This example code is in the public domain. - */ +*/ #include #include diff --git a/extras/codespell-ignore-words-list.txt b/extras/codespell-ignore-words-list.txt new file mode 100644 index 0000000..e69de29 diff --git a/src/File.cpp b/src/File.cpp index 6eee39a..c4af48d 100644 --- a/src/File.cpp +++ b/src/File.cpp @@ -1,16 +1,16 @@ /* - SD - a slightly more friendly wrapper for sdfatlib + SD - a slightly more friendly wrapper for sdfatlib - This library aims to expose a subset of SD card functionality - in the form of a higher level "wrapper" object. + This library aims to expose a subset of SD card functionality + in the form of a higher level "wrapper" object. - License: GNU General Public License V3 + License: GNU General Public License V3 (Because sdfatlib is licensed with this.) - (C) Copyright 2010 SparkFun Electronics + (C) Copyright 2010 SparkFun Electronics - */ +*/ #include @@ -19,14 +19,14 @@ */ File::File(SdFile f, const char *n) { - // oh man you are kidding me, new() doesnt exist? Ok we do it by hand! - _file = (SdFile *)malloc(sizeof(SdFile)); + // oh man you are kidding me, new() doesn't exist? Ok we do it by hand! + _file = (SdFile *)malloc(sizeof(SdFile)); if (_file) { memcpy(_file, &f, sizeof(SdFile)); - + strncpy(_name, n, 12); _name[12] = 0; - + /* for debugging file open/close leaks nfilecount++; Serial.print("Created \""); @@ -74,29 +74,36 @@ size_t File::write(const uint8_t *buf, size_t size) { } int File::peek() { - if (! _file) + if (! _file) { return 0; + } int c = _file->read(); - if (c != -1) _file->seekCur(-1); + if (c != -1) { + _file->seekCur(-1); + } return c; } int File::read() { - if (_file) + if (_file) { return _file->read(); + } return -1; } // buffered read for more efficient, high speed reading int File::read(void *buf, uint16_t nbyte) { - if (_file) + if (_file) { return _file->read(buf, nbyte); + } return 0; } int File::available() { - if (! _file) return 0; + if (! _file) { + return 0; + } uint32_t n = size() - position(); @@ -104,43 +111,51 @@ int File::available() { } void File::flush() { - if (_file) + if (_file) { _file->sync(); + } } boolean File::seek(uint32_t pos) { - if (! _file) return false; + if (! _file) { + return false; + } return _file->seekSet(pos); } uint32_t File::position() { - if (! _file) return -1; + if (! _file) { + return -1; + } return _file->curPosition(); } uint32_t File::size() { - if (! _file) return 0; + if (! _file) { + return 0; + } return _file->fileSize(); } void File::close() { if (_file) { _file->close(); - free(_file); + free(_file); _file = 0; /* for debugging file open/close leaks - nfilecount--; - Serial.print("Deleted "); - Serial.println(nfilecount, DEC); + nfilecount--; + Serial.print("Deleted "); + Serial.println(nfilecount, DEC); */ } } File::operator bool() { - if (_file) + if (_file) { return _file->isOpen(); + } return false; } diff --git a/src/SD.cpp b/src/SD.cpp index fbadb62..244847d 100644 --- a/src/SD.cpp +++ b/src/SD.cpp @@ -1,43 +1,43 @@ /* - SD - a slightly more friendly wrapper for sdfatlib + SD - a slightly more friendly wrapper for sdfatlib - This library aims to expose a subset of SD card functionality - in the form of a higher level "wrapper" object. + This library aims to expose a subset of SD card functionality + in the form of a higher level "wrapper" object. - License: GNU General Public License V3 + License: GNU General Public License V3 (Because sdfatlib is licensed with this.) - (C) Copyright 2010 SparkFun Electronics + (C) Copyright 2010 SparkFun Electronics - This library provides four key benefits: + This library provides four key benefits: - * Including `SD.h` automatically creates a global + Including `SD.h` automatically creates a global `SD` object which can be interacted with in a similar manner to other standard global objects like `Serial` and `Ethernet`. - * Boilerplate initialisation code is contained in one method named + Boilerplate initialisation code is contained in one method named `begin` and no further objects need to be created in order to access the SD card. - * Calls to `open` can supply a full path name including parent + Calls to `open` can supply a full path name including parent directories which simplifies interacting with files in subdirectories. - * Utility methods are provided to determine whether a file exists - and to create a directory heirarchy. + Utility methods are provided to determine whether a file exists + and to create a directory hierarchy. Note however that not all functionality provided by the underlying sdfatlib library is exposed. - */ +*/ /* Implementation Notes - In order to handle multi-directory path traversal, functionality that + In order to handle multi-directory path traversal, functionality that requires this ability is implemented as callback functions. Individual methods call the `walkPath` function which performs the actual @@ -48,239 +48,239 @@ or make directory) which others will only take an action at the bottom level (e.g. open). - */ +*/ #include "SD.h" namespace SDLib { -// Used by `getNextPathComponent` + // Used by `getNextPathComponent` #define MAX_COMPONENT_LEN 12 // What is max length? #define PATH_COMPONENT_BUFFER_LEN MAX_COMPONENT_LEN+1 -bool getNextPathComponent(const char *path, unsigned int *p_offset, - char *buffer) { - /* + bool getNextPathComponent(const char *path, unsigned int *p_offset, + char *buffer) { + /* - Parse individual path components from a path. + Parse individual path components from a path. - e.g. after repeated calls '/foo/bar/baz' will be split - into 'foo', 'bar', 'baz'. + e.g. after repeated calls '/foo/bar/baz' will be split + into 'foo', 'bar', 'baz'. - This is similar to `strtok()` but copies the component into the - supplied buffer rather than modifying the original string. + This is similar to `strtok()` but copies the component into the + supplied buffer rather than modifying the original string. - `buffer` needs to be PATH_COMPONENT_BUFFER_LEN in size. + `buffer` needs to be PATH_COMPONENT_BUFFER_LEN in size. - `p_offset` needs to point to an integer of the offset at - which the previous path component finished. + `p_offset` needs to point to an integer of the offset at + which the previous path component finished. - Returns `true` if more components remain. + Returns `true` if more components remain. - Returns `false` if this is the last component. - (This means path ended with 'foo' or 'foo/'.) + Returns `false` if this is the last component. + (This means path ended with 'foo' or 'foo/'.) - */ + */ - // TODO: Have buffer local to this function, so we know it's the - // correct length? + // TODO: Have buffer local to this function, so we know it's the + // correct length? - int bufferOffset = 0; + int bufferOffset = 0; - int offset = *p_offset; + int offset = *p_offset; - // Skip root or other separator - if (path[offset] == '/') { - offset++; - } - - // Copy the next next path segment - while (bufferOffset < MAX_COMPONENT_LEN - && (path[offset] != '/') - && (path[offset] != '\0')) { - buffer[bufferOffset++] = path[offset++]; - } + // Skip root or other separator + if (path[offset] == '/') { + offset++; + } + + // Copy the next next path segment + while (bufferOffset < MAX_COMPONENT_LEN + && (path[offset] != '/') + && (path[offset] != '\0')) { + buffer[bufferOffset++] = path[offset++]; + } + + buffer[bufferOffset] = '\0'; + + // Skip trailing separator so we can determine if this + // is the last component in the path or not. + if (path[offset] == '/') { + offset++; + } - buffer[bufferOffset] = '\0'; + *p_offset = offset; - // Skip trailing separator so we can determine if this - // is the last component in the path or not. - if (path[offset] == '/') { - offset++; + return (path[offset] != '\0'); } - *p_offset = offset; - return (path[offset] != '\0'); -} + boolean walkPath(const char *filepath, SdFile& parentDir, + boolean(*callback)(SdFile& parentDir, + const char *filePathComponent, + boolean isLastComponent, + void *object), + void *object = NULL) { + /* + When given a file path (and parent directory--normally root), + this function traverses the directories in the path and at each + level calls the supplied callback function while also providing + the supplied object for context if required. -boolean walkPath(const char *filepath, SdFile& parentDir, - boolean (*callback)(SdFile& parentDir, - const char *filePathComponent, - boolean isLastComponent, - void *object), - void *object = NULL) { - /* - - When given a file path (and parent directory--normally root), - this function traverses the directories in the path and at each - level calls the supplied callback function while also providing - the supplied object for context if required. + e.g. given the path '/foo/bar/baz' + the callback would be called at the equivalent of + '/foo', '/foo/bar' and '/foo/bar/baz'. + + The implementation swaps between two different directory/file + handles as it traverses the directories and does not use recursion + in an attempt to use memory efficiently. - e.g. given the path '/foo/bar/baz' - the callback would be called at the equivalent of - '/foo', '/foo/bar' and '/foo/bar/baz'. + If a callback wishes to stop the directory traversal it should + return false--in this case the function will stop the traversal, + tidy up and return false. - The implementation swaps between two different directory/file - handles as it traverses the directories and does not use recursion - in an attempt to use memory efficiently. + If a directory path doesn't exist at some point this function will + also return false and not subsequently call the callback. - If a callback wishes to stop the directory traversal it should - return false--in this case the function will stop the traversal, - tidy up and return false. + If a directory path specified is complete, valid and the callback + did not indicate the traversal should be interrupted then this + function will return true. - If a directory path doesn't exist at some point this function will - also return false and not subsequently call the callback. + */ - If a directory path specified is complete, valid and the callback - did not indicate the traversal should be interrupted then this - function will return true. - */ + SdFile subfile1; + SdFile subfile2; + char buffer[PATH_COMPONENT_BUFFER_LEN]; - SdFile subfile1; - SdFile subfile2; + unsigned int offset = 0; - char buffer[PATH_COMPONENT_BUFFER_LEN]; + SdFile *p_parent; + SdFile *p_child; - unsigned int offset = 0; + SdFile *p_tmp_sdfile; - SdFile *p_parent; - SdFile *p_child; + p_child = &subfile1; - SdFile *p_tmp_sdfile; - - p_child = &subfile1; - - p_parent = &parentDir; + p_parent = &parentDir; - while (true) { + while (true) { - boolean moreComponents = getNextPathComponent(filepath, &offset, buffer); + boolean moreComponents = getNextPathComponent(filepath, &offset, buffer); - boolean shouldContinue = callback((*p_parent), buffer, !moreComponents, object); + boolean shouldContinue = callback((*p_parent), buffer, !moreComponents, object); + + if (!shouldContinue) { + // TODO: Don't repeat this code? + // If it's one we've created then we + // don't need the parent handle anymore. + if (p_parent != &parentDir) { + (*p_parent).close(); + } + return false; + } + + if (!moreComponents) { + break; + } + + boolean exists = (*p_child).open(*p_parent, buffer, O_RDONLY); - if (!shouldContinue) { - // TODO: Don't repeat this code? // If it's one we've created then we // don't need the parent handle anymore. if (p_parent != &parentDir) { (*p_parent).close(); } - return false; - } - - if (!moreComponents) { - break; + + // Handle case when it doesn't exist and we can't continue... + if (exists) { + // We alternate between two file handles as we go down + // the path. + if (p_parent == &parentDir) { + p_parent = &subfile2; + } + + p_tmp_sdfile = p_parent; + p_parent = p_child; + p_child = p_tmp_sdfile; + } else { + return false; + } } - - boolean exists = (*p_child).open(*p_parent, buffer, O_RDONLY); - // If it's one we've created then we - // don't need the parent handle anymore. if (p_parent != &parentDir) { - (*p_parent).close(); + (*p_parent).close(); // TODO: Return/ handle different? } - - // Handle case when it doesn't exist and we can't continue... - if (exists) { - // We alternate between two file handles as we go down - // the path. - if (p_parent == &parentDir) { - p_parent = &subfile2; - } - p_tmp_sdfile = p_parent; - p_parent = p_child; - p_child = p_tmp_sdfile; - } else { - return false; - } - } - - if (p_parent != &parentDir) { - (*p_parent).close(); // TODO: Return/ handle different? + return true; } - return true; -} + /* -/* + The callbacks used to implement various functionality follow. - The callbacks used to implement various functionality follow. + Each callback is supplied with a parent directory handle, + character string with the name of the current file path component, + a flag indicating if this component is the last in the path and + a pointer to an arbitrary object used for context. - Each callback is supplied with a parent directory handle, - character string with the name of the current file path component, - a flag indicating if this component is the last in the path and - a pointer to an arbitrary object used for context. + */ - */ + boolean callback_pathExists(SdFile& parentDir, const char *filePathComponent, + boolean /* isLastComponent */, void * /* object */) { + /* -boolean callback_pathExists(SdFile& parentDir, const char *filePathComponent, - boolean /* isLastComponent */, void * /* object */) { - /* + Callback used to determine if a file/directory exists in parent + directory. - Callback used to determine if a file/directory exists in parent - directory. + Returns true if file path exists. - Returns true if file path exists. + */ + SdFile child; - */ - SdFile child; + boolean exists = child.open(parentDir, filePathComponent, O_RDONLY); - boolean exists = child.open(parentDir, filePathComponent, O_RDONLY); - - if (exists) { - child.close(); + if (exists) { + child.close(); + } + + return exists; } - - return exists; -} -boolean callback_makeDirPath(SdFile& parentDir, const char *filePathComponent, - boolean isLastComponent, void *object) { - /* + boolean callback_makeDirPath(SdFile& parentDir, const char *filePathComponent, + boolean isLastComponent, void *object) { + /* - Callback used to create a directory in the parent directory if - it does not already exist. + Callback used to create a directory in the parent directory if + it does not already exist. - Returns true if a directory was created or it already existed. + Returns true if a directory was created or it already existed. - */ - boolean result = false; - SdFile child; - - result = callback_pathExists(parentDir, filePathComponent, isLastComponent, object); - if (!result) { - result = child.makeDir(parentDir, filePathComponent); - } - - return result; -} + */ + boolean result = false; + SdFile child; + + result = callback_pathExists(parentDir, filePathComponent, isLastComponent, object); + if (!result) { + result = child.makeDir(parentDir, filePathComponent); + } + + return result; + } /* -boolean callback_openPath(SdFile& parentDir, char *filePathComponent, - boolean isLastComponent, void *object) { + boolean callback_openPath(SdFile& parentDir, char *filePathComponent, + boolean isLastComponent, void *object) { Callback used to open a file specified by a filepath that may specify one or more directories above it. @@ -290,12 +290,12 @@ boolean callback_openPath(SdFile& parentDir, char *filePathComponent, file/directory with the associated file open mode property. Always returns true if the directory traversal hasn't reached the - bottom of the directory heirarchy. + bottom of the directory hierarchy. Returns false once the file has been opened--to prevent the traversal from descending further. (This may be unnecessary.) - if (isLastComponent) { + if (isLastComponent) { SDClass *p_SD = static_cast(object); p_SD->file.open(parentDir, filePathComponent, p_SD->fileOpenMode); if (p_SD->fileOpenMode == FILE_WRITE) { @@ -303,326 +303,335 @@ boolean callback_openPath(SdFile& parentDir, char *filePathComponent, } // TODO: Return file open result? return false; - } - return true; -} + } + return true; + } */ -boolean callback_remove(SdFile& parentDir, const char *filePathComponent, - boolean isLastComponent, void * /* object */) { - if (isLastComponent) { - return SdFile::remove(parentDir, filePathComponent); + boolean callback_remove(SdFile& parentDir, const char *filePathComponent, + boolean isLastComponent, void * /* object */) { + if (isLastComponent) { + return SdFile::remove(parentDir, filePathComponent); + } + return true; } - return true; -} - -boolean callback_rmdir(SdFile& parentDir, const char *filePathComponent, - boolean isLastComponent, void * /* object */) { - if (isLastComponent) { - SdFile f; - if (!f.open(parentDir, filePathComponent, O_READ)) return false; - return f.rmDir(); + + boolean callback_rmdir(SdFile& parentDir, const char *filePathComponent, + boolean isLastComponent, void * /* object */) { + if (isLastComponent) { + SdFile f; + if (!f.open(parentDir, filePathComponent, O_READ)) { + return false; + } + return f.rmDir(); + } + return true; } - return true; -} -/* Implementation of class used to create `SDCard` object. */ + /* Implementation of class used to create `SDCard` object. */ -boolean SDClass::begin(uint8_t csPin) { - if(root.isOpen()) root.close(); + boolean SDClass::begin(uint8_t csPin) { + if (root.isOpen()) { + root.close(); + } - /* + /* - Performs the initialisation required by the sdfatlib library. + Performs the initialisation required by the sdfatlib library. - Return true if initialization succeeds, false otherwise. + Return true if initialization succeeds, false otherwise. - */ - return card.init(SPI_HALF_SPEED, csPin) && - volume.init(card) && - root.openRoot(volume); -} + */ + return card.init(SPI_HALF_SPEED, csPin) && + volume.init(card) && + root.openRoot(volume); + } -boolean SDClass::begin(uint32_t clock, uint8_t csPin) { - if(root.isOpen()) root.close(); + boolean SDClass::begin(uint32_t clock, uint8_t csPin) { + if (root.isOpen()) { + root.close(); + } - return card.init(SPI_HALF_SPEED, csPin) && - card.setSpiClock(clock) && - volume.init(card) && - root.openRoot(volume); -} + return card.init(SPI_HALF_SPEED, csPin) && + card.setSpiClock(clock) && + volume.init(card) && + root.openRoot(volume); + } -//call this when a card is removed. It will allow you to insert and initialise a new card. -void SDClass::end() -{ - root.close(); -} + //call this when a card is removed. It will allow you to insert and initialise a new card. + void SDClass::end() { + root.close(); + } -// this little helper is used to traverse paths -SdFile SDClass::getParentDir(const char *filepath, int *index) { - // get parent directory - SdFile d1; - SdFile d2; + // this little helper is used to traverse paths + SdFile SDClass::getParentDir(const char *filepath, int *index) { + // get parent directory + SdFile d1; + SdFile d2; - d1.openRoot(volume); // start with the mostparent, root! + d1.openRoot(volume); // start with the mostparent, root! - // we'll use the pointers to swap between the two objects - SdFile *parent = &d1; - SdFile *subdir = &d2; - - const char *origpath = filepath; + // we'll use the pointers to swap between the two objects + SdFile *parent = &d1; + SdFile *subdir = &d2; - while (strchr(filepath, '/')) { + const char *origpath = filepath; - // get rid of leading /'s - if (filepath[0] == '/') { - filepath++; - continue; - } - - if (! strchr(filepath, '/')) { - // it was in the root directory, so leave now - break; - } + while (strchr(filepath, '/')) { - // extract just the name of the next subdirectory - uint8_t idx = strchr(filepath, '/') - filepath; - if (idx > 12) - idx = 12; // dont let them specify long names - char subdirname[13]; - strncpy(subdirname, filepath, idx); - subdirname[idx] = 0; - - // close the subdir (we reuse them) if open - subdir->close(); - if (! subdir->open(parent, subdirname, O_READ)) { - // failed to open one of the subdirectories - return SdFile(); - } - // move forward to the next subdirectory - filepath += idx; + // get rid of leading /'s + if (filepath[0] == '/') { + filepath++; + continue; + } + + if (! strchr(filepath, '/')) { + // it was in the root directory, so leave now + break; + } + + // extract just the name of the next subdirectory + uint8_t idx = strchr(filepath, '/') - filepath; + if (idx > 12) { + idx = 12; // don't let them specify long names + } + char subdirname[13]; + strncpy(subdirname, filepath, idx); + subdirname[idx] = 0; + + // close the subdir (we reuse them) if open + subdir->close(); + if (! subdir->open(parent, subdirname, O_READ)) { + // failed to open one of the subdirectories + return SdFile(); + } + // move forward to the next subdirectory + filepath += idx; - // we reuse the objects, close it. - parent->close(); + // we reuse the objects, close it. + parent->close(); - // swap the pointers - SdFile *t = parent; - parent = subdir; - subdir = t; + // swap the pointers + SdFile *t = parent; + parent = subdir; + subdir = t; + } + + *index = (int)(filepath - origpath); + // parent is now the parent directory of the file! + return *parent; } - *index = (int)(filepath - origpath); - // parent is now the parent diretory of the file! - return *parent; -} + File SDClass::open(const char *filepath, uint8_t mode) { + /* -File SDClass::open(const char *filepath, uint8_t mode) { - /* + Open the supplied file path for reading or writing. - Open the supplied file path for reading or writing. + The file content can be accessed via the `file` property of + the `SDClass` object--this property is currently + a standard `SdFile` object from `sdfatlib`. - The file content can be accessed via the `file` property of - the `SDClass` object--this property is currently - a standard `SdFile` object from `sdfatlib`. + Defaults to read only. - Defaults to read only. + If `write` is true, default action (when `append` is true) is to + append data to the end of the file. - If `write` is true, default action (when `append` is true) is to - append data to the end of the file. + If `append` is false then the file will be truncated first. - If `append` is false then the file will be truncated first. + If the file does not exist and it is opened for writing the file + will be created. - If the file does not exist and it is opened for writing the file - will be created. + An attempt to open a file for reading that does not exist is an + error. - An attempt to open a file for reading that does not exist is an - error. + */ - */ + int pathidx; - int pathidx; + // do the interative search + SdFile parentdir = getParentDir(filepath, &pathidx); + // no more subdirs! - // do the interative search - SdFile parentdir = getParentDir(filepath, &pathidx); - // no more subdirs! + filepath += pathidx; - filepath += pathidx; + if (! filepath[0]) { + // it was the directory itself! + return File(parentdir, "/"); + } - if (! filepath[0]) { - // it was the directory itself! - return File(parentdir, "/"); - } + // Open the file itself + SdFile file; - // Open the file itself - SdFile file; + // failed to open a subdir! + if (!parentdir.isOpen()) { + return File(); + } - // failed to open a subdir! - if (!parentdir.isOpen()) - return File(); + if (! file.open(parentdir, filepath, mode)) { + return File(); + } + // close the parent + parentdir.close(); - if ( ! file.open(parentdir, filepath, mode)) { - return File(); + if ((mode & (O_APPEND | O_WRITE)) == (O_APPEND | O_WRITE)) { + file.seekSet(file.fileSize()); + } + return File(file, filepath); } - // close the parent - parentdir.close(); - if ((mode & (O_APPEND | O_WRITE)) == (O_APPEND | O_WRITE)) - file.seekSet(file.fileSize()); - return File(file, filepath); -} + /* + File SDClass::open(char *filepath, uint8_t mode) { + // -/* -File SDClass::open(char *filepath, uint8_t mode) { - // + Open the supplied file path for reading or writing. + + The file content can be accessed via the `file` property of + the `SDClass` object--this property is currently + a standard `SdFile` object from `sdfatlib`. - Open the supplied file path for reading or writing. + Defaults to read only. - The file content can be accessed via the `file` property of - the `SDClass` object--this property is currently - a standard `SdFile` object from `sdfatlib`. + If `write` is true, default action (when `append` is true) is to + append data to the end of the file. - Defaults to read only. + If `append` is false then the file will be truncated first. - If `write` is true, default action (when `append` is true) is to - append data to the end of the file. + If the file does not exist and it is opened for writing the file + will be created. - If `append` is false then the file will be truncated first. + An attempt to open a file for reading that does not exist is an + error. - If the file does not exist and it is opened for writing the file - will be created. + // - An attempt to open a file for reading that does not exist is an - error. + // TODO: Allow for read&write? (Possibly not, as it requires seek.) - // + fileOpenMode = mode; + walkPath(filepath, root, callback_openPath, this); - // TODO: Allow for read&write? (Possibly not, as it requires seek.) + return File(); - fileOpenMode = mode; - walkPath(filepath, root, callback_openPath, this); + } + */ - return File(); -} -*/ + //boolean SDClass::close() { + // /* + // + // Closes the file opened by the `open` method. + // + // */ + // file.close(); + //} -//boolean SDClass::close() { -// /* -// -// Closes the file opened by the `open` method. -// -// */ -// file.close(); -//} + boolean SDClass::exists(const char *filepath) { + /* + Returns true if the supplied file path exists. -boolean SDClass::exists(const char *filepath) { - /* + */ + return walkPath(filepath, root, callback_pathExists); + } - Returns true if the supplied file path exists. - */ - return walkPath(filepath, root, callback_pathExists); -} + //boolean SDClass::exists(char *filepath, SdFile& parentDir) { + // /* + // + // Returns true if the supplied file path rooted at `parentDir` + // exists. + // + // */ + // return walkPath(filepath, parentDir, callback_pathExists); + //} -//boolean SDClass::exists(char *filepath, SdFile& parentDir) { -// /* -// -// Returns true if the supplied file path rooted at `parentDir` -// exists. -// -// */ -// return walkPath(filepath, parentDir, callback_pathExists); -//} + boolean SDClass::mkdir(const char *filepath) { + /* + Makes a single directory or a hierarchy of directories. -boolean SDClass::mkdir(const char *filepath) { - /* - - Makes a single directory or a heirarchy of directories. + A rough equivalent to `mkdir -p`. - A rough equivalent to `mkdir -p`. - - */ - return walkPath(filepath, root, callback_makeDirPath); -} + */ + return walkPath(filepath, root, callback_makeDirPath); + } -boolean SDClass::rmdir(const char *filepath) { - /* - - Remove a single directory or a heirarchy of directories. + boolean SDClass::rmdir(const char *filepath) { + /* - A rough equivalent to `rm -rf`. - - */ - return walkPath(filepath, root, callback_rmdir); -} + Remove a single directory or a hierarchy of directories. -boolean SDClass::remove(const char *filepath) { - return walkPath(filepath, root, callback_remove); -} + A rough equivalent to `rm -rf`. + */ + return walkPath(filepath, root, callback_rmdir); + } -// allows you to recurse into a directory -File File::openNextFile(uint8_t mode) { - dir_t p; + boolean SDClass::remove(const char *filepath) { + return walkPath(filepath, root, callback_remove); + } - //Serial.print("\t\treading dir..."); - while (_file->readDir(&p) > 0) { - // done if past last used entry - if (p.name[0] == DIR_NAME_FREE) { - //Serial.println("end"); - return File(); - } + // allows you to recurse into a directory + File File::openNextFile(uint8_t mode) { + dir_t p; - // skip deleted entry and entries for . and .. - if (p.name[0] == DIR_NAME_DELETED || p.name[0] == '.') { - //Serial.println("dots"); - continue; - } + //Serial.print("\t\treading dir..."); + while (_file->readDir(&p) > 0) { - // only list subdirectories and files - if (!DIR_IS_FILE_OR_SUBDIR(&p)) { - //Serial.println("notafile"); - continue; - } + // done if past last used entry + if (p.name[0] == DIR_NAME_FREE) { + //Serial.println("end"); + return File(); + } - // print file name with possible blank fill - SdFile f; - char name[13]; - _file->dirName(p, name); - //Serial.print("try to open file "); - //Serial.println(name); - - if (f.open(_file, name, mode)) { - //Serial.println("OK!"); - return File(f, name); - } else { - //Serial.println("ugh"); - return File(); + // skip deleted entry and entries for . and .. + if (p.name[0] == DIR_NAME_DELETED || p.name[0] == '.') { + //Serial.println("dots"); + continue; + } + + // only list subdirectories and files + if (!DIR_IS_FILE_OR_SUBDIR(&p)) { + //Serial.println("notafile"); + continue; + } + + // print file name with possible blank fill + SdFile f; + char name[13]; + _file->dirName(p, name); + //Serial.print("try to open file "); + //Serial.println(name); + + if (f.open(_file, name, mode)) { + //Serial.println("OK!"); + return File(f, name); + } else { + //Serial.println("ugh"); + return File(); + } } - } - //Serial.println("nothing"); - return File(); -} + //Serial.println("nothing"); + return File(); + } -void File::rewindDirectory(void) { - if (isDirectory()) - _file->rewind(); -} + void File::rewindDirectory(void) { + if (isDirectory()) { + _file->rewind(); + } + } -SDClass SD; + SDClass SD; }; diff --git a/src/SD.h b/src/SD.h index 65ae340..bb81fb4 100644 --- a/src/SD.h +++ b/src/SD.h @@ -1,16 +1,16 @@ /* - SD - a slightly more friendly wrapper for sdfatlib + SD - a slightly more friendly wrapper for sdfatlib - This library aims to expose a subset of SD card functionality - in the form of a higher level "wrapper" object. + This library aims to expose a subset of SD card functionality + in the form of a higher level "wrapper" object. - License: GNU General Public License V3 + License: GNU General Public License V3 (Because sdfatlib is licensed with this.) - (C) Copyright 2010 SparkFun Electronics + (C) Copyright 2010 SparkFun Electronics - */ +*/ #ifndef __SD_H__ #define __SD_H__ @@ -25,90 +25,100 @@ namespace SDLib { -class File : public Stream { - private: - char _name[13]; // our name - SdFile *_file; // underlying file pointer - -public: - File(SdFile f, const char *name); // wraps an underlying SdFile - File(void); // 'empty' constructor - virtual size_t write(uint8_t); - virtual size_t write(const uint8_t *buf, size_t size); - virtual int read(); - virtual int peek(); - virtual int available(); - virtual void flush(); - int read(void *buf, uint16_t nbyte); - boolean seek(uint32_t pos); - uint32_t position(); - uint32_t size(); - void close(); - operator bool(); - char * name(); - - boolean isDirectory(void); - File openNextFile(uint8_t mode = O_RDONLY); - void rewindDirectory(void); - - using Print::write; -}; - -class SDClass { - -private: - // These are required for initialisation and use of sdfatlib - Sd2Card card; - SdVolume volume; - SdFile root; - - // my quick&dirty iterator, should be replaced - SdFile getParentDir(const char *filepath, int *indx); -public: - // This needs to be called to set up the connection to the SD card - // before other methods are used. - boolean begin(uint8_t csPin = SD_CHIP_SELECT_PIN); - boolean begin(uint32_t clock, uint8_t csPin); - - //call this when a card is removed. It will allow you to insert and initialise a new card. - void end(); - - // Open the specified file/directory with the supplied mode (e.g. read or - // write, etc). Returns a File object for interacting with the file. - // Note that currently only one file can be open at a time. - File open(const char *filename, uint8_t mode = FILE_READ); - File open(const String &filename, uint8_t mode = FILE_READ) { return open( filename.c_str(), mode ); } - - // Methods to determine if the requested file path exists. - boolean exists(const char *filepath); - boolean exists(const String &filepath) { return exists(filepath.c_str()); } - - // Create the requested directory heirarchy--if intermediate directories - // do not exist they will be created. - boolean mkdir(const char *filepath); - boolean mkdir(const String &filepath) { return mkdir(filepath.c_str()); } - - // Delete the file. - boolean remove(const char *filepath); - boolean remove(const String &filepath) { return remove(filepath.c_str()); } - - boolean rmdir(const char *filepath); - boolean rmdir(const String &filepath) { return rmdir(filepath.c_str()); } - -private: - - // This is used to determine the mode used to open a file - // it's here because it's the easiest place to pass the - // information through the directory walking function. But - // it's probably not the best place for it. - // It shouldn't be set directly--it is set via the parameters to `open`. - int fileOpenMode; - - friend class File; - friend boolean callback_openPath(SdFile&, const char *, boolean, void *); -}; - -extern SDClass SD; + class File : public Stream { + private: + char _name[13]; // our name + SdFile *_file; // underlying file pointer + + public: + File(SdFile f, const char *name); // wraps an underlying SdFile + File(void); // 'empty' constructor + virtual size_t write(uint8_t); + virtual size_t write(const uint8_t *buf, size_t size); + virtual int read(); + virtual int peek(); + virtual int available(); + virtual void flush(); + int read(void *buf, uint16_t nbyte); + boolean seek(uint32_t pos); + uint32_t position(); + uint32_t size(); + void close(); + operator bool(); + char * name(); + + boolean isDirectory(void); + File openNextFile(uint8_t mode = O_RDONLY); + void rewindDirectory(void); + + using Print::write; + }; + + class SDClass { + + private: + // These are required for initialisation and use of sdfatlib + Sd2Card card; + SdVolume volume; + SdFile root; + + // my quick&dirty iterator, should be replaced + SdFile getParentDir(const char *filepath, int *indx); + public: + // This needs to be called to set up the connection to the SD card + // before other methods are used. + boolean begin(uint8_t csPin = SD_CHIP_SELECT_PIN); + boolean begin(uint32_t clock, uint8_t csPin); + + //call this when a card is removed. It will allow you to insert and initialise a new card. + void end(); + + // Open the specified file/directory with the supplied mode (e.g. read or + // write, etc). Returns a File object for interacting with the file. + // Note that currently only one file can be open at a time. + File open(const char *filename, uint8_t mode = FILE_READ); + File open(const String &filename, uint8_t mode = FILE_READ) { + return open(filename.c_str(), mode); + } + + // Methods to determine if the requested file path exists. + boolean exists(const char *filepath); + boolean exists(const String &filepath) { + return exists(filepath.c_str()); + } + + // Create the requested directory heirarchy--if intermediate directories + // do not exist they will be created. + boolean mkdir(const char *filepath); + boolean mkdir(const String &filepath) { + return mkdir(filepath.c_str()); + } + + // Delete the file. + boolean remove(const char *filepath); + boolean remove(const String &filepath) { + return remove(filepath.c_str()); + } + + boolean rmdir(const char *filepath); + boolean rmdir(const String &filepath) { + return rmdir(filepath.c_str()); + } + + private: + + // This is used to determine the mode used to open a file + // it's here because it's the easiest place to pass the + // information through the directory walking function. But + // it's probably not the best place for it. + // It shouldn't be set directly--it is set via the parameters to `open`. + int fileOpenMode; + + friend class File; + friend boolean callback_openPath(SdFile&, const char *, boolean, void *); + }; + + extern SDClass SD; }; diff --git a/src/utility/FatStructs.h b/src/utility/FatStructs.h index 8a2d9eb..84c1cc7 100644 --- a/src/utility/FatStructs.h +++ b/src/utility/FatStructs.h @@ -1,32 +1,32 @@ /* Arduino SdFat Library - * Copyright (C) 2009 by William Greiman - * - * This file is part of the Arduino SdFat Library - * - * This Library is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This Library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with the Arduino SdFat Library. If not, see - * . - */ + Copyright (C) 2009 by William Greiman + + This file is part of the Arduino SdFat Library + + This Library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the Arduino SdFat Library. If not, see + . +*/ #ifndef FatStructs_h #define FatStructs_h /** - * \file - * FAT file structures - */ + \file + FAT file structures +*/ /* - * mostly from Microsoft document fatgen103.doc - * http://www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx - */ + 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 */ uint8_t const BOOTSIG0 = 0X55; @@ -34,242 +34,242 @@ uint8_t const BOOTSIG0 = 0X55; uint8_t const BOOTSIG1 = 0XAA; //------------------------------------------------------------------------------ /** - * \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 + \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. - */ + /** + Boot Indicator . Indicates whether the volume is the active + partition. Legal values include: 0X00. Do not use for booting. + 0X80 Active partition. + */ uint8_t boot; - /** - * Head part of Cylinder-head-sector address of the first block in - * the partition. Legal values are 0-255. Only used in old PC BIOS. - */ + /** + Head part of Cylinder-head-sector address of the first block in + the partition. Legal values are 0-255. Only used in old PC BIOS. + */ uint8_t beginHead; - /** - * Sector part of Cylinder-head-sector address of the first block in - * the partition. Legal values are 1-63. Only used in old PC BIOS. - */ + /** + Sector part of Cylinder-head-sector address of the first block in + the partition. Legal values are 1-63. Only used in old PC BIOS. + */ unsigned beginSector : 6; - /** High bits cylinder for first block in partition. */ + /** High bits cylinder for first block in partition. */ unsigned beginCylinderHigh : 2; - /** - * Combine beginCylinderLow with beginCylinderHigh. Legal values - * are 0-1023. Only used in old PC BIOS. - */ + /** + Combine beginCylinderLow with beginCylinderHigh. Legal values + are 0-1023. Only used in old PC BIOS. + */ uint8_t beginCylinderLow; - /** - * Partition type. See defines that begin with PART_TYPE_ for - * some Microsoft partition types. - */ + /** + Partition type. See defines that begin with PART_TYPE_ for + some Microsoft partition types. + */ uint8_t type; - /** - * head part of cylinder-head-sector address of the last sector in the - * partition. Legal values are 0-255. Only used in old PC BIOS. - */ + /** + head part of cylinder-head-sector address of the last sector in the + partition. Legal values are 0-255. Only used in old PC BIOS. + */ uint8_t endHead; - /** - * Sector part of cylinder-head-sector address of the last sector in - * the partition. Legal values are 1-63. Only used in old PC BIOS. - */ + /** + Sector part of cylinder-head-sector address of the last sector in + the partition. Legal values are 1-63. Only used in old PC BIOS. + */ unsigned endSector : 6; - /** High bits of end cylinder */ + /** High bits of end cylinder */ unsigned endCylinderHigh : 2; - /** - * Combine endCylinderLow with endCylinderHigh. Legal values - * are 0-1023. Only used in old PC BIOS. - */ + /** + Combine endCylinderLow with endCylinderHigh. Legal values + are 0-1023. Only used in old PC BIOS. + */ uint8_t endCylinderLow; - /** Logical block address of the first block in the partition. */ + /** Logical block address of the first block in the partition. */ uint32_t firstSector; - /** Length of the partition, in blocks. */ + /** Length of the partition, in blocks. */ uint32_t totalSectors; } __attribute__((packed)); /** 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 + + \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. */ + /** Code Area for master boot program. */ uint8_t codeArea[440]; - /** Optional WindowsNT disk signature. May contain more boot code. */ + /** Optional WindowsNT disk signature. May contain more boot code. */ uint32_t diskSignature; - /** Usually zero but may be more boot code. */ + /** Usually zero but may be more boot code. */ uint16_t usuallyZero; - /** Partition tables. */ + /** Partition tables. */ part_t part[4]; - /** First MBR signature byte. Must be 0X55 */ + /** First MBR signature byte. Must be 0X55 */ uint8_t mbrSig0; - /** Second MBR signature byte. Must be 0XAA */ + /** Second MBR signature byte. Must be 0XAA */ uint8_t mbrSig1; } __attribute__((packed)); /** 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 + + \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 - */ + /** + 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. - */ + /** + 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. - */ + /** + 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. - */ + /** 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. - */ + /** + For FAT12 and FAT16 volumes, this field contains the count of + 32-byte directory entries in the root directory. For FAT32 volumes, + this field must be set to 0. For FAT12 and FAT16 volumes, this + value should always specify a count that when multiplied by 32 + results in a multiple of bytesPerSector. FAT16 volumes should + use the value 512. + */ uint16_t rootDirEntryCount; - /** - * This field is the old 16-bit total count of sectors on the volume. - * This count includes the count of all sectors in all four regions - * of the volume. This field can be 0; if it is 0, then totalSectors32 - * must be non-zero. For FAT32 volumes, this field must be 0. For - * FAT12 and FAT16 volumes, this field contains the sector count, and - * totalSectors32 is 0 if the total sector count fits - * (is less than 0x10000). - */ + /** + This field is the old 16-bit total count of sectors on the volume. + This count includes the count of all sectors in all four regions + of the volume. This field can be 0; if it is 0, then totalSectors32 + must be non-zero. For FAT32 volumes, this field must be 0. For + FAT12 and FAT16 volumes, this field contains the sector count, and + totalSectors32 is 0 if the total sector count fits + (is less than 0x10000). + */ uint16_t totalSectors16; - /** - * This dates back to the old MS-DOS 1.x media determination and is - * no longer usually used for anything. 0xF8 is the standard value - * for fixed (non-removable) media. For removable media, 0xF0 is - * frequently used. Legal values are 0xF0 or 0xF8-0xFF. - */ + /** + This dates back to the old MS-DOS 1.x media determination and is + no longer usually used for anything. 0xF8 is the standard value + for fixed (non-removable) media. For removable media, 0xF0 is + frequently used. Legal values are 0xF0 or 0xF8-0xFF. + */ uint8_t mediaType; - /** - * Count of sectors occupied by one FAT on FAT12/FAT16 volumes. - * On FAT32 volumes this field must be 0, and sectorsPerFat32 - * contains the FAT size count. - */ + /** + Count of sectors occupied by one FAT on FAT12/FAT16 volumes. + On FAT32 volumes this field must be 0, and sectorsPerFat32 + contains the FAT size count. + */ uint16_t sectorsPerFat16; - /** Sectors per track for interrupt 0x13. Not used otherwise. */ + /** Sectors per track for interrupt 0x13. Not used otherwise. */ uint16_t sectorsPerTrtack; - /** Number of heads for interrupt 0x13. Not used otherwise. */ + /** Number of heads for interrupt 0x13. Not used otherwise. */ uint16_t headCount; - /** - * Count of hidden sectors preceding the partition that contains this - * FAT volume. This field is generally only relevant for media - * visible on interrupt 0x13. - */ + /** + Count of hidden sectors preceding the partition that contains this + FAT volume. This field is generally only relevant for media + visible on interrupt 0x13. + */ uint32_t hidddenSectors; - /** - * This field is the new 32-bit total count of sectors on the volume. - * This count includes the count of all sectors in all four regions - * of the volume. This field can be 0; if it is 0, then - * totalSectors16 must be non-zero. - */ + /** + This field is the new 32-bit total count of sectors on the volume. + This count includes the count of all sectors in all four regions + of the volume. This field can be 0; if it is 0, then + totalSectors16 must be non-zero. + */ uint32_t totalSectors32; - /** - * Count of sectors occupied by one FAT on FAT32 volumes. - */ + /** + Count of sectors occupied by one FAT on FAT32 volumes. + */ uint32_t sectorsPerFat32; - /** - * This field is only defined for FAT32 media and does not exist on - * FAT12 and FAT16 media. - * Bits 0-3 -- Zero-based number of active FAT. - * Only valid if mirroring is disabled. - * Bits 4-6 -- Reserved. - * Bit 7 -- 0 means the FAT is mirrored at runtime into all FATs. - * -- 1 means only one FAT is active; it is the one referenced in bits 0-3. - * Bits 8-15 -- Reserved. - */ + /** + This field is only defined for FAT32 media and does not exist on + FAT12 and FAT16 media. + Bits 0-3 -- Zero-based number of active FAT. + Only valid if mirroring is disabled. + Bits 4-6 -- Reserved. + Bit 7 -- 0 means the FAT is mirrored at runtime into all FATs. + -- 1 means only one FAT is active; it is the one referenced in bits 0-3. + Bits 8-15 -- Reserved. + */ uint16_t fat32Flags; - /** - * FAT32 version. High byte is major revision number. - * Low byte is minor revision number. Only 0.0 define. - */ + /** + FAT32 version. High byte is major revision number. + Low byte is minor revision number. Only 0.0 define. + */ uint16_t fat32Version; - /** - * Cluster number of the first cluster of the root directory for FAT32. - * This usually 2 but not required to be 2. - */ + /** + Cluster number of the first cluster of the root directory for FAT32. + This usually 2 but not required to be 2. + */ uint32_t fat32RootCluster; - /** - * Sector number of FSINFO structure in the reserved area of the - * FAT32 volume. Usually 1. - */ + /** + Sector number of FSINFO structure in the reserved area of the + FAT32 volume. Usually 1. + */ uint16_t fat32FSInfo; - /** - * If non-zero, indicates the sector number in the reserved area - * of the volume of a copy of the boot record. Usually 6. - * No value other than 6 is recommended. - */ + /** + If non-zero, indicates the sector number in the reserved area + of the volume of a copy of the boot record. Usually 6. + No value other than 6 is recommended. + */ uint16_t fat32BackBootBlock; - /** - * Reserved for future expansion. Code that formats FAT32 volumes - * should always set all of the bytes of this field to 0. - */ + /** + Reserved for future expansion. Code that formats FAT32 volumes + should always set all of the bytes of this field to 0. + */ uint8_t fat32Reserved[12]; } __attribute__((packed)); /** Type name for biosParmBlock */ typedef struct biosParmBlock bpb_t; //------------------------------------------------------------------------------ /** - * \struct fat32BootSector - * - * \brief Boot sector for a FAT16 or FAT32 volume. - * - */ + \struct fat32BootSector + + \brief Boot sector for a FAT16 or FAT32 volume. + +*/ struct fat32BootSector { - /** X86 jmp to boot program */ + /** X86 jmp to boot program */ uint8_t jmpToBootCode[3]; - /** informational only - don't depend on it */ + /** informational only - don't depend on it */ char oemName[8]; - /** BIOS Parameter Block */ + /** BIOS Parameter Block */ bpb_t bpb; - /** for int0x13 use value 0X80 for hard drive */ + /** for int0x13 use value 0X80 for hard drive */ uint8_t driveNumber; - /** used by Windows NT - should be zero for FAT */ + /** used by Windows NT - should be zero for FAT */ uint8_t reserved1; - /** 0X29 if next three fields are valid */ + /** 0X29 if next three fields are valid */ uint8_t bootSignature; - /** usually generated by combining date and time */ + /** usually generated by combining date and time */ uint32_t volumeSerialNumber; - /** should match volume label in root dir */ + /** should match volume label in root dir */ char volumeLabel[11]; - /** informational only - don't depend on it */ + /** informational only - don't depend on it */ char fileSystemType[8]; - /** X86 boot code */ + /** X86 boot code */ uint8_t bootCode[420]; - /** must be 0X55 */ + /** must be 0X55 */ uint8_t bootSectorSig0; - /** must be 0XAA */ + /** must be 0XAA */ uint8_t bootSectorSig1; } __attribute__((packed)); //------------------------------------------------------------------------------ @@ -289,82 +289,82 @@ uint32_t const FAT32MASK = 0X0FFFFFFF; typedef struct fat32BootSector fbs_t; //------------------------------------------------------------------------------ /** - * \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 + \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. - */ + /** + Short 8.3 name. + The first eight bytes contain the file name with blank fill. + The last three bytes contain the file extension with blank fill. + */ uint8_t name[11]; - /** Entry attributes. - * - * The upper two bits of the attribute byte are reserved and should - * always be set to 0 when a file is created and never modified or - * looked at after that. See defines that begin with DIR_ATT_. - */ + /** Entry attributes. + + The upper two bits of the attribute byte are reserved and should + always be set to 0 when a file is created and never modified or + looked at after that. See defines that begin with DIR_ATT_. + */ uint8_t attributes; - /** - * Reserved for use by Windows NT. Set value to 0 when a file is - * created and never modify or look at it after that. - */ + /** + Reserved for use by Windows NT. Set value to 0 when a file is + created and never modify or look at it after that. + */ uint8_t reservedNT; - /** - * The granularity of the seconds part of creationTime is 2 seconds - * so this field is a count of tenths of a second and its valid - * value range is 0-199 inclusive. (WHG note - seems to be hundredths) - */ + /** + The granularity of the seconds part of creationTime is 2 seconds + so this field is a count of tenths of a second and its valid + value range is 0-199 inclusive. (WHG note - seems to be hundredths) + */ uint8_t creationTimeTenths; - /** Time file was created. */ + /** Time file was created. */ uint16_t creationTime; - /** Date file was created. */ + /** Date file was created. */ uint16_t creationDate; - /** - * Last access date. Note that there is no last access time, only - * a date. This is the date of last read or write. In the case of - * a write, this should be set to the same date as lastWriteDate. - */ + /** + Last access date. Note that there is no last access time, only + a date. This is the date of last read or write. In the case of + a write, this should be set to the same date as lastWriteDate. + */ uint16_t lastAccessDate; - /** - * High word of this entry's first cluster number (always 0 for a - * FAT12 or FAT16 volume). - */ + /** + High word of this entry's first cluster number (always 0 for a + FAT12 or FAT16 volume). + */ uint16_t firstClusterHigh; - /** Time of last write. File creation is considered a write. */ + /** Time of last write. File creation is considered a write. */ uint16_t lastWriteTime; - /** Date of last write. File creation is considered a write. */ + /** Date of last write. File creation is considered a write. */ uint16_t lastWriteDate; - /** Low word of this entry's first cluster number. */ + /** Low word of this entry's first cluster number. */ uint16_t firstClusterLow; - /** 32-bit unsigned holding this file's size in bytes. */ + /** 32-bit unsigned holding this file's size in bytes. */ uint32_t fileSize; } __attribute__((packed)); //------------------------------------------------------------------------------ diff --git a/src/utility/Sd2Card.cpp b/src/utility/Sd2Card.cpp index 406f838..586a92b 100644 --- a/src/utility/Sd2Card.cpp +++ b/src/utility/Sd2Card.cpp @@ -1,22 +1,22 @@ /* Arduino Sd2Card Library - * Copyright (C) 2009 by William Greiman - * - * This file is part of the Arduino Sd2Card Library - * - * This Library is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This Library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with the Arduino Sd2Card Library. If not, see - * . - */ + Copyright (C) 2009 by William Greiman + + This file is part of the Arduino Sd2Card Library + + This Library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the Arduino Sd2Card Library. If not, see + . +*/ #define USE_SPI_LIB #include #include "Sd2Card.h" @@ -24,32 +24,32 @@ #ifndef SOFTWARE_SPI #ifdef USE_SPI_LIB -#ifndef SDCARD_SPI -#define SDCARD_SPI SPI -#endif + #ifndef SDCARD_SPI + #define SDCARD_SPI SPI + #endif -#include -static SPISettings settings; + #include + static SPISettings settings; #endif // functions for hardware SPI /** Send a byte to the card */ static void spiSend(uint8_t b) { -#ifndef USE_SPI_LIB + #ifndef USE_SPI_LIB SPDR = b; while (!(SPSR & (1 << SPIF))) ; -#else + #else SDCARD_SPI.transfer(b); -#endif + #endif } /** Receive a byte from the card */ static uint8_t spiRec(void) { -#ifndef USE_SPI_LIB + #ifndef USE_SPI_LIB spiSend(0XFF); return SPDR; -#else + #else return SDCARD_SPI.transfer(0xFF); -#endif + #endif } #else // SOFTWARE_SPI //------------------------------------------------------------------------------ @@ -73,7 +73,9 @@ uint8_t spiRec(void) { data <<= 1; - if (fastDigitalRead(SPI_MISO_PIN)) data |= 1; + if (fastDigitalRead(SPI_MISO_PIN)) { + data |= 1; + } fastDigitalWrite(SPI_SCK_PIN, LOW); } @@ -122,12 +124,18 @@ uint8_t Sd2Card::cardCommand(uint8_t cmd, uint32_t arg) { spiSend(cmd | 0x40); // send argument - for (int8_t s = 24; s >= 0; s -= 8) spiSend(arg >> s); + for (int8_t s = 24; s >= 0; s -= 8) { + spiSend(arg >> s); + } // send CRC uint8_t crc = 0XFF; - if (cmd == CMD0) crc = 0X95; // correct crc for CMD0 with arg 0 - if (cmd == CMD8) crc = 0X87; // correct crc for CMD8 with arg 0X1AA + if (cmd == CMD0) { + crc = 0X95; // correct crc for CMD0 with arg 0 + } + if (cmd == CMD8) { + crc = 0X87; // correct crc for CMD8 with arg 0X1AA + } spiSend(crc); // wait for response @@ -137,14 +145,16 @@ uint8_t Sd2Card::cardCommand(uint8_t cmd, uint32_t arg) { } //------------------------------------------------------------------------------ /** - * Determine the size of an SD flash memory card. - * - * \return The number of 512 byte data blocks in the card - * or zero if an error occurs. - */ + Determine the size of an SD flash memory card. + + \return The number of 512 byte data blocks in the card + or zero if an error occurs. +*/ uint32_t Sd2Card::cardSize(void) { csd_t csd; - if (!readCSD(&csd)) return 0; + if (!readCSD(&csd)) { + return 0; + } if (csd.v1.csd_ver == 0) { uint8_t read_bl_len = csd.v1.read_bl_len; uint16_t c_size = (csd.v1.c_size_high << 10) @@ -166,37 +176,37 @@ static uint8_t chip_select_asserted = 0; void Sd2Card::chipSelectHigh(void) { digitalWrite(chipSelectPin_, HIGH); -#ifdef USE_SPI_LIB + #ifdef USE_SPI_LIB if (chip_select_asserted) { chip_select_asserted = 0; SDCARD_SPI.endTransaction(); } -#endif + #endif } //------------------------------------------------------------------------------ void Sd2Card::chipSelectLow(void) { -#ifdef USE_SPI_LIB + #ifdef USE_SPI_LIB if (!chip_select_asserted) { chip_select_asserted = 1; SDCARD_SPI.beginTransaction(settings); } -#endif + #endif digitalWrite(chipSelectPin_, LOW); } //------------------------------------------------------------------------------ /** Erase a range of blocks. - * - * \param[in] firstBlock The address of the first block in the range. - * \param[in] lastBlock The address of the last block in the range. - * - * \note This function requests the SD card to do a flash erase for a - * range of blocks. The data on the card after an erase operation is - * either 0 or 1, depends on the card vendor. The card must support - * single block erase. - * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. - */ + + \param[in] firstBlock The address of the first block in the range. + \param[in] lastBlock The address of the last block in the range. + + \note This function requests the SD card to do a flash erase for a + range of blocks. The data on the card after an erase operation is + either 0 or 1, depends on the card vendor. The card must support + single block erase. + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. +*/ uint8_t Sd2Card::erase(uint32_t firstBlock, uint32_t lastBlock) { if (!eraseSingleBlockEnable()) { error(SD_CARD_ERROR_ERASE_SINGLE_BLOCK); @@ -207,10 +217,10 @@ uint8_t Sd2Card::erase(uint32_t firstBlock, uint32_t lastBlock) { lastBlock <<= 9; } if (cardCommand(CMD32, firstBlock) - || cardCommand(CMD33, lastBlock) - || cardCommand(CMD38, 0)) { - error(SD_CARD_ERROR_ERASE); - goto fail; + || cardCommand(CMD33, lastBlock) + || cardCommand(CMD38, 0)) { + error(SD_CARD_ERROR_ERASE); + goto fail; } if (!waitNotBusy(SD_ERASE_TIMEOUT)) { error(SD_CARD_ERROR_ERASE_TIMEOUT); @@ -219,31 +229,31 @@ uint8_t Sd2Card::erase(uint32_t firstBlock, uint32_t lastBlock) { chipSelectHigh(); return true; - fail: +fail: chipSelectHigh(); return false; } //------------------------------------------------------------------------------ /** Determine if card supports single block erase. - * - * \return The value one, true, is returned if single block erase is supported. - * The value zero, false, is returned if single block erase is not supported. - */ + + \return The value one, true, is returned if single block erase is supported. + The value zero, false, is returned if single block erase is not supported. +*/ uint8_t Sd2Card::eraseSingleBlockEnable(void) { csd_t csd; return readCSD(&csd) ? csd.v1.erase_blk_en : 0; } //------------------------------------------------------------------------------ /** - * Initialize an SD flash memory card. - * - * \param[in] sckRateID SPI clock rate selector. See setSckRate(). - * \param[in] chipSelectPin SD chip select pin number. - * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. The reason for failure - * can be determined by calling errorCode() and errorData(). - */ + Initialize an SD flash memory card. + + \param[in] sckRateID SPI clock rate selector. See setSckRate(). + \param[in] chipSelectPin SD chip select pin number. + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. The reason for failure + can be determined by calling errorCode() and errorData(). +*/ uint8_t Sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin) { errorCode_ = inBlock_ = partialBlockRead_ = type_ = 0; chipSelectPin_ = chipSelectPin; @@ -254,14 +264,14 @@ uint8_t Sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin) { // set pin modes pinMode(chipSelectPin_, OUTPUT); digitalWrite(chipSelectPin_, HIGH); -#ifndef USE_SPI_LIB + #ifndef USE_SPI_LIB pinMode(SPI_MISO_PIN, INPUT); pinMode(SPI_MOSI_PIN, OUTPUT); pinMode(SPI_SCK_PIN, OUTPUT); -#endif + #endif -#ifndef SOFTWARE_SPI -#ifndef USE_SPI_LIB + #ifndef SOFTWARE_SPI + #ifndef USE_SPI_LIB // SS must be in output mode even it is not chip select pinMode(SS_PIN, OUTPUT); digitalWrite(SS_PIN, HIGH); // disable any SPI device using hardware SS pin @@ -269,20 +279,22 @@ uint8_t Sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin) { SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR1) | (1 << SPR0); // clear double speed SPSR &= ~(1 << SPI2X); -#else // USE_SPI_LIB + #else // USE_SPI_LIB SDCARD_SPI.begin(); settings = SPISettings(250000, MSBFIRST, SPI_MODE0); -#endif // USE_SPI_LIB -#endif // SOFTWARE_SPI + #endif // USE_SPI_LIB + #endif // SOFTWARE_SPI // must supply min of 74 clock cycles with CS high. -#ifdef USE_SPI_LIB + #ifdef USE_SPI_LIB SDCARD_SPI.beginTransaction(settings); -#endif - for (uint8_t i = 0; i < 10; i++) spiSend(0XFF); -#ifdef USE_SPI_LIB + #endif + for (uint8_t i = 0; i < 10; i++) { + spiSend(0XFF); + } + #ifdef USE_SPI_LIB SDCARD_SPI.endTransaction(); -#endif + #endif chipSelectLow(); @@ -299,7 +311,9 @@ uint8_t Sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin) { type(SD_CARD_TYPE_SD1); } else { // only need last byte of r7 response - for (uint8_t i = 0; i < 4; i++) status_ = spiRec(); + for (uint8_t i = 0; i < 4; i++) { + status_ = spiRec(); + } if (status_ != 0XAA) { error(SD_CARD_ERROR_CMD8); goto fail; @@ -323,74 +337,82 @@ uint8_t Sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin) { error(SD_CARD_ERROR_CMD58); goto fail; } - if ((spiRec() & 0XC0) == 0XC0) type(SD_CARD_TYPE_SDHC); + if ((spiRec() & 0XC0) == 0XC0) { + type(SD_CARD_TYPE_SDHC); + } // discard rest of ocr - contains allowed voltage range - for (uint8_t i = 0; i < 3; i++) spiRec(); + for (uint8_t i = 0; i < 3; i++) { + spiRec(); + } } chipSelectHigh(); -#ifndef SOFTWARE_SPI + #ifndef SOFTWARE_SPI return setSckRate(sckRateID); -#else // SOFTWARE_SPI + #else // SOFTWARE_SPI return true; -#endif // SOFTWARE_SPI + #endif // SOFTWARE_SPI - fail: +fail: chipSelectHigh(); return false; } //------------------------------------------------------------------------------ /** - * Enable or disable partial block reads. - * - * Enabling partial block reads improves performance by allowing a block - * to be read over the SPI bus as several sub-blocks. Errors may occur - * if the time between reads is too long since the SD card may timeout. - * The SPI SS line will be held low until the entire block is read or - * readEnd() is called. - * - * Use this for applications like the Adafruit Wave Shield. - * - * \param[in] value The value TRUE (non-zero) or FALSE (zero).) - */ + Enable or disable partial block reads. + + Enabling partial block reads improves performance by allowing a block + to be read over the SPI bus as several sub-blocks. Errors may occur + if the time between reads is too long since the SD card may timeout. + The SPI SS line will be held low until the entire block is read or + readEnd() is called. + + Use this for applications like the Adafruit Wave Shield. + + \param[in] value The value TRUE (non-zero) or FALSE (zero).) +*/ void Sd2Card::partialBlockRead(uint8_t value) { readEnd(); partialBlockRead_ = value; } //------------------------------------------------------------------------------ /** - * Read a 512 byte block from an SD card device. - * - * \param[in] block Logical block to be read. - * \param[out] dst Pointer to the location that will receive the data. - - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. - */ + Read a 512 byte block from an SD card device. + + \param[in] block Logical block to be read. + \param[out] dst Pointer to the location that will receive the data. + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. +*/ uint8_t Sd2Card::readBlock(uint32_t block, uint8_t* dst) { return readData(block, 0, 512, dst); } //------------------------------------------------------------------------------ /** - * Read part of a 512 byte block from an SD card. - * - * \param[in] block Logical block to be read. - * \param[in] offset Number of bytes to skip at start of block - * \param[out] dst Pointer to the location that will receive the data. - * \param[in] count Number of bytes to read - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. - */ + Read part of a 512 byte block from an SD card. + + \param[in] block Logical block to be read. + \param[in] offset Number of bytes to skip at start of block + \param[out] dst Pointer to the location that will receive the data. + \param[in] count Number of bytes to read + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. +*/ uint8_t Sd2Card::readData(uint32_t block, - uint16_t offset, uint16_t count, uint8_t* dst) { - if (count == 0) return true; + uint16_t offset, uint16_t count, uint8_t* dst) { + if (count == 0) { + return true; + } if ((count + offset) > 512) { goto fail; } if (!inBlock_ || block != block_ || offset < offset_) { block_ = block; // use address if not SDHC card - if (type()!= SD_CARD_TYPE_SDHC) block <<= 9; + if (type() != SD_CARD_TYPE_SDHC) { + block <<= 9; + } if (cardCommand(CMD17, block)) { error(SD_CARD_ERROR_CMD17); goto fail; @@ -402,12 +424,12 @@ uint8_t Sd2Card::readData(uint32_t block, inBlock_ = 1; } -#ifdef OPTIMIZE_HARDWARE_SPI + #ifdef OPTIMIZE_HARDWARE_SPI // start first spi transfer SPDR = 0XFF; // skip data before offset - for (;offset_ < offset; offset_++) { + for (; offset_ < offset; offset_++) { while (!(SPSR & (1 << SPIF))) ; SPDR = 0XFF; @@ -425,17 +447,17 @@ uint8_t Sd2Card::readData(uint32_t block, ; dst[n] = SPDR; -#else // OPTIMIZE_HARDWARE_SPI + #else // OPTIMIZE_HARDWARE_SPI // skip data before offset - for (;offset_ < offset; offset_++) { + for (; offset_ < offset; offset_++) { spiRec(); } // transfer data for (uint16_t i = 0; i < count; i++) { dst[i] = spiRec(); } -#endif // OPTIMIZE_HARDWARE_SPI + #endif // OPTIMIZE_HARDWARE_SPI offset_ += count; if (!partialBlockRead_ || offset_ >= 512) { @@ -444,7 +466,7 @@ uint8_t Sd2Card::readData(uint32_t block, } return true; - fail: +fail: chipSelectHigh(); return false; } @@ -452,8 +474,8 @@ uint8_t Sd2Card::readData(uint32_t block, /** Skip remaining data in a block when in partial block read mode. */ void Sd2Card::readEnd(void) { if (inBlock_) { - // skip data and crc -#ifdef OPTIMIZE_HARDWARE_SPI + // skip data and crc + #ifdef OPTIMIZE_HARDWARE_SPI // optimize skip for hardware SPDR = 0XFF; while (offset_++ < 513) { @@ -464,9 +486,11 @@ void Sd2Card::readEnd(void) { // wait for last crc byte while (!(SPSR & (1 << SPIF))) ; -#else // OPTIMIZE_HARDWARE_SPI - while (offset_++ < 514) spiRec(); -#endif // OPTIMIZE_HARDWARE_SPI + #else // OPTIMIZE_HARDWARE_SPI + while (offset_++ < 514) { + spiRec(); + } + #endif // OPTIMIZE_HARDWARE_SPI chipSelectHigh(); inBlock_ = 0; } @@ -479,47 +503,51 @@ uint8_t Sd2Card::readRegister(uint8_t cmd, void* buf) { error(SD_CARD_ERROR_READ_REG); goto fail; } - if (!waitStartBlock()) goto fail; + if (!waitStartBlock()) { + goto fail; + } // transfer data - for (uint16_t i = 0; i < 16; i++) dst[i] = spiRec(); + for (uint16_t i = 0; i < 16; i++) { + dst[i] = spiRec(); + } spiRec(); // get first crc byte spiRec(); // get second crc byte chipSelectHigh(); return true; - fail: +fail: chipSelectHigh(); return false; } //------------------------------------------------------------------------------ /** - * Set the SPI clock rate. - * - * \param[in] sckRateID A value in the range [0, 6]. - * - * The SPI clock will be set to F_CPU/pow(2, 1 + sckRateID). The maximum - * SPI rate is F_CPU/2 for \a sckRateID = 0 and the minimum rate is F_CPU/128 - * for \a scsRateID = 6. - * - * \return The value one, true, is returned for success and the value zero, - * false, is returned for an invalid value of \a sckRateID. - */ + Set the SPI clock rate. + + \param[in] sckRateID A value in the range [0, 6]. + + The SPI clock will be set to F_CPU/pow(2, 1 + sckRateID). The maximum + SPI rate is F_CPU/2 for \a sckRateID = 0 and the minimum rate is F_CPU/128 + for \a scsRateID = 6. + + \return The value one, true, is returned for success and the value zero, + false, is returned for an invalid value of \a sckRateID. +*/ uint8_t Sd2Card::setSckRate(uint8_t sckRateID) { if (sckRateID > 6) { error(SD_CARD_ERROR_SCK_RATE); return false; } -#ifndef USE_SPI_LIB + #ifndef USE_SPI_LIB // see avr processor datasheet for SPI register bit definitions if ((sckRateID & 1) || sckRateID == 6) { SPSR &= ~(1 << SPI2X); } else { SPSR |= (1 << SPI2X); } - SPCR &= ~((1 <. - */ + Copyright (C) 2009 by William Greiman + + This file is part of the Arduino Sd2Card Library + + This Library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the Arduino Sd2Card Library. If not, see + . +*/ #ifndef Sd2Card_h #define Sd2Card_h /** - * \file - * Sd2Card class - */ + \file + Sd2Card class +*/ #include "Sd2PinMap.h" #include "SdInfo.h" /** Set SCK to max rate of F_CPU/2. See Sd2Card::setSckRate(). */ @@ -32,76 +32,76 @@ uint8_t const SPI_HALF_SPEED = 1; /** Set SCK rate to F_CPU/8. Sd2Card::setSckRate(). */ uint8_t const SPI_QUARTER_SPEED = 2; /** - * USE_SPI_LIB: if set, use the SPI library bundled with Arduino IDE, otherwise - * run with a standalone driver for AVR. - */ + USE_SPI_LIB: if set, use the SPI library bundled with Arduino IDE, otherwise + run with a standalone driver for AVR. +*/ #define USE_SPI_LIB /** - * Define MEGA_SOFT_SPI non-zero to use software SPI on Mega Arduinos. - * Pins used are SS 10, MOSI 11, MISO 12, and SCK 13. - * - * MEGA_SOFT_SPI allows an unmodified Adafruit GPS Shield to be used - * on Mega Arduinos. Software SPI works well with GPS Shield V1.1 - * but many SD cards will fail with GPS Shield V1.0. - */ + Define MEGA_SOFT_SPI non-zero to use software SPI on Mega Arduinos. + Pins used are SS 10, MOSI 11, MISO 12, and SCK 13. + + MEGA_SOFT_SPI allows an unmodified Adafruit GPS Shield to be used + on Mega Arduinos. Software SPI works well with GPS Shield V1.1 + but many SD cards will fail with GPS Shield V1.0. +*/ #define MEGA_SOFT_SPI 0 //------------------------------------------------------------------------------ #if MEGA_SOFT_SPI && (defined(__AVR_ATmega1280__)||defined(__AVR_ATmega2560__)) -#define SOFTWARE_SPI + #define SOFTWARE_SPI #endif // MEGA_SOFT_SPI //------------------------------------------------------------------------------ // SPI pin definitions // #ifndef SOFTWARE_SPI -// hardware pin defs + // hardware pin defs -// include pins_arduino.h or variant.h depending on architecture, via Arduino.h -#include + // include pins_arduino.h or variant.h depending on architecture, via Arduino.h + #include -/** - * SD Chip Select pin - * - * Warning if this pin is redefined the hardware SS will pin will be enabled - * as an output by init(). An avr processor will not function as an SPI - * master unless SS is set to output mode. - */ -#ifndef SDCARD_SS_PIN -/** The default chip select pin for the SD card is SS. */ -uint8_t const SD_CHIP_SELECT_PIN = SS; -#else -uint8_t const SD_CHIP_SELECT_PIN = SDCARD_SS_PIN; -#endif + /** + SD Chip Select pin -// The following three pins must not be redefined for hardware SPI, -// so ensure that they are taken from pins_arduino.h or variant.h, depending on architecture. -#ifndef SDCARD_MOSI_PIN -/** SPI Master Out Slave In pin */ -uint8_t const SPI_MOSI_PIN = MOSI; -/** SPI Master In Slave Out pin */ -uint8_t const SPI_MISO_PIN = MISO; -/** SPI Clock pin */ -uint8_t const SPI_SCK_PIN = SCK; -#else -uint8_t const SPI_MOSI_PIN = SDCARD_MOSI_PIN; -uint8_t const SPI_MISO_PIN = SDCARD_MISO_PIN; -uint8_t const SPI_SCK_PIN = SDCARD_SCK_PIN; -#endif + Warning if this pin is redefined the hardware SS will pin will be enabled + as an output by init(). An avr processor will not function as an SPI + master unless SS is set to output mode. + */ + #ifndef SDCARD_SS_PIN + /** The default chip select pin for the SD card is SS. */ + uint8_t const SD_CHIP_SELECT_PIN = SS; + #else + uint8_t const SD_CHIP_SELECT_PIN = SDCARD_SS_PIN; + #endif -/** optimize loops for hardware SPI */ -#ifndef USE_SPI_LIB -#define OPTIMIZE_HARDWARE_SPI -#endif + // The following three pins must not be redefined for hardware SPI, + // so ensure that they are taken from pins_arduino.h or variant.h, depending on architecture. + #ifndef SDCARD_MOSI_PIN + /** SPI Master Out Slave In pin */ + uint8_t const SPI_MOSI_PIN = MOSI; + /** SPI Master In Slave Out pin */ + uint8_t const SPI_MISO_PIN = MISO; + /** SPI Clock pin */ + uint8_t const SPI_SCK_PIN = SCK; + #else + uint8_t const SPI_MOSI_PIN = SDCARD_MOSI_PIN; + uint8_t const SPI_MISO_PIN = SDCARD_MISO_PIN; + uint8_t const SPI_SCK_PIN = SDCARD_SCK_PIN; + #endif + + /** optimize loops for hardware SPI */ + #ifndef USE_SPI_LIB + #define OPTIMIZE_HARDWARE_SPI + #endif #else // SOFTWARE_SPI -// define software SPI pins so Mega can use unmodified GPS Shield -/** SPI chip select pin */ -uint8_t const SD_CHIP_SELECT_PIN = 10; -/** SPI Master Out Slave In pin */ -uint8_t const SPI_MOSI_PIN = 11; -/** SPI Master In Slave Out pin */ -uint8_t const SPI_MISO_PIN = 12; -/** SPI Clock pin */ -uint8_t const SPI_SCK_PIN = 13; + // define software SPI pins so Mega can use unmodified GPS Shield + /** SPI chip select pin */ + uint8_t const SD_CHIP_SELECT_PIN = 10; + /** SPI Master Out Slave In pin */ + uint8_t const SPI_MOSI_PIN = 11; + /** SPI Master In Slave Out pin */ + uint8_t const SPI_MISO_PIN = 12; + /** SPI Clock pin */ + uint8_t const SPI_SCK_PIN = 13; #endif // SOFTWARE_SPI //------------------------------------------------------------------------------ /** Protect block zero from write if nonzero */ @@ -170,91 +170,103 @@ uint8_t const SD_CARD_TYPE_SD2 = 2; uint8_t const SD_CARD_TYPE_SDHC = 3; //------------------------------------------------------------------------------ /** - * \class Sd2Card - * \brief Raw access to SD and SDHC flash memory cards. - */ + \class Sd2Card + \brief Raw access to SD and SDHC flash memory cards. +*/ class Sd2Card { - public: - /** Construct an instance of Sd2Card. */ - Sd2Card(void) : errorCode_(0), inBlock_(0), partialBlockRead_(0), type_(0) {} - uint32_t cardSize(void); - uint8_t erase(uint32_t firstBlock, uint32_t lastBlock); - uint8_t eraseSingleBlockEnable(void); - /** - * \return error code for last error. See Sd2Card.h for a list of error codes. - */ - uint8_t errorCode(void) const {return errorCode_;} - /** \return error data for last error. */ - uint8_t errorData(void) const {return status_;} - /** - * Initialize an SD flash memory card with default clock rate and chip - * select pin. See sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin). - */ - uint8_t init(void) { - return init(SPI_FULL_SPEED, SD_CHIP_SELECT_PIN); - } - /** - * Initialize an SD flash memory card with the selected SPI clock rate - * and the default SD chip select pin. - * See sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin). - */ - uint8_t init(uint8_t sckRateID) { - return init(sckRateID, SD_CHIP_SELECT_PIN); - } - uint8_t init(uint8_t sckRateID, uint8_t chipSelectPin); - void partialBlockRead(uint8_t value); - /** Returns the current value, true or false, for partial block read. */ - uint8_t partialBlockRead(void) const {return partialBlockRead_;} - uint8_t readBlock(uint32_t block, uint8_t* dst); - uint8_t readData(uint32_t block, - uint16_t offset, uint16_t count, uint8_t* dst); - /** - * Read a cards CID register. The CID contains card identification - * information such as Manufacturer ID, Product name, Product serial - * number and Manufacturing date. */ - uint8_t readCID(cid_t* cid) { - return readRegister(CMD10, cid); - } - /** - * Read a cards CSD register. The CSD contains Card-Specific Data that - * provides information regarding access to the card's contents. */ - uint8_t readCSD(csd_t* csd) { - return readRegister(CMD9, csd); - } - void readEnd(void); - uint8_t setSckRate(uint8_t sckRateID); -#ifdef USE_SPI_LIB - uint8_t setSpiClock(uint32_t clock); -#endif - /** Return the card type: SD V1, SD V2 or SDHC */ - uint8_t type(void) const {return type_;} - uint8_t writeBlock(uint32_t blockNumber, const uint8_t* src); - uint8_t writeData(const uint8_t* src); - uint8_t writeStart(uint32_t blockNumber, uint32_t eraseCount); - uint8_t writeStop(void); - private: - uint32_t block_; - uint8_t chipSelectPin_; - uint8_t errorCode_; - uint8_t inBlock_; - uint16_t offset_; - uint8_t partialBlockRead_; - uint8_t status_; - uint8_t type_; - // 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); - void error(uint8_t code) {errorCode_ = code;} - uint8_t readRegister(uint8_t cmd, void* buf); - uint8_t sendWriteCommand(uint32_t blockNumber, uint32_t eraseCount); - void chipSelectHigh(void); - void chipSelectLow(void); - void type(uint8_t value) {type_ = value;} - uint8_t waitNotBusy(unsigned int timeoutMillis); - uint8_t writeData(uint8_t token, const uint8_t* src); - uint8_t waitStartBlock(void); + public: + /** Construct an instance of Sd2Card. */ + Sd2Card(void) : errorCode_(0), inBlock_(0), partialBlockRead_(0), type_(0) {} + uint32_t cardSize(void); + uint8_t erase(uint32_t firstBlock, uint32_t lastBlock); + uint8_t eraseSingleBlockEnable(void); + /** + \return error code for last error. See Sd2Card.h for a list of error codes. + */ + uint8_t errorCode(void) const { + return errorCode_; + } + /** \return error data for last error. */ + uint8_t errorData(void) const { + return status_; + } + /** + Initialize an SD flash memory card with default clock rate and chip + select pin. See sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin). + */ + uint8_t init(void) { + return init(SPI_FULL_SPEED, SD_CHIP_SELECT_PIN); + } + /** + Initialize an SD flash memory card with the selected SPI clock rate + and the default SD chip select pin. + See sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin). + */ + uint8_t init(uint8_t sckRateID) { + return init(sckRateID, SD_CHIP_SELECT_PIN); + } + uint8_t init(uint8_t sckRateID, uint8_t chipSelectPin); + void partialBlockRead(uint8_t value); + /** Returns the current value, true or false, for partial block read. */ + uint8_t partialBlockRead(void) const { + return partialBlockRead_; + } + uint8_t readBlock(uint32_t block, uint8_t* dst); + uint8_t readData(uint32_t block, + uint16_t offset, uint16_t count, uint8_t* dst); + /** + Read a cards CID register. The CID contains card identification + information such as Manufacturer ID, Product name, Product serial + number and Manufacturing date. */ + uint8_t readCID(cid_t* cid) { + return readRegister(CMD10, cid); + } + /** + Read a cards CSD register. The CSD contains Card-Specific Data that + provides information regarding access to the card's contents. */ + uint8_t readCSD(csd_t* csd) { + return readRegister(CMD9, csd); + } + void readEnd(void); + uint8_t setSckRate(uint8_t sckRateID); + #ifdef USE_SPI_LIB + uint8_t setSpiClock(uint32_t clock); + #endif + /** Return the card type: SD V1, SD V2 or SDHC */ + uint8_t type(void) const { + return type_; + } + uint8_t writeBlock(uint32_t blockNumber, const uint8_t* src); + uint8_t writeData(const uint8_t* src); + uint8_t writeStart(uint32_t blockNumber, uint32_t eraseCount); + uint8_t writeStop(void); + private: + uint32_t block_; + uint8_t chipSelectPin_; + uint8_t errorCode_; + uint8_t inBlock_; + uint16_t offset_; + uint8_t partialBlockRead_; + uint8_t status_; + uint8_t type_; + // 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); + void error(uint8_t code) { + errorCode_ = code; + } + uint8_t readRegister(uint8_t cmd, void* buf); + uint8_t sendWriteCommand(uint32_t blockNumber, uint32_t eraseCount); + void chipSelectHigh(void); + void chipSelectLow(void); + void type(uint8_t value) { + type_ = value; + } + uint8_t waitNotBusy(unsigned int timeoutMillis); + uint8_t writeData(uint8_t token, const uint8_t* src); + uint8_t waitStartBlock(void); }; #endif // Sd2Card_h diff --git a/src/utility/Sd2PinMap.h b/src/utility/Sd2PinMap.h index 67b4db4..0609ffe 100644 --- a/src/utility/Sd2PinMap.h +++ b/src/utility/Sd2PinMap.h @@ -1,47 +1,47 @@ /* Arduino SdFat Library - * Copyright (C) 2010 by William Greiman - * - * This file is part of the Arduino SdFat Library - * - * This Library is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This Library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with the Arduino SdFat Library. If not, see - * . - */ + Copyright (C) 2010 by William Greiman + + This file is part of the Arduino SdFat Library + + This Library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the Arduino SdFat Library. If not, see + . +*/ #if defined(__arm__) // Arduino Due Board follows #ifndef Sd2PinMap_h -#define Sd2PinMap_h + #define Sd2PinMap_h -#include + #include -uint8_t const SS_PIN = SS; -uint8_t const MOSI_PIN = MOSI; -uint8_t const MISO_PIN = MISO; -uint8_t const SCK_PIN = SCK; + uint8_t const SS_PIN = SS; + uint8_t const MOSI_PIN = MOSI; + uint8_t const MISO_PIN = MISO; + uint8_t const SCK_PIN = SCK; #endif // Sd2PinMap_h #elif defined(__AVR_ATmega4809__) // Arduino UNO WiFI Rev2 follows #ifndef Sd2PinMap_h -#define Sd2PinMap_h + #define Sd2PinMap_h -#include + #include -uint8_t const SS_PIN = SS; -uint8_t const MOSI_PIN = MOSI; -uint8_t const MISO_PIN = MISO; -uint8_t const SCK_PIN = SCK; + uint8_t const SS_PIN = SS; + uint8_t const MOSI_PIN = MOSI; + uint8_t const MISO_PIN = MISO; + uint8_t const SCK_PIN = SCK; #endif // Sd2PinMap_h @@ -193,7 +193,7 @@ static const pin_map_t digitalPinMap[] = { {&DDRA, &PINA, &PORTA, 2}, // D29 PA2 {&DDRA, &PINA, &PORTA, 1}, // D30 PA1 {&DDRA, &PINA, &PORTA, 0} // D31 PA0 -}; +}; //------------------------------------------------------------------------------ #elif defined(__AVR_ATmega128RFA1__) && defined(CORE_MICRODUINO) // Microduino Core RF @@ -231,7 +231,7 @@ static const pin_map_t digitalPinMap[] = { {&DDRD, &PIND, &PORTD, 0}, // D19 PD0 {&DDRF, &PINF, &PORTF, 3}, // D20 PF3 {&DDRF, &PINF, &PORTF, 2}, // D21 PF2 -}; +}; //------------------------------------------------------------------------------ #elif defined(__AVR_ATmega32U4__) && defined(CORE_MICRODUINO) // Microduino Core USB @@ -269,7 +269,7 @@ static const pin_map_t digitalPinMap[] = { {&DDRD, &PIND, &PORTD, 0}, // D19 - PD0 {&DDRF, &PINF, &PORTF, 1}, // D20 - A6 - PF1 {&DDRF, &PINF, &PORTF, 0}, // D21 - A7 - PF0 -}; +}; //------------------------------------------------------------------------------ #elif defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__) // Sanguino @@ -458,13 +458,13 @@ static const pin_map_t digitalPinMap[] = { }; #endif // defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) //------------------------------------------------------------------------------ -static const uint8_t digitalPinCount = sizeof(digitalPinMap)/sizeof(pin_map_t); +static const uint8_t digitalPinCount = sizeof(digitalPinMap) / sizeof(pin_map_t); uint8_t badPinNumber(void) - __attribute__((error("Pin number is too large or not a constant"))); +__attribute__((error("Pin number is too large or not a constant"))); static inline __attribute__((always_inline)) - uint8_t getPinMode(uint8_t pin) { +uint8_t getPinMode(uint8_t pin) { if (__builtin_constant_p(pin) && pin < digitalPinCount) { return (*digitalPinMap[pin].ddr >> digitalPinMap[pin].bit) & 1; } else { @@ -472,7 +472,7 @@ static inline __attribute__((always_inline)) } } static inline __attribute__((always_inline)) - void setPinMode(uint8_t pin, uint8_t mode) { +void setPinMode(uint8_t pin, uint8_t mode) { if (__builtin_constant_p(pin) && pin < digitalPinCount) { if (mode) { *digitalPinMap[pin].ddr |= 1 << digitalPinMap[pin].bit; @@ -484,7 +484,7 @@ static inline __attribute__((always_inline)) } } static inline __attribute__((always_inline)) - uint8_t fastDigitalRead(uint8_t pin) { +uint8_t fastDigitalRead(uint8_t pin) { if (__builtin_constant_p(pin) && pin < digitalPinCount) { return (*digitalPinMap[pin].pin >> digitalPinMap[pin].bit) & 1; } else { @@ -492,7 +492,7 @@ static inline __attribute__((always_inline)) } } static inline __attribute__((always_inline)) - void fastDigitalWrite(uint8_t pin, uint8_t value) { +void fastDigitalWrite(uint8_t pin, uint8_t value) { if (__builtin_constant_p(pin) && pin < digitalPinCount) { if (value) { *digitalPinMap[pin].port |= 1 << digitalPinMap[pin].bit; @@ -508,15 +508,15 @@ static inline __attribute__((always_inline)) #elif defined (__CPU_ARC__) #if defined (__ARDUINO_ARC__) -// Two Wire (aka I2C) ports -uint8_t const SDA_PIN = 18; -uint8_t const SCL_PIN = 19; - -// SPI port -uint8_t const SS_PIN = 10; -uint8_t const MOSI_PIN = 11; -uint8_t const MISO_PIN = 12; -uint8_t const SCK_PIN = 13; + // Two Wire (aka I2C) ports + uint8_t const SDA_PIN = 18; + uint8_t const SCL_PIN = 19; + + // SPI port + uint8_t const SS_PIN = 10; + uint8_t const MOSI_PIN = 11; + uint8_t const MISO_PIN = 12; + uint8_t const SCK_PIN = 13; #endif // Arduino ARC diff --git a/src/utility/SdFat.h b/src/utility/SdFat.h index ef47ad0..acad32e 100644 --- a/src/utility/SdFat.h +++ b/src/utility/SdFat.h @@ -1,38 +1,38 @@ /* Arduino SdFat Library - * Copyright (C) 2009 by William Greiman - * - * This file is part of the Arduino SdFat Library - * - * This Library is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This Library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with the Arduino SdFat Library. If not, see - * . - */ + Copyright (C) 2009 by William Greiman + + This file is part of the Arduino SdFat Library + + This Library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the Arduino SdFat Library. If not, see + . +*/ #ifndef SdFat_h #define SdFat_h /** - * \file - * SdFile and SdVolume classes - */ -#if defined (__AVR__) || defined (__CPU_ARC__) -#include + \file + SdFile and SdVolume classes +*/ +#if defined (__AVR__) || defined (__CPU_ARC__) + #include #endif #include "Sd2Card.h" #include "FatStructs.h" #include //------------------------------------------------------------------------------ /** - * Allow use of deprecated functions if non-zero - */ + Allow use of deprecated functions if non-zero +*/ #define ALLOW_DEPRECATED_FUNCTIONS 1 //------------------------------------------------------------------------------ // forward declaration since SdVolume is used in SdFile @@ -119,11 +119,11 @@ static inline uint8_t FAT_HOUR(uint16_t fatTime) { } /** minute part of FAT directory time field */ static inline uint8_t FAT_MINUTE(uint16_t fatTime) { - return(fatTime >> 5) & 0X3F; + return (fatTime >> 5) & 0X3F; } /** second part of FAT directory time field */ static inline uint8_t FAT_SECOND(uint16_t fatTime) { - return 2*(fatTime & 0X1F); + return 2 * (fatTime & 0X1F); } /** Default date for file timestamps is 1 Jan 2000 */ uint16_t const FAT_DEFAULT_DATE = ((2000 - 1980) << 9) | (1 << 5) | 1; @@ -131,421 +131,487 @@ uint16_t const FAT_DEFAULT_DATE = ((2000 - 1980) << 9) | (1 << 5) | 1; uint16_t const FAT_DEFAULT_TIME = (1 << 11); //------------------------------------------------------------------------------ /** - * \class SdFile - * \brief Access FAT16 and FAT32 files on SD and SDHC cards. - */ + \class SdFile + \brief Access FAT16 and FAT32 files on SD and SDHC cards. +*/ class SdFile : public Print { - public: - /** Create an instance of SdFile. */ - SdFile(void) : type_(FAT_FILE_TYPE_CLOSED) {} - /** - * writeError is set to true if an error occurs during a write(). - * Set writeError to false before calling print() and/or write() and check - * for true after calls to print() and/or write(). - */ - //bool writeError; - /** - * Cancel unbuffered reads for this file. - * See setUnbufferedRead() - */ - void clearUnbufferedRead(void) { - flags_ &= ~F_FILE_UNBUFFERED_READ; - } - uint8_t close(void); - uint8_t contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock); - uint8_t createContiguous(SdFile* dirFile, - const char* fileName, uint32_t size); - /** \return The current cluster number for a file or directory. */ - uint32_t curCluster(void) const {return curCluster_;} - /** \return The current position for a file or directory. */ - uint32_t curPosition(void) const {return curPosition_;} - /** - * 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)) { - dateTime_ = dateTime; - } - /** - * Cancel the date/time callback function. - */ - static void dateTimeCallbackCancel(void) { - // use explicit zero since NULL is not defined for Sanguino - dateTime_ = 0; - } - /** \return Address of the block that contains this file's directory. */ - uint32_t dirBlock(void) const {return dirBlock_;} - uint8_t dirEntry(dir_t* dir); - /** \return Index of this file's directory in the block dirBlock. */ - uint8_t dirIndex(void) const {return dirIndex_;} - static void dirName(const dir_t& dir, char* name); - /** \return The total number of bytes in a file or directory. */ - uint32_t fileSize(void) const {return fileSize_;} - /** \return The first cluster number for a file or directory. */ - uint32_t firstCluster(void) const {return firstCluster_;} - /** \return True if this is a SdFile for a directory else false. */ - uint8_t isDir(void) const {return type_ >= FAT_FILE_TYPE_MIN_DIR;} - /** \return True if this is a SdFile for a file else false. */ - uint8_t isFile(void) const {return type_ == FAT_FILE_TYPE_NORMAL;} - /** \return True if this is a SdFile for an open file/directory else false. */ - uint8_t isOpen(void) const {return type_ != FAT_FILE_TYPE_CLOSED;} - /** \return True if this is a SdFile for a subdirectory else false. */ - uint8_t isSubDir(void) const {return type_ == FAT_FILE_TYPE_SUBDIR;} - /** \return True if this is a SdFile for the root directory. */ - uint8_t isRoot(void) const { - return type_ == FAT_FILE_TYPE_ROOT16 || type_ == FAT_FILE_TYPE_ROOT32; - } - void ls(uint8_t flags = 0, uint8_t indent = 0); - uint8_t makeDir(SdFile* dir, const char* dirName); - uint8_t open(SdFile* dirFile, uint16_t index, uint8_t oflag); - uint8_t open(SdFile* dirFile, const char* fileName, uint8_t oflag); + public: + /** Create an instance of SdFile. */ + SdFile(void) : type_(FAT_FILE_TYPE_CLOSED) {} + /** + writeError is set to true if an error occurs during a write(). + Set writeError to false before calling print() and/or write() and check + for true after calls to print() and/or write(). + */ + //bool writeError; + /** + Cancel unbuffered reads for this file. + See setUnbufferedRead() + */ + void clearUnbufferedRead(void) { + flags_ &= ~F_FILE_UNBUFFERED_READ; + } + uint8_t close(void); + uint8_t contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock); + uint8_t createContiguous(SdFile* dirFile, + const char* fileName, uint32_t size); + /** \return The current cluster number for a file or directory. */ + uint32_t curCluster(void) const { + return curCluster_; + } + /** \return The current position for a file or directory. */ + uint32_t curPosition(void) const { + return curPosition_; + } + /** + Set the date/time callback function - uint8_t openRoot(SdVolume* vol); - static void printDirName(const dir_t& dir, uint8_t width); - static void printFatDate(uint16_t fatDate); - static void printFatTime(uint16_t fatTime); - static void printTwoDigits(uint8_t v); - /** - * Read the next byte from a file. - * - * \return For success read returns the next byte in the file as an int. - * If an error occurs or end of file is reached -1 is returned. - */ - int16_t read(void) { - uint8_t b; - return read(&b, 1) == 1 ? b : -1; - } - int16_t read(void* buf, uint16_t nbyte); - int8_t readDir(dir_t* dir); - static uint8_t remove(SdFile* dirFile, const char* fileName); - uint8_t remove(void); - /** Set the file's current position to zero. */ - void rewind(void) { - curPosition_ = curCluster_ = 0; - } - uint8_t rmDir(void); - uint8_t rmRfStar(void); - /** Set the files position to current position + \a pos. See seekSet(). */ - uint8_t seekCur(uint32_t pos) { - return seekSet(curPosition_ + pos); - } - /** - * Set the files current position to end of file. Useful to position - * a file for append. See seekSet(). - */ - uint8_t seekEnd(void) {return seekSet(fileSize_);} - uint8_t seekSet(uint32_t pos); - /** - * Use unbuffered reads to access this file. Used with Wave - * Shield ISR. Used with Sd2Card::partialBlockRead() in WaveRP. - * - * Not recommended for normal applications. - */ - void setUnbufferedRead(void) { - if (isFile()) flags_ |= F_FILE_UNBUFFERED_READ; - } - uint8_t timestamp(uint8_t flag, uint16_t year, uint8_t month, uint8_t day, - uint8_t hour, uint8_t minute, uint8_t second); - uint8_t sync(void); - /** Type of this SdFile. You should use isFile() or isDir() instead of type() - * if possible. - * - * \return The file or directory type. - */ - uint8_t type(void) const {return type_;} - uint8_t truncate(uint32_t size); - /** \return Unbuffered read flag. */ - uint8_t unbufferedRead(void) const { - return flags_ & F_FILE_UNBUFFERED_READ; - } - /** \return SdVolume that contains this file. */ - SdVolume* volume(void) const {return vol_;} - size_t write(uint8_t b); - size_t write(const void* buf, uint16_t nbyte); - size_t write(const char* str); -#ifdef __AVR__ - void write_P(PGM_P str); - void writeln_P(PGM_P str); -#endif -//------------------------------------------------------------------------------ -#if ALLOW_DEPRECATED_FUNCTIONS -// Deprecated functions - suppress cpplint warnings with NOLINT comment - /** \deprecated Use: - * uint8_t SdFile::contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock); - */ - uint8_t contiguousRange(uint32_t& bgnBlock, uint32_t& endBlock) { // NOLINT - return contiguousRange(&bgnBlock, &endBlock); - } - /** \deprecated Use: - * uint8_t SdFile::createContiguous(SdFile* dirFile, - * const char* fileName, uint32_t size) - */ - uint8_t createContiguous(SdFile& dirFile, // NOLINT - const char* fileName, uint32_t size) { - return createContiguous(&dirFile, fileName, size); - } + \param[in] dateTime The user's call back function. The callback + function is of the form: - /** - * \deprecated Use: - * static void SdFile::dateTimeCallback( - * void (*dateTime)(uint16_t* date, uint16_t* time)); - */ - static void dateTimeCallback( - void (*dateTime)(uint16_t& date, uint16_t& time)) { // NOLINT - oldDateTime_ = dateTime; - dateTime_ = dateTime ? oldToNew : 0; - } - /** \deprecated Use: uint8_t SdFile::dirEntry(dir_t* dir); */ - uint8_t dirEntry(dir_t& dir) {return dirEntry(&dir);} // NOLINT - /** \deprecated Use: - * uint8_t SdFile::makeDir(SdFile* dir, const char* dirName); - */ - uint8_t makeDir(SdFile& dir, const char* dirName) { // NOLINT - return makeDir(&dir, dirName); - } - /** \deprecated Use: - * uint8_t SdFile::open(SdFile* dirFile, const char* fileName, uint8_t oflag); - */ - uint8_t open(SdFile& dirFile, // NOLINT - const char* fileName, uint8_t oflag) { - return open(&dirFile, fileName, oflag); - } - /** \deprecated Do not use in new apps */ - uint8_t open(SdFile& dirFile, const char* fileName) { // NOLINT - return open(dirFile, fileName, O_RDWR); - } - /** \deprecated Use: - * uint8_t SdFile::open(SdFile* dirFile, uint16_t index, uint8_t oflag); - */ - uint8_t open(SdFile& dirFile, uint16_t index, uint8_t oflag) { // NOLINT - return open(&dirFile, index, oflag); - } - /** \deprecated Use: uint8_t SdFile::openRoot(SdVolume* vol); */ - uint8_t openRoot(SdVolume& vol) {return openRoot(&vol);} // NOLINT + \code + void dateTime(uint16_t* date, uint16_t* time) { + uint16_t year; + uint8_t month, day, hour, minute, second; - /** \deprecated Use: int8_t SdFile::readDir(dir_t* dir); */ - int8_t readDir(dir_t& dir) {return readDir(&dir);} // NOLINT - /** \deprecated Use: - * static uint8_t SdFile::remove(SdFile* dirFile, const char* fileName); - */ - static uint8_t remove(SdFile& dirFile, const char* fileName) { // NOLINT - return remove(&dirFile, fileName); - } -//------------------------------------------------------------------------------ -// rest are private - private: - static void (*oldDateTime_)(uint16_t& date, uint16_t& time); // NOLINT - static void oldToNew(uint16_t* date, uint16_t* time) { - uint16_t d; - uint16_t t; - oldDateTime_(d, t); - *date = d; - *time = t; - } -#endif // ALLOW_DEPRECATED_FUNCTIONS - private: - // bits defined in flags_ - // should be 0XF - static uint8_t const F_OFLAG = (O_ACCMODE | O_APPEND | O_SYNC); - // available bits - static uint8_t const F_UNUSED = 0X30; - // use unbuffered SD read - static uint8_t const F_FILE_UNBUFFERED_READ = 0X40; - // sync of directory entry required - static uint8_t const F_FILE_DIR_DIRTY = 0X80; + // 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)) { + dateTime_ = dateTime; + } + /** + Cancel the date/time callback function. + */ + static void dateTimeCallbackCancel(void) { + // use explicit zero since NULL is not defined for Sanguino + dateTime_ = 0; + } + /** \return Address of the block that contains this file's directory. */ + uint32_t dirBlock(void) const { + return dirBlock_; + } + uint8_t dirEntry(dir_t* dir); + /** \return Index of this file's directory in the block dirBlock. */ + uint8_t dirIndex(void) const { + return dirIndex_; + } + static void dirName(const dir_t& dir, char* name); + /** \return The total number of bytes in a file or directory. */ + uint32_t fileSize(void) const { + return fileSize_; + } + /** \return The first cluster number for a file or directory. */ + uint32_t firstCluster(void) const { + return firstCluster_; + } + /** \return True if this is a SdFile for a directory else false. */ + uint8_t isDir(void) const { + return type_ >= FAT_FILE_TYPE_MIN_DIR; + } + /** \return True if this is a SdFile for a file else false. */ + uint8_t isFile(void) const { + return type_ == FAT_FILE_TYPE_NORMAL; + } + /** \return True if this is a SdFile for an open file/directory else false. */ + uint8_t isOpen(void) const { + return type_ != FAT_FILE_TYPE_CLOSED; + } + /** \return True if this is a SdFile for a subdirectory else false. */ + uint8_t isSubDir(void) const { + return type_ == FAT_FILE_TYPE_SUBDIR; + } + /** \return True if this is a SdFile for the root directory. */ + uint8_t isRoot(void) const { + return type_ == FAT_FILE_TYPE_ROOT16 || type_ == FAT_FILE_TYPE_ROOT32; + } + void ls(uint8_t flags = 0, uint8_t indent = 0); + uint8_t makeDir(SdFile* dir, const char* dirName); + uint8_t open(SdFile* dirFile, uint16_t index, uint8_t oflag); + uint8_t open(SdFile* dirFile, const char* fileName, uint8_t oflag); + + uint8_t openRoot(SdVolume* vol); + static void printDirName(const dir_t& dir, uint8_t width); + static void printFatDate(uint16_t fatDate); + static void printFatTime(uint16_t fatTime); + static void printTwoDigits(uint8_t v); + /** + Read the next byte from a file. -// make sure F_OFLAG is ok -#if ((F_UNUSED | F_FILE_UNBUFFERED_READ | F_FILE_DIR_DIRTY) & F_OFLAG) + \return For success read returns the next byte in the file as an int. + If an error occurs or end of file is reached -1 is returned. + */ + int16_t read(void) { + uint8_t b; + return read(&b, 1) == 1 ? b : -1; + } + int16_t read(void* buf, uint16_t nbyte); + int8_t readDir(dir_t* dir); + static uint8_t remove(SdFile* dirFile, const char* fileName); + uint8_t remove(void); + /** Set the file's current position to zero. */ + void rewind(void) { + curPosition_ = curCluster_ = 0; + } + uint8_t rmDir(void); + uint8_t rmRfStar(void); + /** Set the files position to current position + \a pos. See seekSet(). */ + uint8_t seekCur(uint32_t pos) { + return seekSet(curPosition_ + pos); + } + /** + Set the files current position to end of file. Useful to position + a file for append. See seekSet(). + */ + uint8_t seekEnd(void) { + return seekSet(fileSize_); + } + uint8_t seekSet(uint32_t pos); + /** + Use unbuffered reads to access this file. Used with Wave + Shield ISR. Used with Sd2Card::partialBlockRead() in WaveRP. + + Not recommended for normal applications. + */ + void setUnbufferedRead(void) { + if (isFile()) { + flags_ |= F_FILE_UNBUFFERED_READ; + } + } + uint8_t timestamp(uint8_t flag, uint16_t year, uint8_t month, uint8_t day, + uint8_t hour, uint8_t minute, uint8_t second); + uint8_t sync(void); + /** Type of this SdFile. You should use isFile() or isDir() instead of type() + if possible. + + \return The file or directory type. + */ + uint8_t type(void) const { + return type_; + } + uint8_t truncate(uint32_t size); + /** \return Unbuffered read flag. */ + uint8_t unbufferedRead(void) const { + return flags_ & F_FILE_UNBUFFERED_READ; + } + /** \return SdVolume that contains this file. */ + SdVolume* volume(void) const { + return vol_; + } + size_t write(uint8_t b); + size_t write(const void* buf, uint16_t nbyte); + size_t write(const char* str); + #ifdef __AVR__ + void write_P(PGM_P str); + void writeln_P(PGM_P str); + #endif + //------------------------------------------------------------------------------ + #if ALLOW_DEPRECATED_FUNCTIONS + // Deprecated functions - suppress cpplint warnings with NOLINT comment + /** \deprecated Use: + uint8_t SdFile::contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock); + */ + uint8_t contiguousRange(uint32_t& bgnBlock, uint32_t& endBlock) { // NOLINT + return contiguousRange(&bgnBlock, &endBlock); + } + /** \deprecated Use: + uint8_t SdFile::createContiguous(SdFile* dirFile, + const char* fileName, uint32_t size) + */ + uint8_t createContiguous(SdFile& dirFile, // NOLINT + const char* fileName, uint32_t size) { + return createContiguous(&dirFile, fileName, size); + } + + /** + \deprecated Use: + static void SdFile::dateTimeCallback( + void (*dateTime)(uint16_t* date, uint16_t* time)); + */ + static void dateTimeCallback( + void (*dateTime)(uint16_t& date, uint16_t& time)) { // NOLINT + oldDateTime_ = dateTime; + dateTime_ = dateTime ? oldToNew : 0; + } + /** \deprecated Use: uint8_t SdFile::dirEntry(dir_t* dir); */ + uint8_t dirEntry(dir_t& dir) { + return dirEntry(&dir); // NOLINT + } + /** \deprecated Use: + uint8_t SdFile::makeDir(SdFile* dir, const char* dirName); + */ + uint8_t makeDir(SdFile& dir, const char* dirName) { // NOLINT + return makeDir(&dir, dirName); + } + /** \deprecated Use: + uint8_t SdFile::open(SdFile* dirFile, const char* fileName, uint8_t oflag); + */ + uint8_t open(SdFile& dirFile, // NOLINT + const char* fileName, uint8_t oflag) { + return open(&dirFile, fileName, oflag); + } + /** \deprecated Do not use in new apps */ + uint8_t open(SdFile& dirFile, const char* fileName) { // NOLINT + return open(dirFile, fileName, O_RDWR); + } + /** \deprecated Use: + uint8_t SdFile::open(SdFile* dirFile, uint16_t index, uint8_t oflag); + */ + uint8_t open(SdFile& dirFile, uint16_t index, uint8_t oflag) { // NOLINT + return open(&dirFile, index, oflag); + } + /** \deprecated Use: uint8_t SdFile::openRoot(SdVolume* vol); */ + uint8_t openRoot(SdVolume& vol) { + return openRoot(&vol); // NOLINT + } + + /** \deprecated Use: int8_t SdFile::readDir(dir_t* dir); */ + int8_t readDir(dir_t& dir) { + return readDir(&dir); // NOLINT + } + /** \deprecated Use: + static uint8_t SdFile::remove(SdFile* dirFile, const char* fileName); + */ + static uint8_t remove(SdFile& dirFile, const char* fileName) { // NOLINT + return remove(&dirFile, fileName); + } + //------------------------------------------------------------------------------ + // rest are private + private: + static void (*oldDateTime_)(uint16_t& date, uint16_t& time); // NOLINT + static void oldToNew(uint16_t* date, uint16_t* time) { + uint16_t d; + uint16_t t; + oldDateTime_(d, t); + *date = d; + *time = t; + } + #endif // ALLOW_DEPRECATED_FUNCTIONS + private: + // bits defined in flags_ + // should be 0XF + static uint8_t const F_OFLAG = (O_ACCMODE | O_APPEND | O_SYNC); + // available bits + static uint8_t const F_UNUSED = 0X30; + // use unbuffered SD read + static uint8_t const F_FILE_UNBUFFERED_READ = 0X40; + // sync of directory entry required + static uint8_t const F_FILE_DIR_DIRTY = 0X80; + + // make sure F_OFLAG is ok + #if ((F_UNUSED | F_FILE_UNBUFFERED_READ | F_FILE_DIR_DIRTY) & F_OFLAG) #error flags_ bits conflict -#endif // flags_ bits + #endif // flags_ bits - // private data - uint8_t flags_; // See above for definition of flags_ bits - uint8_t type_; // type of file see above for values - uint32_t curCluster_; // cluster for current file position - uint32_t curPosition_; // current file position in bytes from beginning - uint32_t dirBlock_; // SD block that contains directory entry for file - uint8_t dirIndex_; // index of entry in dirBlock 0 <= dirIndex_ <= 0XF - uint32_t fileSize_; // file size in bytes - uint32_t firstCluster_; // first cluster of file - SdVolume* vol_; // volume where file is located + // private data + uint8_t flags_; // See above for definition of flags_ bits + uint8_t type_; // type of file see above for values + uint32_t curCluster_; // cluster for current file position + uint32_t curPosition_; // current file position in bytes from beginning + uint32_t dirBlock_; // SD block that contains directory entry for file + uint8_t dirIndex_; // index of entry in dirBlock 0 <= dirIndex_ <= 0XF + uint32_t fileSize_; // file size in bytes + uint32_t firstCluster_; // first cluster of file + SdVolume* vol_; // volume where file is located - // private functions - uint8_t addCluster(void); - uint8_t addDirCluster(void); - dir_t* cacheDirEntry(uint8_t action); - static void (*dateTime_)(uint16_t* date, uint16_t* time); - static uint8_t make83Name(const char* str, uint8_t* name); - uint8_t openCachedEntry(uint8_t cacheIndex, uint8_t oflags); - dir_t* readDirCache(void); + // private functions + uint8_t addCluster(void); + uint8_t addDirCluster(void); + dir_t* cacheDirEntry(uint8_t action); + static void (*dateTime_)(uint16_t* date, uint16_t* time); + static uint8_t make83Name(const char* str, uint8_t* name); + uint8_t openCachedEntry(uint8_t cacheIndex, uint8_t oflags); + dir_t* readDirCache(void); }; //============================================================================== // SdVolume class /** - * \brief Cache for an SD data block - */ + \brief Cache for an SD data block +*/ union cache_t { - /** Used to access cached file data blocks. */ + /** Used to access cached file data blocks. */ uint8_t data[512]; - /** Used to access cached FAT16 entries. */ + /** Used to access cached FAT16 entries. */ uint16_t fat16[256]; - /** Used to access cached FAT32 entries. */ + /** Used to access cached FAT32 entries. */ uint32_t fat32[128]; - /** Used to access cached directory entries. */ + /** Used to access cached directory entries. */ dir_t dir[16]; - /** Used to access a cached MasterBoot Record. */ + /** Used to access a cached MasterBoot Record. */ mbr_t mbr; - /** Used to access to a cached FAT boot sector. */ + /** Used to access to a cached FAT boot sector. */ fbs_t fbs; }; //------------------------------------------------------------------------------ /** - * \class SdVolume - * \brief Access FAT16 and FAT32 volumes on SD and SDHC cards. - */ + \class SdVolume + \brief Access FAT16 and FAT32 volumes on SD and SDHC cards. +*/ class SdVolume { - public: - /** Create an instance of SdVolume */ - SdVolume(void) :allocSearchStart_(2), fatType_(0) {} - /** Clear the cache and returns a pointer to the cache. Used by the WaveRP - * recorder to do raw write to the SD card. Not for normal apps. - */ - static uint8_t* cacheClear(void) { - cacheFlush(); - cacheBlockNumber_ = 0XFFFFFFFF; - return cacheBuffer_.data; - } - /** - * Initialize a FAT volume. Try partition one first then try super - * floppy format. - * - * \param[in] dev The Sd2Card where the volume is located. - * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. Reasons for - * failure include not finding a valid partition, not finding a valid - * FAT file system or an I/O error. - */ - uint8_t init(Sd2Card* dev) { return init(dev, 1) ? true : init(dev, 0);} - uint8_t init(Sd2Card* dev, uint8_t part); + public: + /** Create an instance of SdVolume */ + SdVolume(void) : allocSearchStart_(2), fatType_(0) {} + /** Clear the cache and returns a pointer to the cache. Used by the WaveRP + recorder to do raw write to the SD card. Not for normal apps. + */ + static uint8_t* cacheClear(void) { + cacheFlush(); + cacheBlockNumber_ = 0XFFFFFFFF; + return cacheBuffer_.data; + } + /** + Initialize a FAT volume. Try partition one first then try super + floppy format. - // inline functions that return volume info - /** \return The volume's cluster size in blocks. */ - uint8_t blocksPerCluster(void) const {return blocksPerCluster_;} - /** \return The number of blocks in one FAT. */ - uint32_t blocksPerFat(void) const {return blocksPerFat_;} - /** \return The total number of clusters in the volume. */ - uint32_t clusterCount(void) const {return clusterCount_;} - /** \return The shift count required to multiply by blocksPerCluster. */ - uint8_t clusterSizeShift(void) const {return clusterSizeShift_;} - /** \return The logical block number for the start of file data. */ - uint32_t dataStartBlock(void) const {return dataStartBlock_;} - /** \return The number of FAT structures on the volume. */ - uint8_t fatCount(void) const {return fatCount_;} - /** \return The logical block number for the start of the first FAT. */ - uint32_t fatStartBlock(void) const {return fatStartBlock_;} - /** \return The FAT type of the volume. Values are 12, 16 or 32. */ - uint8_t fatType(void) const {return fatType_;} - /** \return The number of entries in the root directory for FAT16 volumes. */ - uint32_t rootDirEntryCount(void) const {return 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(void) const {return rootDirStart_;} - /** return a pointer to the Sd2Card object for this volume */ - static Sd2Card* sdCard(void) {return sdCard_;} -//------------------------------------------------------------------------------ -#if ALLOW_DEPRECATED_FUNCTIONS - // Deprecated functions - suppress cpplint warnings with NOLINT comment - /** \deprecated Use: uint8_t SdVolume::init(Sd2Card* dev); */ - uint8_t init(Sd2Card& dev) {return init(&dev);} // NOLINT + \param[in] dev The Sd2Card where the volume is located. - /** \deprecated Use: uint8_t SdVolume::init(Sd2Card* dev, uint8_t vol); */ - uint8_t init(Sd2Card& dev, uint8_t part) { // NOLINT - return init(&dev, part); - } -#endif // ALLOW_DEPRECATED_FUNCTIONS -//------------------------------------------------------------------------------ + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. Reasons for + failure include not finding a valid partition, not finding a valid + FAT file system or an I/O error. + */ + uint8_t init(Sd2Card* dev) { + return init(dev, 1) ? true : init(dev, 0); + } + uint8_t init(Sd2Card* dev, uint8_t part); + + // inline functions that return volume info + /** \return The volume's cluster size in blocks. */ + uint8_t blocksPerCluster(void) const { + return blocksPerCluster_; + } + /** \return The number of blocks in one FAT. */ + uint32_t blocksPerFat(void) const { + return blocksPerFat_; + } + /** \return The total number of clusters in the volume. */ + uint32_t clusterCount(void) const { + return clusterCount_; + } + /** \return The shift count required to multiply by blocksPerCluster. */ + uint8_t clusterSizeShift(void) const { + return clusterSizeShift_; + } + /** \return The logical block number for the start of file data. */ + uint32_t dataStartBlock(void) const { + return dataStartBlock_; + } + /** \return The number of FAT structures on the volume. */ + uint8_t fatCount(void) const { + return fatCount_; + } + /** \return The logical block number for the start of the first FAT. */ + uint32_t fatStartBlock(void) const { + return fatStartBlock_; + } + /** \return The FAT type of the volume. Values are 12, 16 or 32. */ + uint8_t fatType(void) const { + return fatType_; + } + /** \return The number of entries in the root directory for FAT16 volumes. */ + uint32_t rootDirEntryCount(void) const { + return 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(void) const { + return rootDirStart_; + } + /** return a pointer to the Sd2Card object for this volume */ + static Sd2Card* sdCard(void) { + return sdCard_; + } + //------------------------------------------------------------------------------ + #if ALLOW_DEPRECATED_FUNCTIONS + // Deprecated functions - suppress cpplint warnings with NOLINT comment + /** \deprecated Use: uint8_t SdVolume::init(Sd2Card* dev); */ + uint8_t init(Sd2Card& dev) { + return init(&dev); // NOLINT + } + + /** \deprecated Use: uint8_t SdVolume::init(Sd2Card* dev, uint8_t vol); */ + uint8_t init(Sd2Card& dev, uint8_t part) { // NOLINT + return init(&dev, part); + } + #endif // ALLOW_DEPRECATED_FUNCTIONS + //------------------------------------------------------------------------------ private: - // Allow SdFile access to SdVolume private data. - friend class SdFile; + // Allow SdFile access to SdVolume private data. + friend class SdFile; - // value for action argument in cacheRawBlock to indicate read from cache - static uint8_t const CACHE_FOR_READ = 0; - // value for action argument in cacheRawBlock to indicate cache dirty - static uint8_t const CACHE_FOR_WRITE = 1; + // value for action argument in cacheRawBlock to indicate read from cache + static uint8_t const CACHE_FOR_READ = 0; + // value for action argument in cacheRawBlock to indicate cache dirty + static uint8_t const CACHE_FOR_WRITE = 1; - static cache_t cacheBuffer_; // 512 byte cache for device blocks - static uint32_t cacheBlockNumber_; // Logical number of block in the cache - static Sd2Card* sdCard_; // Sd2Card object for cache - static uint8_t cacheDirty_; // cacheFlush() will write block if true - static uint32_t cacheMirrorBlock_; // block number for mirror FAT -// - uint32_t allocSearchStart_; // start cluster for alloc search - uint8_t blocksPerCluster_; // cluster size in blocks - uint32_t blocksPerFat_; // FAT size in blocks - uint32_t clusterCount_; // clusters in one FAT - uint8_t clusterSizeShift_; // shift to convert cluster count to block count - uint32_t dataStartBlock_; // first data block number - uint8_t fatCount_; // number of FATs on volume - uint32_t fatStartBlock_; // start block for first FAT - uint8_t fatType_; // volume type (12, 16, OR 32) - uint16_t rootDirEntryCount_; // number of entries in FAT16 root dir - uint32_t rootDirStart_; // root start block for FAT16, cluster for FAT32 - //---------------------------------------------------------------------------- - uint8_t allocContiguous(uint32_t count, uint32_t* curCluster); - uint8_t blockOfCluster(uint32_t position) const { - return (position >> 9) & (blocksPerCluster_ - 1);} - uint32_t clusterStartBlock(uint32_t cluster) const { - return dataStartBlock_ + ((cluster - 2) << clusterSizeShift_);} - uint32_t blockNumber(uint32_t cluster, uint32_t position) const { - return clusterStartBlock(cluster) + blockOfCluster(position);} - static uint8_t cacheFlush(void); - static uint8_t cacheRawBlock(uint32_t blockNumber, uint8_t action); - static void cacheSetDirty(void) {cacheDirty_ |= CACHE_FOR_WRITE;} - static uint8_t cacheZeroBlock(uint32_t blockNumber); - uint8_t chainSize(uint32_t beginCluster, uint32_t* size) const; - uint8_t fatGet(uint32_t cluster, uint32_t* value) const; - uint8_t fatPut(uint32_t cluster, uint32_t value); - uint8_t fatPutEOC(uint32_t cluster) { - return fatPut(cluster, 0x0FFFFFFF); - } - uint8_t freeChain(uint32_t cluster); - uint8_t isEOC(uint32_t cluster) const { - return cluster >= (fatType_ == 16 ? FAT16EOC_MIN : FAT32EOC_MIN); - } - uint8_t readBlock(uint32_t block, uint8_t* dst) { - return sdCard_->readBlock(block, dst);} - uint8_t readData(uint32_t block, uint16_t offset, - uint16_t count, uint8_t* dst) { + static cache_t cacheBuffer_; // 512 byte cache for device blocks + static uint32_t cacheBlockNumber_; // Logical number of block in the cache + static Sd2Card* sdCard_; // Sd2Card object for cache + static uint8_t cacheDirty_; // cacheFlush() will write block if true + static uint32_t cacheMirrorBlock_; // block number for mirror FAT + // + uint32_t allocSearchStart_; // start cluster for alloc search + uint8_t blocksPerCluster_; // cluster size in blocks + uint32_t blocksPerFat_; // FAT size in blocks + uint32_t clusterCount_; // clusters in one FAT + uint8_t clusterSizeShift_; // shift to convert cluster count to block count + uint32_t dataStartBlock_; // first data block number + uint8_t fatCount_; // number of FATs on volume + uint32_t fatStartBlock_; // start block for first FAT + uint8_t fatType_; // volume type (12, 16, OR 32) + uint16_t rootDirEntryCount_; // number of entries in FAT16 root dir + uint32_t rootDirStart_; // root start block for FAT16, cluster for FAT32 + //---------------------------------------------------------------------------- + uint8_t allocContiguous(uint32_t count, uint32_t* curCluster); + uint8_t blockOfCluster(uint32_t position) const { + return (position >> 9) & (blocksPerCluster_ - 1); + } + uint32_t clusterStartBlock(uint32_t cluster) const { + return dataStartBlock_ + ((cluster - 2) << clusterSizeShift_); + } + uint32_t blockNumber(uint32_t cluster, uint32_t position) const { + return clusterStartBlock(cluster) + blockOfCluster(position); + } + static uint8_t cacheFlush(void); + static uint8_t cacheRawBlock(uint32_t blockNumber, uint8_t action); + static void cacheSetDirty(void) { + cacheDirty_ |= CACHE_FOR_WRITE; + } + static uint8_t cacheZeroBlock(uint32_t blockNumber); + uint8_t chainSize(uint32_t beginCluster, uint32_t* size) const; + uint8_t fatGet(uint32_t cluster, uint32_t* value) const; + uint8_t fatPut(uint32_t cluster, uint32_t value); + uint8_t fatPutEOC(uint32_t cluster) { + return fatPut(cluster, 0x0FFFFFFF); + } + uint8_t freeChain(uint32_t cluster); + uint8_t isEOC(uint32_t cluster) const { + return cluster >= (fatType_ == 16 ? FAT16EOC_MIN : FAT32EOC_MIN); + } + uint8_t readBlock(uint32_t block, uint8_t* dst) { + return sdCard_->readBlock(block, dst); + } + uint8_t readData(uint32_t block, uint16_t offset, + uint16_t count, uint8_t* dst) { return sdCard_->readData(block, offset, count, dst); - } - uint8_t writeBlock(uint32_t block, const uint8_t* dst) { - return sdCard_->writeBlock(block, dst); - } + } + uint8_t writeBlock(uint32_t block, const uint8_t* dst) { + return sdCard_->writeBlock(block, dst); + } }; #endif // SdFat_h diff --git a/src/utility/SdFatUtil.h b/src/utility/SdFatUtil.h index d1b4d53..4b93de8 100644 --- a/src/utility/SdFatUtil.h +++ b/src/utility/SdFatUtil.h @@ -1,36 +1,36 @@ /* Arduino SdFat Library - * Copyright (C) 2008 by William Greiman - * - * This file is part of the Arduino SdFat Library - * - * This Library is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This Library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + Copyright (C) 2008 by William Greiman - * You should have received a copy of the GNU General Public License - * along with the Arduino SdFat Library. If not, see - * . - */ + This file is part of the Arduino SdFat Library + + This Library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the Arduino SdFat Library. If not, see + . +*/ #ifndef SdFatUtil_h #define SdFatUtil_h /** - * \file - * Useful utility functions. - */ + \file + Useful utility functions. +*/ #include #ifdef __AVR__ -#include -/** Store and print a string in flash memory.*/ -#define PgmPrint(x) SerialPrint_P(PSTR(x)) -/** Store and print a string in flash memory followed by a CR/LF.*/ -#define PgmPrintln(x) SerialPrintln_P(PSTR(x)) -/** Defined so doxygen works for function definitions. */ + #include + /** Store and print a string in flash memory.*/ + #define PgmPrint(x) SerialPrint_P(PSTR(x)) + /** Store and print a string in flash memory followed by a CR/LF.*/ + #define PgmPrintln(x) SerialPrintln_P(PSTR(x)) + /** Defined so doxygen works for function definitions. */ #endif #define NOINLINE __attribute__((noinline,unused)) #define UNUSEDOK __attribute__((unused)) @@ -54,19 +54,21 @@ static UNUSEDOK int FreeRam(void) { #ifdef __AVR__ //------------------------------------------------------------------------------ /** - * %Print a string in flash memory to the serial port. - * - * \param[in] str Pointer to string stored in flash memory. - */ + %Print a string in flash memory to the serial port. + + \param[in] str Pointer to string stored in flash memory. +*/ static NOINLINE void SerialPrint_P(PGM_P str) { - for (uint8_t c; (c = pgm_read_byte(str)); str++) Serial.write(c); + for (uint8_t c; (c = pgm_read_byte(str)); str++) { + Serial.write(c); + } } //------------------------------------------------------------------------------ /** - * %Print a string in flash memory followed by a CR/LF. - * - * \param[in] str Pointer to string stored in flash memory. - */ + %Print a string in flash memory followed by a CR/LF. + + \param[in] str Pointer to string stored in flash memory. +*/ static NOINLINE void SerialPrintln_P(PGM_P str) { SerialPrint_P(str); Serial.println(); diff --git a/src/utility/SdFatmainpage.h b/src/utility/SdFatmainpage.h index 73b3b63..1e6d96f 100644 --- a/src/utility/SdFatmainpage.h +++ b/src/utility/SdFatmainpage.h @@ -1,202 +1,202 @@ /* Arduino SdFat Library - * Copyright (C) 2009 by William Greiman - * - * This file is part of the Arduino SdFat Library - * - * This Library is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This Library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with the Arduino SdFat Library. If not, see - * . - */ + Copyright (C) 2009 by William Greiman + + This file is part of the Arduino SdFat Library + + This Library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the Arduino SdFat Library. If not, see + . +*/ /** -\mainpage Arduino SdFat Library -
Copyright © 2009 by William Greiman -
+ \mainpage Arduino SdFat Library +
Copyright © 2009 by William Greiman +
-\section Intro Introduction -The Arduino SdFat Library is a minimal implementation of FAT16 and FAT32 -file systems on SD flash memory cards. Standard SD and high capacity -SDHC cards are supported. + \section Intro Introduction + The Arduino SdFat Library is a minimal implementation of FAT16 and FAT32 + file systems on SD flash memory cards. Standard SD and high capacity + SDHC cards are supported. -The SdFat only supports short 8.3 names. + The SdFat only supports short 8.3 names. -The main classes in SdFat are Sd2Card, SdVolume, and SdFile. + The main classes in SdFat are Sd2Card, SdVolume, and SdFile. -The Sd2Card class supports access to standard SD cards and SDHC cards. Most -applications will only need to call the Sd2Card::init() member function. + The Sd2Card class supports access to standard SD cards and SDHC cards. Most + applications will only need to call the Sd2Card::init() member function. -The SdVolume class supports FAT16 and FAT32 partitions. Most applications -will only need to call the SdVolume::init() member function. + The SdVolume class supports FAT16 and FAT32 partitions. Most applications + will only need to call the SdVolume::init() member function. -The SdFile class provides file access functions such as open(), read(), -remove(), write(), close() and sync(). This class supports access to the root -directory and subdirectories. + The SdFile class provides file access functions such as open(), read(), + remove(), write(), close() and sync(). This class supports access to the root + directory and subdirectories. -A number of example are provided in the SdFat/examples folder. These were -developed to test SdFat and illustrate its use. + A number of example are provided in the SdFat/examples folder. These were + developed to test SdFat and illustrate its use. -SdFat was developed for high speed data recording. SdFat was used to implement -an audio record/play class, WaveRP, for the Adafruit Wave Shield. This -application uses special Sd2Card calls to write to contiguous files in raw mode. -These functions reduce write latency so that audio can be recorded with the -small amount of RAM in the Arduino. + SdFat was developed for high speed data recording. SdFat was used to implement + an audio record/play class, WaveRP, for the Adafruit Wave Shield. This + application uses special Sd2Card calls to write to contiguous files in raw mode. + These functions reduce write latency so that audio can be recorded with the + small amount of RAM in the Arduino. -\section SDcard SD\SDHC Cards + \section SDcard SD\SDHC Cards -Arduinos access SD cards using the cards SPI protocol. PCs, Macs, and -most consumer devices use the 4-bit parallel SD protocol. A card that -functions well on A PC or Mac may not work well on the Arduino. + Arduinos access SD cards using the cards SPI protocol. PCs, Macs, and + most consumer devices use the 4-bit parallel SD protocol. A card that + functions well on A PC or Mac may not work well on the Arduino. -Most cards have good SPI read performance but cards vary widely in SPI -write performance. Write performance is limited by how efficiently the -card manages internal erase/remapping operations. The Arduino cannot -optimize writes to reduce erase operations because of its limit RAM. + Most cards have good SPI read performance but cards vary widely in SPI + write performance. Write performance is limited by how efficiently the + card manages internal erase/remapping operations. The Arduino cannot + optimize writes to reduce erase operations because of its limit RAM. -SanDisk cards generally have good write performance. They seem to have -more internal RAM buffering than other cards and therefore can limit -the number of flash erase operations that the Arduino forces due to its -limited RAM. + SanDisk cards generally have good write performance. They seem to have + more internal RAM buffering than other cards and therefore can limit + the number of flash erase operations that the Arduino forces due to its + limited RAM. -\section Hardware Hardware Configuration + \section Hardware Hardware Configuration -SdFat was developed using an - Adafruit Industries - Wave Shield. + SdFat was developed using an + Adafruit Industries + Wave Shield. -The hardware interface to the SD card should not use a resistor based level -shifter. SdFat sets the SPI bus frequency to 8 MHz which results in signal -rise times that are too slow for the edge detectors in many newer SD card -controllers when resistor voltage dividers are used. + The hardware interface to the SD card should not use a resistor based level + shifter. SdFat sets the SPI bus frequency to 8 MHz which results in signal + rise times that are too slow for the edge detectors in many newer SD card + controllers when resistor voltage dividers are used. -The 5 to 3.3 V level shifter for 5 V Arduinos should be IC based like the -74HC4050N based circuit shown in the file SdLevel.png. The Adafruit Wave Shield -uses a 74AHC125N. Gravitech sells SD and MicroSD Card Adapters based on the -74LCX245. + The 5 to 3.3 V level shifter for 5 V Arduinos should be IC based like the + 74HC4050N based circuit shown in the file SdLevel.png. The Adafruit Wave Shield + uses a 74AHC125N. Gravitech sells SD and MicroSD Card Adapters based on the + 74LCX245. -If you are using a resistor based level shifter and are having problems try -setting the SPI bus frequency to 4 MHz. This can be done by using -card.init(SPI_HALF_SPEED) to initialize the SD card. + If you are using a resistor based level shifter and are having problems try + setting the SPI bus frequency to 4 MHz. This can be done by using + card.init(SPI_HALF_SPEED) to initialize the SD card. -\section comment Bugs and Comments + \section comment Bugs and Comments -If you wish to report bugs or have comments, send email to fat16lib@sbcglobal.net. + If you wish to report bugs or have comments, send email to fat16lib@sbcglobal.net. -\section SdFatClass SdFat Usage + \section SdFatClass SdFat Usage -SdFat uses a slightly restricted form of short names. -Only printable ASCII characters are supported. No characters with code point -values greater than 127 are allowed. Space is not allowed even though space -was allowed in the API of early versions of DOS. + SdFat uses a slightly restricted form of short names. + Only printable ASCII characters are supported. No characters with code point + values greater than 127 are allowed. Space is not allowed even though space + was allowed in the API of early versions of DOS. -Short names are limited to 8 characters followed by an optional period (.) -and extension of up to 3 characters. The characters may be any combination -of letters and digits. The following special characters are also allowed: + Short names are limited to 8 characters followed by an optional period (.) + and extension of up to 3 characters. The characters may be any combination + of letters and digits. The following special characters are also allowed: -$ % ' - _ @ ~ ` ! ( ) { } ^ # & + $ % ' - _ @ ~ ` ! ( ) { } ^ # & -Short names are always converted to upper case and their original case -value is lost. + Short names are always converted to upper case and their original case + value is lost. -\note + \note The Arduino Print class uses character -at a time writes so it was necessary to use a \link SdFile::sync() sync() \endlink -function to control when data is written to the SD card. + at a time writes so it was necessary to use a \link SdFile::sync() sync() \endlink + function to control when data is written to the SD card. + + \par + An application which writes to a file using \link Print::print() print()\endlink, + \link Print::println() println() \endlink + or \link SdFile::write write() \endlink must call \link SdFile::sync() sync() \endlink + at the appropriate time to force data and directory information to be written + to the SD Card. Data and directory information are also written to the SD card + when \link SdFile::close() close() \endlink is called. -\par -An application which writes to a file using \link Print::print() print()\endlink, -\link Print::println() println() \endlink -or \link SdFile::write write() \endlink must call \link SdFile::sync() sync() \endlink -at the appropriate time to force data and directory information to be written -to the SD Card. Data and directory information are also written to the SD card -when \link SdFile::close() close() \endlink is called. + \par + Applications must use care calling \link SdFile::sync() sync() \endlink + since 2048 bytes of I/O is required to update file and + directory information. This includes writing the current data block, reading + the block that contains the directory entry for update, writing the directory + block back and reading back the current data block. -\par -Applications must use care calling \link SdFile::sync() sync() \endlink -since 2048 bytes of I/O is required to update file and -directory information. This includes writing the current data block, reading -the block that contains the directory entry for update, writing the directory -block back and reading back the current data block. + It is possible to open a file with two or more instances of SdFile. A file may + be corrupted if data is written to the file by more than one instance of SdFile. -It is possible to open a file with two or more instances of SdFile. A file may -be corrupted if data is written to the file by more than one instance of SdFile. + \section HowTo How to format SD Cards as FAT Volumes -\section HowTo How to format SD Cards as FAT Volumes + You should use a freshly formatted SD card for best performance. FAT + file systems become slower if many files have been created and deleted. + This is because the directory entry for a deleted file is marked as deleted, + but is not deleted. When a new file is created, these entries must be scanned + before creating the file, a flaw in the FAT design. Also files can become + fragmented which causes reads and writes to be slower. -You should use a freshly formatted SD card for best performance. FAT -file systems become slower if many files have been created and deleted. -This is because the directory entry for a deleted file is marked as deleted, -but is not deleted. When a new file is created, these entries must be scanned -before creating the file, a flaw in the FAT design. Also files can become -fragmented which causes reads and writes to be slower. + Microsoft operating systems support removable media formatted with a + Master Boot Record, MBR, or formatted as a super floppy with a FAT Boot Sector + in block zero. -Microsoft operating systems support removable media formatted with a -Master Boot Record, MBR, or formatted as a super floppy with a FAT Boot Sector -in block zero. + Microsoft operating systems expect MBR formatted removable media + to have only one partition. The first partition should be used. -Microsoft operating systems expect MBR formatted removable media -to have only one partition. The first partition should be used. + Microsoft operating systems do not support partitioning SD flash cards. + If you erase an SD card with a program like KillDisk, Most versions of + Windows will format the card as a super floppy. -Microsoft operating systems do not support partitioning SD flash cards. -If you erase an SD card with a program like KillDisk, Most versions of -Windows will format the card as a super floppy. + The best way to restore an SD card's format is to use SDFormatter + which can be downloaded from: -The best way to restore an SD card's format is to use SDFormatter -which can be downloaded from: + http://www.sdcard.org/consumers/formatter/ -http://www.sdcard.org/consumers/formatter/ + SDFormatter aligns flash erase boundaries with file + system structures which reduces write latency and file system overhead. -SDFormatter aligns flash erase boundaries with file -system structures which reduces write latency and file system overhead. + SDFormatter does not have an option for FAT type so it may format + small cards as FAT12. -SDFormatter does not have an option for FAT type so it may format -small cards as FAT12. + After the MBR is restored by SDFormatter you may need to reformat small + cards that have been formatted FAT12 to force the volume type to be FAT16. -After the MBR is restored by SDFormatter you may need to reformat small -cards that have been formatted FAT12 to force the volume type to be FAT16. + If you reformat the SD card with an OS utility, choose a cluster size that + will result in: -If you reformat the SD card with an OS utility, choose a cluster size that -will result in: + 4084 < CountOfClusters && CountOfClusters < 65525 -4084 < CountOfClusters && CountOfClusters < 65525 + The volume will then be FAT16. -The volume will then be FAT16. + If you are formatting an SD card on OS X or Linux, be sure to use the first + partition. Format this partition with a cluster count in above range. -If you are formatting an SD card on OS X or Linux, be sure to use the first -partition. Format this partition with a cluster count in above range. + \section References References -\section References References + Adafruit Industries: -Adafruit Industries: + http://www.adafruit.com/ -http://www.adafruit.com/ + http://www.ladyada.net/make/waveshield/ -http://www.ladyada.net/make/waveshield/ + The Arduino site: -The Arduino site: + http://www.arduino.cc/ -http://www.arduino.cc/ + For more information about FAT file systems see: -For more information about FAT file systems see: + http://www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx -http://www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx + For information about using SD cards as SPI devices see: -For information about using SD cards as SPI devices see: + http://www.sdcard.org/developers/tech/sdcard/pls/Simplified_Physical_Layer_Spec.pdf -http://www.sdcard.org/developers/tech/sdcard/pls/Simplified_Physical_Layer_Spec.pdf + The ATmega328 datasheet: -The ATmega328 datasheet: + http://www.atmel.com/dyn/resources/prod_documents/doc8161.pdf -http://www.atmel.com/dyn/resources/prod_documents/doc8161.pdf - - */ +*/ diff --git a/src/utility/SdFile.cpp b/src/utility/SdFile.cpp index 620fc0d..cac47d4 100644 --- a/src/utility/SdFile.cpp +++ b/src/utility/SdFile.cpp @@ -1,25 +1,25 @@ /* Arduino SdFat Library - * Copyright (C) 2009 by William Greiman - * - * This file is part of the Arduino SdFat Library - * - * This Library is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This Library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with the Arduino SdFat Library. If not, see - * . - */ + Copyright (C) 2009 by William Greiman + + This file is part of the Arduino SdFat Library + + This Library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the Arduino SdFat Library. If not, see + . +*/ #include "SdFat.h" #ifdef __AVR__ -#include + #include #endif #include //------------------------------------------------------------------------------ @@ -27,13 +27,15 @@ void (*SdFile::dateTime_)(uint16_t* date, uint16_t* time) = NULL; #if ALLOW_DEPRECATED_FUNCTIONS -// suppress cpplint warnings with NOLINT comment -void (*SdFile::oldDateTime_)(uint16_t& date, uint16_t& time) = NULL; // NOLINT + // suppress cpplint warnings with NOLINT comment + void (*SdFile::oldDateTime_)(uint16_t& date, uint16_t& time) = NULL; // NOLINT #endif // ALLOW_DEPRECATED_FUNCTIONS //------------------------------------------------------------------------------ // add a cluster to a file uint8_t SdFile::addCluster() { - if (!vol_->allocContiguous(1, &curCluster_)) return false; + if (!vol_->allocContiguous(1, &curCluster_)) { + return false; + } // if first cluster of file link to directory entry if (firstCluster_ == 0) { @@ -46,12 +48,16 @@ uint8_t SdFile::addCluster() { // Add a cluster to a directory file and zero the cluster. // return with first block of cluster in the cache uint8_t SdFile::addDirCluster(void) { - if (!addCluster()) return false; + if (!addCluster()) { + return false; + } // zero data in cluster insure first cluster is in cache uint32_t block = vol_->clusterStartBlock(curCluster_); for (uint8_t i = vol_->blocksPerCluster_; i != 0; i--) { - if (!SdVolume::cacheZeroBlock(block + i - 1)) return false; + if (!SdVolume::cacheZeroBlock(block + i - 1)) { + return false; + } } // Increase directory file size by cluster size fileSize_ += 512UL << vol_->clusterSizeShift_; @@ -61,47 +67,57 @@ uint8_t SdFile::addDirCluster(void) { // cache a file's directory entry // return pointer to cached entry or null for failure dir_t* SdFile::cacheDirEntry(uint8_t action) { - if (!SdVolume::cacheRawBlock(dirBlock_, action)) return NULL; + if (!SdVolume::cacheRawBlock(dirBlock_, action)) { + return NULL; + } return SdVolume::cacheBuffer_.dir + dirIndex_; } //------------------------------------------------------------------------------ /** - * Close a file and force cached data and directory information - * to be written to the storage device. - * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. - * Reasons for failure include no file is open or an I/O error. - */ + Close a file and force cached data and directory information + to be written to the storage device. + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. + Reasons for failure include no file is open or an I/O error. +*/ uint8_t SdFile::close(void) { - if (!sync())return false; + if (!sync()) { + return false; + } type_ = FAT_FILE_TYPE_CLOSED; return true; } //------------------------------------------------------------------------------ /** - * Check for contiguous file and return its raw block range. - * - * \param[out] bgnBlock the first block address for the file. - * \param[out] endBlock the last block address for the file. - * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. - * Reasons for failure include file is not contiguous, file has zero length - * or an I/O error occurred. - */ + Check for contiguous file and return its raw block range. + + \param[out] bgnBlock the first block address for the file. + \param[out] endBlock the last block address for the file. + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. + Reasons for failure include file is not contiguous, file has zero length + or an I/O error occurred. +*/ uint8_t SdFile::contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock) { // error if no blocks - if (firstCluster_ == 0) return false; + if (firstCluster_ == 0) { + return false; + } for (uint32_t c = firstCluster_; ; c++) { uint32_t next; - if (!vol_->fatGet(c, &next)) return false; + if (!vol_->fatGet(c, &next)) { + return false; + } // check for contiguous if (next != (c + 1)) { // error if not end of chain - if (!vol_->isEOC(next)) return false; + if (!vol_->isEOC(next)) { + return false; + } *bgnBlock = vol_->clusterStartBlock(firstCluster_); *endBlock = vol_->clusterStartBlock(c) + vol_->blocksPerCluster_ - 1; @@ -111,28 +127,32 @@ uint8_t SdFile::contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock) { } //------------------------------------------------------------------------------ /** - * Create and open a new contiguous file of a specified size. - * - * \note This function only supports short DOS 8.3 names. - * See open() for more information. - * - * \param[in] dirFile The directory where the file will be created. - * \param[in] fileName A valid DOS 8.3 file name. - * \param[in] size The desired file size. - * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. - * Reasons for failure include \a fileName contains - * an invalid DOS 8.3 file name, the FAT volume has not been initialized, - * a file is already open, the file already exists, the root - * directory is full or an I/O error. - * - */ + Create and open a new contiguous file of a specified size. + + \note This function only supports short DOS 8.3 names. + See open() for more information. + + \param[in] dirFile The directory where the file will be created. + \param[in] fileName A valid DOS 8.3 file name. + \param[in] size The desired file size. + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. + Reasons for failure include \a fileName contains + an invalid DOS 8.3 file name, the FAT volume has not been initialized, + a file is already open, the file already exists, the root + directory is full or an I/O error. + +*/ uint8_t SdFile::createContiguous(SdFile* dirFile, - const char* fileName, uint32_t size) { + const char* fileName, uint32_t size) { // don't allow zero length file - if (size == 0) return false; - if (!open(dirFile, fileName, O_CREAT | O_EXCL | O_RDWR)) return false; + if (size == 0) { + return false; + } + if (!open(dirFile, fileName, O_CREAT | O_EXCL | O_RDWR)) { + return false; + } // calculate number of clusters needed uint32_t count = ((size - 1) >> (vol_->clusterSizeShift_ + 9)) + 1; @@ -150,20 +170,24 @@ uint8_t SdFile::createContiguous(SdFile* dirFile, } //------------------------------------------------------------------------------ /** - * Return a files directory entry - * - * \param[out] dir Location for return of the files directory entry. - * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. - */ + Return a files directory entry + + \param[out] dir Location for return of the files directory entry. + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. +*/ uint8_t SdFile::dirEntry(dir_t* dir) { // make sure fields on SD are correct - if (!sync()) return false; + if (!sync()) { + return false; + } // read entry dir_t* p = cacheDirEntry(SdVolume::CACHE_FOR_READ); - if (!p) return false; + if (!p) { + return false; + } // copy to caller's struct memcpy(dir, p, sizeof(dir_t)); @@ -171,60 +195,72 @@ uint8_t SdFile::dirEntry(dir_t* dir) { } //------------------------------------------------------------------------------ /** - * 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. - */ + Format the name field of \a dir into the 13 byte array + \a name in standard 8.3 short name format. + + \param[in] dir The directory structure containing the name. + \param[out] name A 13 byte char array for the formatted name. +*/ void SdFile::dirName(const dir_t& dir, char* name) { uint8_t j = 0; for (uint8_t i = 0; i < 11; i++) { - if (dir.name[i] == ' ')continue; - if (i == 8) name[j++] = '.'; + if (dir.name[i] == ' ') { + continue; + } + if (i == 8) { + name[j++] = '.'; + } name[j++] = dir.name[i]; } name[j] = 0; } //------------------------------------------------------------------------------ /** List directory contents 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. - * - * \param[in] indent Amount of space before file name. Used for recursive - * list to indicate subdirectory level. - */ + + \param[in] flags The inclusive OR of + + LS_DATE - %Print file modification date + + LS_SIZE - %Print file size. + + LS_R - Recursive list of subdirectories. + + \param[in] indent Amount of space before file name. Used for recursive + list to indicate subdirectory level. +*/ void SdFile::ls(uint8_t flags, uint8_t indent) { dir_t* p; rewind(); while ((p = readDirCache())) { // done if past last used entry - if (p->name[0] == DIR_NAME_FREE) break; + if (p->name[0] == DIR_NAME_FREE) { + break; + } // skip deleted entry and entries for . and .. - if (p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') continue; + if (p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') { + continue; + } // only list subdirectories and files - if (!DIR_IS_FILE_OR_SUBDIR(p)) continue; + if (!DIR_IS_FILE_OR_SUBDIR(p)) { + continue; + } // print any indent spaces - for (int8_t i = 0; i < indent; i++) Serial.print(' '); + for (int8_t i = 0; i < indent; i++) { + Serial.print(' '); + } // print file name with possible blank fill printDirName(*p, flags & (LS_DATE | LS_SIZE) ? 14 : 0); // print modify date/time if requested if (flags & LS_DATE) { - printFatDate(p->lastWriteDate); - Serial.print(' '); - printFatTime(p->lastWriteTime); + printFatDate(p->lastWriteDate); + Serial.print(' '); + printFatTime(p->lastWriteTime); } // print size if requested if (!DIR_IS_SUBDIR(p) && (flags & LS_SIZE)) { @@ -235,9 +271,11 @@ void SdFile::ls(uint8_t flags, uint8_t indent) { // list subdirectory content if requested if ((flags & LS_R) && DIR_IS_SUBDIR(p)) { - uint16_t index = curPosition()/32 - 1; + uint16_t index = curPosition() / 32 - 1; SdFile s; - if (s.open(this, index, O_READ)) s.ls(flags, indent + 2); + if (s.open(this, index, O_READ)) { + s.ls(flags, indent + 2); + } seekSet(32 * (index + 1)); } } @@ -249,26 +287,36 @@ uint8_t SdFile::make83Name(const char* str, uint8_t* name) { uint8_t n = 7; // max index for part before dot uint8_t i = 0; // blank fill name and extension - while (i < 11) name[i++] = ' '; + while (i < 11) { + name[i++] = ' '; + } i = 0; while ((c = *str++) != '\0') { if (c == '.') { - if (n == 10) return false; // only one dot allowed + if (n == 10) { + return false; // only one dot allowed + } n = 10; // max index for full 8.3 name i = 8; // place for extension } else { // illegal FAT characters uint8_t b; -#if defined(__AVR__) + #if defined(__AVR__) PGM_P p = PSTR("|<>^+=?/[];,*\"\\"); - while ((b = pgm_read_byte(p++))) if (b == c) return false; -#elif defined(__arm__) + while ((b = pgm_read_byte(p++))) if (b == c) { + return false; + } + #elif defined(__arm__) const uint8_t valid[] = "|<>^+=?/[];,*\"\\"; const uint8_t *p = valid; - while ((b = *p++)) if (b == c) return false; -#endif + while ((b = *p++)) if (b == c) { + return false; + } + #endif // check size and only allow ASCII printable characters - if (i > n || c < 0X21 || c > 0X7E)return false; + if (i > n || c < 0X21 || c > 0X7E) { + return false; + } // only upper case allowed in 8.3 names - convert lower to upper name[i++] = c < 'a' || c > 'z' ? c : c + ('A' - 'a'); } @@ -278,48 +326,60 @@ uint8_t SdFile::make83Name(const char* str, uint8_t* name) { } //------------------------------------------------------------------------------ /** Make a new directory. - * - * \param[in] dir An open SdFat instance for the directory that will containing - * the new directory. - * - * \param[in] dirName A valid 8.3 DOS name for the new directory. - * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. - * Reasons for failure include this SdFile is already open, \a dir is not a - * directory, \a dirName is invalid or already exists in \a dir. - */ + + \param[in] dir An open SdFat instance for the directory that will containing + the new directory. + + \param[in] dirName A valid 8.3 DOS name for the new directory. + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. + Reasons for failure include this SdFile is already open, \a dir is not a + directory, \a dirName is invalid or already exists in \a dir. +*/ uint8_t SdFile::makeDir(SdFile* dir, const char* dirName) { dir_t d; // create a normal file - if (!open(dir, dirName, O_CREAT | O_EXCL | O_RDWR)) return false; + if (!open(dir, dirName, O_CREAT | O_EXCL | O_RDWR)) { + return false; + } // convert SdFile to directory flags_ = O_READ; type_ = FAT_FILE_TYPE_SUBDIR; // allocate and zero first cluster - if (!addDirCluster())return false; + if (!addDirCluster()) { + return false; + } // force entry to SD - if (!sync()) return false; + if (!sync()) { + return false; + } // cache entry - should already be in cache due to sync() call dir_t* p = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); - if (!p) return false; + if (!p) { + return false; + } // change directory entry attribute p->attributes = DIR_ATT_DIRECTORY; // make entry for '.' memcpy(&d, p, sizeof(d)); - for (uint8_t i = 1; i < 11; i++) d.name[i] = ' '; + for (uint8_t i = 1; i < 11; i++) { + d.name[i] = ' '; + } d.name[0] = '.'; // cache block for '.' and '..' uint32_t block = vol_->clusterStartBlock(firstCluster_); - if (!SdVolume::cacheRawBlock(block, SdVolume::CACHE_FOR_WRITE)) return false; + if (!SdVolume::cacheRawBlock(block, SdVolume::CACHE_FOR_WRITE)) { + return false; + } // copy '.' to block memcpy(&SdVolume::cacheBuffer_.dir[0], &d, sizeof(d)); @@ -344,59 +404,63 @@ uint8_t SdFile::makeDir(SdFile* dir, const char* dirName) { } //------------------------------------------------------------------------------ /** - * Open a file or directory by name. - * - * \param[in] dirFile An open SdFat instance for the directory containing the - * file to be opened. - * - * \param[in] fileName A valid 8.3 DOS name for a file to be opened. - * - * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive - * OR of flags from the following list - * - * O_READ - Open for reading. - * - * O_RDONLY - Same as O_READ. - * - * O_WRITE - Open for writing. - * - * O_WRONLY - Same as O_WRITE. - * - * O_RDWR - Open for reading and writing. - * - * O_APPEND - If set, the file offset shall be set to the end of the - * file prior to each write. - * - * O_CREAT - If the file exists, this flag has no effect except as noted - * under O_EXCL below. Otherwise, the file shall be created - * - * O_EXCL - If O_CREAT and O_EXCL are set, open() shall fail if the file exists. - * - * O_SYNC - Call sync() after each write. This flag should not be used with - * write(uint8_t), write_P(PGM_P), writeln_P(PGM_P), or the Arduino Print class. - * These functions do character at a time writes so sync() will be called - * after each byte. - * - * O_TRUNC - If the file exists and is a regular file, and the file is - * successfully opened and is not read only, its length shall be truncated to 0. - * - * \note Directory files must be opened read only. Write and truncation is - * not allowed for directory files. - * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. - * Reasons for failure include this SdFile is already open, \a difFile is not - * a directory, \a fileName is invalid, the file does not exist - * or can't be opened in the access mode specified by oflag. - */ + Open a file or directory by name. + + \param[in] dirFile An open SdFat instance for the directory containing the + file to be opened. + + \param[in] fileName A valid 8.3 DOS name for a file to be opened. + + \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive + OR of flags from the following list + + O_READ - Open for reading. + + O_RDONLY - Same as O_READ. + + O_WRITE - Open for writing. + + O_WRONLY - Same as O_WRITE. + + O_RDWR - Open for reading and writing. + + O_APPEND - If set, the file offset shall be set to the end of the + file prior to each write. + + O_CREAT - If the file exists, this flag has no effect except as noted + under O_EXCL below. Otherwise, the file shall be created + + O_EXCL - If O_CREAT and O_EXCL are set, open() shall fail if the file exists. + + O_SYNC - Call sync() after each write. This flag should not be used with + write(uint8_t), write_P(PGM_P), writeln_P(PGM_P), or the Arduino Print class. + These functions do character at a time writes so sync() will be called + after each byte. + + O_TRUNC - If the file exists and is a regular file, and the file is + successfully opened and is not read only, its length shall be truncated to 0. + + \note Directory files must be opened read only. Write and truncation is + not allowed for directory files. + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. + Reasons for failure include this SdFile is already open, \a difFile is not + a directory, \a fileName is invalid, the file does not exist + or can't be opened in the access mode specified by oflag. +*/ uint8_t SdFile::open(SdFile* dirFile, const char* fileName, uint8_t oflag) { uint8_t dname[11]; dir_t* p; // error if already open - if (isOpen())return false; + if (isOpen()) { + return false; + } - if (!make83Name(fileName, dname)) return false; + if (!make83Name(fileName, dname)) { + return false; + } vol_ = dirFile->vol_; dirFile->rewind(); @@ -407,7 +471,9 @@ uint8_t SdFile::open(SdFile* dirFile, const char* fileName, uint8_t oflag) { while (dirFile->curPosition_ < dirFile->fileSize_) { uint8_t index = 0XF & (dirFile->curPosition_ >> 5); p = dirFile->readDirCache(); - if (p == NULL) return false; + if (p == NULL) { + return false; + } if (p->name[0] == DIR_NAME_FREE || p->name[0] == DIR_NAME_DELETED) { // remember first empty slot @@ -417,27 +483,39 @@ uint8_t SdFile::open(SdFile* dirFile, const char* fileName, uint8_t oflag) { dirBlock_ = SdVolume::cacheBlockNumber_; } // done if no entries follow - if (p->name[0] == DIR_NAME_FREE) break; + if (p->name[0] == DIR_NAME_FREE) { + break; + } } else if (!memcmp(dname, p->name, 11)) { // don't open existing file if O_CREAT and O_EXCL - if ((oflag & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) return false; + if ((oflag & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) { + return false; + } // open found file return openCachedEntry(0XF & index, oflag); } } // only create file if O_CREAT and O_WRITE - if ((oflag & (O_CREAT | O_WRITE)) != (O_CREAT | O_WRITE)) return false; + if ((oflag & (O_CREAT | O_WRITE)) != (O_CREAT | O_WRITE)) { + return false; + } // cache found slot or add cluster if end of file if (emptyFound) { p = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); - if (!p) return false; + if (!p) { + return false; + } } else { - if (dirFile->type_ == FAT_FILE_TYPE_ROOT16) return false; + if (dirFile->type_ == FAT_FILE_TYPE_ROOT16) { + return false; + } // add and zero cluster for dirFile - first cluster is in cache for write - if (!dirFile->addDirCluster()) return false; + if (!dirFile->addDirCluster()) { + return false; + } // use first entry in cluster dirIndex_ = 0; @@ -461,41 +539,51 @@ uint8_t SdFile::open(SdFile* dirFile, const char* fileName, uint8_t oflag) { p->lastWriteTime = p->creationTime; // force write of entry to SD - if (!SdVolume::cacheFlush()) return false; + if (!SdVolume::cacheFlush()) { + return false; + } // open entry in cache return openCachedEntry(dirIndex_, oflag); } //------------------------------------------------------------------------------ /** - * Open a file by index. - * - * \param[in] dirFile An open SdFat instance for the directory. - * - * \param[in] index The \a index of the directory entry for the file to be - * opened. The value for \a index is (directory file position)/32. - * - * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive - * OR of flags O_READ, O_WRITE, O_TRUNC, and O_SYNC. - * - * See open() by fileName for definition of flags and return values. - * - */ + Open a file by index. + + \param[in] dirFile An open SdFat instance for the directory. + + \param[in] index The \a index of the directory entry for the file to be + opened. The value for \a index is (directory file position)/32. + + \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive + OR of flags O_READ, O_WRITE, O_TRUNC, and O_SYNC. + + See open() by fileName for definition of flags and return values. + +*/ uint8_t SdFile::open(SdFile* dirFile, uint16_t index, uint8_t oflag) { // error if already open - if (isOpen())return false; + if (isOpen()) { + return false; + } // don't open existing file if O_CREAT and O_EXCL - user call error - if ((oflag & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) return false; + if ((oflag & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) { + return false; + } vol_ = dirFile->vol_; // seek to location of entry - if (!dirFile->seekSet(32 * index)) return false; + if (!dirFile->seekSet(32 * index)) { + return false; + } // read entry into cache dir_t* p = dirFile->readDirCache(); - if (p == NULL) return false; + if (p == NULL) { + return false; + } // error if empty slot or '.' or '..' if (p->name[0] == DIR_NAME_FREE || @@ -513,7 +601,9 @@ uint8_t SdFile::openCachedEntry(uint8_t dirIndex, uint8_t oflag) { // write or truncate is an error for a directory or read-only file if (p->attributes & (DIR_ATT_READ_ONLY | DIR_ATT_DIRECTORY)) { - if (oflag & (O_WRITE | O_TRUNC)) return false; + if (oflag & (O_WRITE | O_TRUNC)) { + return false; + } } // remember location of directory entry on SD dirIndex_ = dirIndex; @@ -528,7 +618,9 @@ uint8_t SdFile::openCachedEntry(uint8_t dirIndex, uint8_t oflag) { fileSize_ = p->fileSize; type_ = FAT_FILE_TYPE_NORMAL; } else if (DIR_IS_SUBDIR(p)) { - if (!vol_->chainSize(firstCluster_, &fileSize_)) return false; + if (!vol_->chainSize(firstCluster_, &fileSize_)) { + return false; + } type_ = FAT_FILE_TYPE_SUBDIR; } else { return false; @@ -541,23 +633,27 @@ uint8_t SdFile::openCachedEntry(uint8_t dirIndex, uint8_t oflag) { curPosition_ = 0; // truncate file to zero length if requested - if (oflag & O_TRUNC) return truncate(0); + if (oflag & O_TRUNC) { + return truncate(0); + } return true; } //------------------------------------------------------------------------------ /** - * Open a volume's root directory. - * - * \param[in] vol The FAT volume containing the root directory to be opened. - * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. - * Reasons for failure include the FAT volume has not been initialized - * or it a FAT12 volume. - */ + Open a volume's root directory. + + \param[in] vol The FAT volume containing the root directory to be opened. + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. + Reasons for failure include the FAT volume has not been initialized + or it a FAT12 volume. +*/ uint8_t SdFile::openRoot(SdVolume* vol) { // error if file is already open - if (isOpen()) return false; + if (isOpen()) { + return false; + } if (vol->fatType() == 16) { type_ = FAT_FILE_TYPE_ROOT16; @@ -566,7 +662,9 @@ uint8_t SdFile::openRoot(SdVolume* vol) { } else if (vol->fatType() == 32) { type_ = FAT_FILE_TYPE_ROOT32; firstCluster_ = vol->rootDirStart(); - if (!vol->chainSize(firstCluster_, &fileSize_)) return false; + if (!vol->chainSize(firstCluster_, &fileSize_)) { + return false; + } } else { // volume is not initialized or FAT12 return false; @@ -586,14 +684,16 @@ uint8_t SdFile::openRoot(SdVolume* vol) { } //------------------------------------------------------------------------------ /** %Print the name field of a directory entry in 8.3 format to Serial. - * - * \param[in] dir The directory structure containing the name. - * \param[in] width Blank fill name if length is less than \a width. - */ + + \param[in] dir The directory structure containing the name. + \param[in] width Blank fill name if length is less than \a width. +*/ void SdFile::printDirName(const dir_t& dir, uint8_t width) { uint8_t w = 0; for (uint8_t i = 0; i < 11; i++) { - if (dir.name[i] == ' ')continue; + if (dir.name[i] == ' ') { + continue; + } if (i == 8) { Serial.print('.'); w++; @@ -612,11 +712,11 @@ void SdFile::printDirName(const dir_t& dir, uint8_t width) { } //------------------------------------------------------------------------------ /** %Print a directory date field to Serial. - * - * Format is yyyy-mm-dd. - * - * \param[in] fatDate The date field from a directory entry. - */ + + Format is yyyy-mm-dd. + + \param[in] fatDate The date field from a directory entry. +*/ void SdFile::printFatDate(uint16_t fatDate) { Serial.print(FAT_YEAR(fatDate)); Serial.print('-'); @@ -626,11 +726,11 @@ void SdFile::printFatDate(uint16_t fatDate) { } //------------------------------------------------------------------------------ /** %Print a directory time field to Serial. - * - * Format is hh:mm:ss. - * - * \param[in] fatTime The time field from a directory entry. - */ + + Format is hh:mm:ss. + + \param[in] fatTime The time field from a directory entry. +*/ void SdFile::printFatTime(uint16_t fatTime) { printTwoDigits(FAT_HOUR(fatTime)); Serial.print(':'); @@ -640,39 +740,43 @@ void SdFile::printFatTime(uint16_t fatTime) { } //------------------------------------------------------------------------------ /** %Print a value as two digits to Serial. - * - * \param[in] v Value to be printed, 0 <= \a v <= 99 - */ + + \param[in] v Value to be printed, 0 <= \a v <= 99 +*/ void SdFile::printTwoDigits(uint8_t v) { char str[3]; - str[0] = '0' + v/10; + str[0] = '0' + v / 10; str[1] = '0' + v % 10; str[2] = 0; Serial.print(str); } //------------------------------------------------------------------------------ /** - * 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. - */ + 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. +*/ int16_t SdFile::read(void* buf, uint16_t nbyte) { uint8_t* dst = reinterpret_cast(buf); // error if not open or write only - if (!isOpen() || !(flags_ & O_READ)) return -1; + if (!isOpen() || !(flags_ & O_READ)) { + return -1; + } // max bytes left in file - if (nbyte > (fileSize_ - curPosition_)) nbyte = fileSize_ - curPosition_; + if (nbyte > (fileSize_ - curPosition_)) { + nbyte = fileSize_ - curPosition_; + } // amount left to read uint16_t toRead = nbyte; @@ -690,7 +794,9 @@ int16_t SdFile::read(void* buf, uint16_t nbyte) { curCluster_ = firstCluster_; } else { // get next cluster from FAT - if (!vol_->fatGet(curCluster_, &curCluster_)) return -1; + if (!vol_->fatGet(curCluster_, &curCluster_)) { + return -1; + } } } block = vol_->clusterStartBlock(curCluster_) + blockOfCluster; @@ -698,19 +804,27 @@ int16_t SdFile::read(void* buf, uint16_t nbyte) { uint16_t n = toRead; // amount to be read from current block - if (n > (512 - offset)) n = 512 - offset; + if (n > (512 - offset)) { + n = 512 - offset; + } // no buffering needed if n == 512 or user requests no buffering if ((unbufferedRead() || n == 512) && - block != SdVolume::cacheBlockNumber_) { - if (!vol_->readData(block, offset, n, dst)) return -1; + block != SdVolume::cacheBlockNumber_) { + if (!vol_->readData(block, offset, n, dst)) { + return -1; + } dst += n; } else { // read block to cache and copy data to caller - if (!SdVolume::cacheRawBlock(block, SdVolume::CACHE_FOR_READ)) return -1; + if (!SdVolume::cacheRawBlock(block, SdVolume::CACHE_FOR_READ)) { + return -1; + } uint8_t* src = SdVolume::cacheBuffer_.data + offset; uint8_t* end = src + n; - while (src != end) *dst++ = *src++; + while (src != end) { + *dst++ = *src++; + } } curPosition_ += n; toRead -= n; @@ -719,28 +833,36 @@ int16_t SdFile::read(void* buf, uint16_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. - */ + 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 SdFile::readDir(dir_t* dir) { int8_t n; // if not a directory file or miss-positioned return an error - if (!isDir() || (0X1F & curPosition_)) return -1; + if (!isDir() || (0X1F & curPosition_)) { + return -1; + } while ((n = read(dir, sizeof(dir_t))) == sizeof(dir_t)) { // last entry if DIR_NAME_FREE - if (dir->name[0] == DIR_NAME_FREE) break; + if (dir->name[0] == DIR_NAME_FREE) { + break; + } // skip empty entries and entry for . and .. - if (dir->name[0] == DIR_NAME_DELETED || dir->name[0] == '.') continue; + 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; + if (DIR_IS_FILE_OR_SUBDIR(dir)) { + return n; + } } // error, end of file, or past last entry return n < 0 ? -1 : 0; @@ -750,13 +872,17 @@ int8_t SdFile::readDir(dir_t* dir) { // Assumes file is correctly positioned dir_t* SdFile::readDirCache(void) { // error if not directory - if (!isDir()) return NULL; + if (!isDir()) { + return NULL; + } // index of entry in cache uint8_t i = (curPosition_ >> 5) & 0XF; // use read to locate and cache block - if (read() < 0) return NULL; + if (read() < 0) { + return NULL; + } // advance to next entry curPosition_ += 31; @@ -766,26 +892,30 @@ dir_t* SdFile::readDirCache(void) { } //------------------------------------------------------------------------------ /** - * Remove a file. - * - * The directory entry and all data for the file are deleted. - * - * \note This function should not be used to delete the 8.3 version of a - * file that has a long name. For example if a file has the long name - * "New Text Document.txt" you should not delete the 8.3 name "NEWTEX~1.TXT". - * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. - * Reasons for failure include the file read-only, is a directory, - * or an I/O error occurred. - */ + Remove a file. + + The directory entry and all data for the file are deleted. + + \note This function should not be used to delete the 8.3 version of a + file that has a long name. For example if a file has the long name + "New Text Document.txt" you should not delete the 8.3 name "NEWTEX~1.TXT". + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. + Reasons for failure include the file read-only, is a directory, + or an I/O error occurred. +*/ uint8_t SdFile::remove(void) { // free any clusters - will fail if read-only or directory - if (!truncate(0)) return false; + if (!truncate(0)) { + return false; + } // cache directory entry dir_t* d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); - if (!d) return false; + if (!d) { + return false; + } // mark entry deleted d->name[0] = DIR_NAME_DELETED; @@ -798,60 +928,72 @@ uint8_t SdFile::remove(void) { } //------------------------------------------------------------------------------ /** - * 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] fileName The name of the file to be removed. - * - * \note This function should not be used to delete the 8.3 version of a - * file that has a long name. For example if a file has the long name - * "New Text Document.txt" you should not delete the 8.3 name "NEWTEX~1.TXT". - * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. - * Reasons for failure include the file is a directory, is read only, - * \a dirFile is not a directory, \a fileName is not found - * or an I/O error occurred. - */ + 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] fileName The name of the file to be removed. + + \note This function should not be used to delete the 8.3 version of a + file that has a long name. For example if a file has the long name + "New Text Document.txt" you should not delete the 8.3 name "NEWTEX~1.TXT". + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. + Reasons for failure include the file is a directory, is read only, + \a dirFile is not a directory, \a fileName is not found + or an I/O error occurred. +*/ uint8_t SdFile::remove(SdFile* dirFile, const char* fileName) { SdFile file; - if (!file.open(dirFile, fileName, O_WRITE)) return false; + if (!file.open(dirFile, fileName, O_WRITE)) { + return false; + } return file.remove(); } //------------------------------------------------------------------------------ /** Remove a directory file. - * - * The directory file will be removed only if it is empty and is not the - * root directory. rmDir() follows DOS and Windows and ignores the - * read-only attribute for the directory. - * - * \note This function should not be used to delete the 8.3 version of a - * directory that has a long name. For example if a directory has the - * long name "New folder" you should not delete the 8.3 name "NEWFOL~1". - * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. - * Reasons for failure include the file is not a directory, is the root - * directory, is not empty, or an I/O error occurred. - */ + + The directory file will be removed only if it is empty and is not the + root directory. rmDir() follows DOS and Windows and ignores the + read-only attribute for the directory. + + \note This function should not be used to delete the 8.3 version of a + directory that has a long name. For example if a directory has the + long name "New folder" you should not delete the 8.3 name "NEWFOL~1". + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. + Reasons for failure include the file is not a directory, is the root + directory, is not empty, or an I/O error occurred. +*/ uint8_t SdFile::rmDir(void) { // must be open subdirectory - if (!isSubDir()) return false; + if (!isSubDir()) { + return false; + } rewind(); // make sure directory is empty while (curPosition_ < fileSize_) { dir_t* p = readDirCache(); - if (p == NULL) return false; + if (p == NULL) { + return false; + } // done if past last used entry - if (p->name[0] == DIR_NAME_FREE) break; + if (p->name[0] == DIR_NAME_FREE) { + break; + } // skip empty slot or '.' or '..' - if (p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') continue; + if (p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') { + continue; + } // error not empty - if (DIR_IS_FILE_OR_SUBDIR(p)) return false; + if (DIR_IS_FILE_OR_SUBDIR(p)) { + return false; + } } // convert empty directory to normal file for remove type_ = FAT_FILE_TYPE_NORMAL; @@ -860,70 +1002,90 @@ uint8_t SdFile::rmDir(void) { } //------------------------------------------------------------------------------ /** Recursively delete a directory and all contained files. - * - * This is like the Unix/Linux 'rm -rf *' if called with the root directory - * hence the name. - * - * Warning - This will remove all contents of the directory including - * subdirectories. The directory will then be removed if it is not root. - * The read-only attribute for files will be ignored. - * - * \note This function should not be used to delete the 8.3 version of - * a directory that has a long name. See remove() and rmDir(). - * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. - */ + + This is like the Unix/Linux 'rm -rf *' if called with the root directory + hence the name. + + Warning - This will remove all contents of the directory including + subdirectories. The directory will then be removed if it is not root. + The read-only attribute for files will be ignored. + + \note This function should not be used to delete the 8.3 version of + a directory that has a long name. See remove() and rmDir(). + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. +*/ uint8_t SdFile::rmRfStar(void) { rewind(); while (curPosition_ < fileSize_) { SdFile f; // remember position - uint16_t index = curPosition_/32; + uint16_t index = curPosition_ / 32; dir_t* p = readDirCache(); - if (!p) return false; + if (!p) { + return false; + } // done if past last entry - if (p->name[0] == DIR_NAME_FREE) break; + if (p->name[0] == DIR_NAME_FREE) { + break; + } // skip empty slot or '.' or '..' - if (p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') continue; + if (p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') { + continue; + } // skip if part of long file name or volume label in root - if (!DIR_IS_FILE_OR_SUBDIR(p)) continue; + if (!DIR_IS_FILE_OR_SUBDIR(p)) { + continue; + } - if (!f.open(this, index, O_READ)) return false; + if (!f.open(this, index, O_READ)) { + return false; + } if (f.isSubDir()) { // recursively delete - if (!f.rmRfStar()) return false; + if (!f.rmRfStar()) { + return false; + } } else { // ignore read-only f.flags_ |= O_WRITE; - if (!f.remove()) return false; + if (!f.remove()) { + return false; + } } // position to next entry if required - if (curPosition_ != (32u*(index + 1))) { - if (!seekSet(32u*(index + 1))) return false; + if (curPosition_ != (32u * (index + 1))) { + if (!seekSet(32u * (index + 1))) { + return false; + } } } // don't try to delete root - if (isRoot()) return true; + if (isRoot()) { + return true; + } return rmDir(); } //------------------------------------------------------------------------------ /** - * Sets a file's position. - * - * \param[in] pos The new position in bytes from the beginning of the file. - * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. - */ + Sets a file's position. + + \param[in] pos The new position in bytes from the beginning of the file. + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. +*/ uint8_t SdFile::seekSet(uint32_t pos) { // error if file not open or seek past end of file - if (!isOpen() || pos > fileSize_) return false; + if (!isOpen() || pos > fileSize_) { + return false; + } if (type_ == FAT_FILE_TYPE_ROOT16) { curPosition_ = pos; @@ -947,31 +1109,39 @@ uint8_t SdFile::seekSet(uint32_t pos) { nNew -= nCur; } while (nNew--) { - if (!vol_->fatGet(curCluster_, &curCluster_)) return false; + if (!vol_->fatGet(curCluster_, &curCluster_)) { + return false; + } } curPosition_ = pos; return true; } //------------------------------------------------------------------------------ /** - * The sync() call causes all modified data and directory fields - * to be written to the storage device. - * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. - * Reasons for failure include a call to sync() before a file has been - * opened or an I/O error. - */ + The sync() call causes all modified data and directory fields + to be written to the storage device. + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. + Reasons for failure include a call to sync() before a file has been + opened or an I/O error. +*/ uint8_t SdFile::sync(void) { // only allow open files and directories - if (!isOpen()) return false; + if (!isOpen()) { + return false; + } if (flags_ & F_FILE_DIR_DIRTY) { dir_t* d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); - if (!d) return false; + if (!d) { + return false; + } // do not set filesize for dir files - if (!isDir()) d->fileSize = fileSize_; + if (!isDir()) { + d->fileSize = fileSize_; + } // update first cluster fields d->firstClusterLow = firstCluster_ & 0XFFFF; @@ -989,55 +1159,57 @@ uint8_t SdFile::sync(void) { } //------------------------------------------------------------------------------ /** - * Set a file's timestamps in its directory entry. - * - * \param[in] flags Values for \a flags are constructed by a bitwise-inclusive - * OR of flags from the following list - * - * T_ACCESS - Set the file's last access date. - * - * T_CREATE - Set the file's creation date and time. - * - * T_WRITE - Set the file's last write/modification date and time. - * - * \param[in] year Valid range 1980 - 2107 inclusive. - * - * \param[in] month Valid range 1 - 12 inclusive. - * - * \param[in] day Valid range 1 - 31 inclusive. - * - * \param[in] hour Valid range 0 - 23 inclusive. - * - * \param[in] minute Valid range 0 - 59 inclusive. - * - * \param[in] second Valid range 0 - 59 inclusive - * - * \note It is possible to set an invalid date since there is no check for - * the number of days in a month. - * - * \note - * Modify and access timestamps may be overwritten if a date time callback - * function has been set by dateTimeCallback(). - * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. - */ + Set a file's timestamps in its directory entry. + + \param[in] flags Values for \a flags are constructed by a bitwise-inclusive + OR of flags from the following list + + T_ACCESS - Set the file's last access date. + + T_CREATE - Set the file's creation date and time. + + T_WRITE - Set the file's last write/modification date and time. + + \param[in] year Valid range 1980 - 2107 inclusive. + + \param[in] month Valid range 1 - 12 inclusive. + + \param[in] day Valid range 1 - 31 inclusive. + + \param[in] hour Valid range 0 - 23 inclusive. + + \param[in] minute Valid range 0 - 59 inclusive. + + \param[in] second Valid range 0 - 59 inclusive + + \note It is possible to set an invalid date since there is no check for + the number of days in a month. + + \note + Modify and access timestamps may be overwritten if a date time callback + function has been set by dateTimeCallback(). + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. +*/ uint8_t SdFile::timestamp(uint8_t flags, uint16_t year, uint8_t month, - uint8_t day, uint8_t hour, uint8_t minute, uint8_t second) { + uint8_t day, uint8_t hour, uint8_t minute, uint8_t second) { if (!isOpen() - || year < 1980 - || year > 2107 - || month < 1 - || month > 12 - || day < 1 - || day > 31 - || hour > 23 - || minute > 59 - || second > 59) { - return false; + || year < 1980 + || year > 2107 + || month < 1 + || month > 12 + || day < 1 + || day > 31 + || hour > 23 + || minute > 59 + || second > 59) { + return false; } dir_t* d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); - if (!d) return false; + if (!d) { + return false; + } uint16_t dirDate = FAT_DATE(year, month, day); uint16_t dirTime = FAT_TIME(hour, minute, second); @@ -1059,47 +1231,63 @@ uint8_t SdFile::timestamp(uint8_t flags, uint16_t year, uint8_t month, } //------------------------------------------------------------------------------ /** - * Truncate a file to a specified length. The current file position - * will be maintained if it is less than or equal to \a length otherwise - * it will be set to end of file. - * - * \param[in] length The desired length for the file. - * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. - * Reasons for failure include file is read only, file is a directory, - * \a length is greater than the current file size or an I/O error occurs. - */ + Truncate a file to a specified length. The current file position + will be maintained if it is less than or equal to \a length otherwise + it will be set to end of file. + + \param[in] length The desired length for the file. + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. + Reasons for failure include file is read only, file is a directory, + \a length is greater than the current file size or an I/O error occurs. +*/ uint8_t SdFile::truncate(uint32_t length) { -// error if not a normal file or read-only - if (!isFile() || !(flags_ & O_WRITE)) return false; + // error if not a normal file or read-only + if (!isFile() || !(flags_ & O_WRITE)) { + return false; + } // error if length is greater than current size - if (length > fileSize_) return false; + if (length > fileSize_) { + return false; + } // fileSize and length are zero - nothing to do - if (fileSize_ == 0) return true; + if (fileSize_ == 0) { + return true; + } // remember position for seek after truncation uint32_t newPos = curPosition_ > length ? length : curPosition_; // position to last cluster in truncated file - if (!seekSet(length)) return false; + if (!seekSet(length)) { + return false; + } if (length == 0) { // free all clusters - if (!vol_->freeChain(firstCluster_)) return false; + if (!vol_->freeChain(firstCluster_)) { + return false; + } firstCluster_ = 0; } else { uint32_t toFree; - if (!vol_->fatGet(curCluster_, &toFree)) return false; + if (!vol_->fatGet(curCluster_, &toFree)) { + return false; + } if (!vol_->isEOC(toFree)) { // free extra clusters - if (!vol_->freeChain(toFree)) return false; + if (!vol_->freeChain(toFree)) { + return false; + } // current cluster is end of chain - if (!vol_->fatPutEOC(curCluster_)) return false; + if (!vol_->fatPutEOC(curCluster_)) { + return false; + } } } fileSize_ = length; @@ -1107,28 +1295,30 @@ uint8_t SdFile::truncate(uint32_t length) { // need to update directory entry flags_ |= F_FILE_DIR_DIRTY; - if (!sync()) return false; + if (!sync()) { + return false; + } // set file to correct position return seekSet(newPos); } //------------------------------------------------------------------------------ /** - * 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 0. 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. - * - */ + 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 0. 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 SdFile::write(const void* buf, uint16_t nbyte) { // convert void* to uint8_t* - must be before goto statements const uint8_t* src = reinterpret_cast(buf); @@ -1137,11 +1327,15 @@ size_t SdFile::write(const void* buf, uint16_t nbyte) { uint16_t nToWrite = nbyte; // error if not a normal file or is read-only - if (!isFile() || !(flags_ & O_WRITE)) goto writeErrorReturn; + if (!isFile() || !(flags_ & O_WRITE)) { + goto writeErrorReturn; + } // seek to end of file if append flag if ((flags_ & O_APPEND) && curPosition_ != fileSize_) { - if (!seekEnd()) goto writeErrorReturn; + if (!seekEnd()) { + goto writeErrorReturn; + } } while (nToWrite > 0) { @@ -1152,16 +1346,22 @@ size_t SdFile::write(const void* buf, uint16_t nbyte) { if (curCluster_ == 0) { if (firstCluster_ == 0) { // allocate first cluster of file - if (!addCluster()) goto writeErrorReturn; + if (!addCluster()) { + goto writeErrorReturn; + } } else { curCluster_ = firstCluster_; } } else { uint32_t next; - if (!vol_->fatGet(curCluster_, &next)) return false; + if (!vol_->fatGet(curCluster_, &next)) { + return false; + } if (vol_->isEOC(next)) { // add cluster if at end of chain - if (!addCluster()) goto writeErrorReturn; + if (!addCluster()) { + goto writeErrorReturn; + } } else { curCluster_ = next; } @@ -1171,7 +1371,9 @@ size_t SdFile::write(const void* buf, uint16_t nbyte) { uint16_t n = 512 - blockOffset; // lesser of space and amount to write - if (n > nToWrite) n = nToWrite; + if (n > nToWrite) { + n = nToWrite; + } // block for data write uint32_t block = vol_->clusterStartBlock(curCluster_) + blockOfCluster; @@ -1181,12 +1383,16 @@ size_t SdFile::write(const void* buf, uint16_t nbyte) { if (SdVolume::cacheBlockNumber_ == block) { SdVolume::cacheBlockNumber_ = 0XFFFFFFFF; } - if (!vol_->writeBlock(block, src)) goto writeErrorReturn; + if (!vol_->writeBlock(block, src)) { + goto writeErrorReturn; + } src += 512; } else { if (blockOffset == 0 && curPosition_ >= fileSize_) { // start of new block don't need to read into cache - if (!SdVolume::cacheFlush()) goto writeErrorReturn; + if (!SdVolume::cacheFlush()) { + goto writeErrorReturn; + } SdVolume::cacheBlockNumber_ = block; SdVolume::cacheSetDirty(); } else { @@ -1197,7 +1403,9 @@ size_t SdFile::write(const void* buf, uint16_t nbyte) { } uint8_t* dst = SdVolume::cacheBuffer_.data + blockOffset; uint8_t* end = dst + n; - while (dst != end) *dst++ = *src++; + while (dst != end) { + *dst++ = *src++; + } } nToWrite -= n; curPosition_ += n; @@ -1212,11 +1420,13 @@ size_t SdFile::write(const void* buf, uint16_t nbyte) { } if (flags_ & O_SYNC) { - if (!sync()) goto writeErrorReturn; + if (!sync()) { + goto writeErrorReturn; + } } return nbyte; - writeErrorReturn: +writeErrorReturn: // return for write error //writeError = true; setWriteError(); @@ -1224,38 +1434,40 @@ size_t SdFile::write(const void* buf, uint16_t nbyte) { } //------------------------------------------------------------------------------ /** - * Write a byte to a file. Required by the Arduino Print class. - * - * Use SdFile::writeError to check for errors. - */ + Write a byte to a file. Required by the Arduino Print class. + + Use SdFile::writeError to check for errors. +*/ size_t SdFile::write(uint8_t b) { return write(&b, 1); } //------------------------------------------------------------------------------ /** - * Write a string to a file. Used by the Arduino Print class. - * - * Use SdFile::writeError to check for errors. - */ + Write a string to a file. Used by the Arduino Print class. + + Use SdFile::writeError to check for errors. +*/ size_t SdFile::write(const char* str) { return write(str, strlen(str)); } #ifdef __AVR__ //------------------------------------------------------------------------------ /** - * Write a PROGMEM string to a file. - * - * Use SdFile::writeError to check for errors. - */ + Write a PROGMEM string to a file. + + Use SdFile::writeError to check for errors. +*/ void SdFile::write_P(PGM_P str) { - for (uint8_t c; (c = pgm_read_byte(str)); str++) write(c); + for (uint8_t c; (c = pgm_read_byte(str)); str++) { + write(c); + } } //------------------------------------------------------------------------------ /** - * Write a PROGMEM string followed by CR/LF to a file. - * - * Use SdFile::writeError to check for errors. - */ + Write a PROGMEM string followed by CR/LF to a file. + + Use SdFile::writeError to check for errors. +*/ void SdFile::writeln_P(PGM_P str) { write_P(str); println(); diff --git a/src/utility/SdInfo.h b/src/utility/SdInfo.h index acde74d..6a0e087 100644 --- a/src/utility/SdInfo.h +++ b/src/utility/SdInfo.h @@ -1,22 +1,22 @@ /* Arduino Sd2Card Library - * Copyright (C) 2009 by William Greiman - * - * This file is part of the Arduino Sd2Card Library - * - * This Library is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This Library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with the Arduino Sd2Card Library. If not, see - * . - */ + Copyright (C) 2009 by William Greiman + + This file is part of the Arduino Sd2Card Library + + This Library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the Arduino Sd2Card Library. If not, see + . +*/ #ifndef SdInfo_h #define SdInfo_h #include @@ -100,11 +100,11 @@ typedef struct CID { unsigned reserved : 4; // byte 14 unsigned mdt_month : 4; - unsigned mdt_year_low :4; + unsigned mdt_year_low : 4; // byte 15 unsigned always1 : 1; unsigned crc : 7; -}cid_t; +} cid_t; //------------------------------------------------------------------------------ // CSD for version 1.00 cards typedef struct CSDV1 { @@ -126,7 +126,7 @@ typedef struct CSDV1 { unsigned c_size_high : 2; unsigned reserved2 : 2; unsigned dsr_imp : 1; - unsigned read_blk_misalign :1; + unsigned read_blk_misalign : 1; unsigned write_blk_misalign : 1; unsigned read_bl_partial : 1; // byte 7 @@ -134,7 +134,7 @@ typedef struct CSDV1 { // byte 8 unsigned vdd_r_curr_max : 3; unsigned vdd_r_curr_min : 3; - unsigned c_size_low :2; + unsigned c_size_low : 2; // byte 9 unsigned c_size_mult_high : 2; unsigned vdd_w_cur_max : 3; @@ -165,7 +165,7 @@ typedef struct CSDV1 { // byte 15 unsigned always1 : 1; unsigned crc : 7; -}csd1_t; +} csd1_t; //------------------------------------------------------------------------------ // CSD for version 2.00 cards typedef struct CSDV2 { @@ -186,7 +186,7 @@ typedef struct CSDV2 { // byte 6 unsigned reserved2 : 4; unsigned dsr_imp : 1; - unsigned read_blk_misalign :1; + unsigned read_blk_misalign : 1; unsigned write_blk_misalign : 1; unsigned read_bl_partial : 1; // byte 7 @@ -222,7 +222,7 @@ typedef struct CSDV2 { // byte 15 unsigned always1 : 1; unsigned crc : 7; -}csd2_t; +} csd2_t; //------------------------------------------------------------------------------ // union of old and new style CSD register union csd_t { diff --git a/src/utility/SdVolume.cpp b/src/utility/SdVolume.cpp index 2fbb810..30fa232 100644 --- a/src/utility/SdVolume.cpp +++ b/src/utility/SdVolume.cpp @@ -1,22 +1,22 @@ /* Arduino SdFat Library - * Copyright (C) 2009 by William Greiman - * - * This file is part of the Arduino SdFat Library - * - * This Library is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This Library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with the Arduino SdFat Library. If not, see - * . - */ + Copyright (C) 2009 by William Greiman + + This file is part of the Arduino SdFat Library + + This Library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the Arduino SdFat Library. If not, see + . +*/ #include "SdFat.h" //------------------------------------------------------------------------------ // raw block cache @@ -58,14 +58,18 @@ uint8_t SdVolume::allocContiguous(uint32_t count, uint32_t* curCluster) { // search the FAT for free clusters for (uint32_t n = 0;; n++, endCluster++) { // can't find space checked all clusters - if (n >= clusterCount_) return false; + if (n >= clusterCount_) { + return false; + } // past end - start from beginning of FAT if (endCluster > fatEnd) { bgnCluster = endCluster = 2; } uint32_t f; - if (!fatGet(endCluster, &f)) return false; + if (!fatGet(endCluster, &f)) { + return false; + } if (f != 0) { // cluster in use try next cluster as bgnCluster @@ -76,22 +80,30 @@ uint8_t SdVolume::allocContiguous(uint32_t count, uint32_t* curCluster) { } } // mark end of chain - if (!fatPutEOC(endCluster)) return false; + if (!fatPutEOC(endCluster)) { + return false; + } // link clusters while (endCluster > bgnCluster) { - if (!fatPut(endCluster - 1, endCluster)) return false; + if (!fatPut(endCluster - 1, endCluster)) { + return false; + } endCluster--; } if (*curCluster != 0) { // connect chains - if (!fatPut(*curCluster, bgnCluster)) return false; + if (!fatPut(*curCluster, bgnCluster)) { + return false; + } } // return first cluster number to caller *curCluster = bgnCluster; // remember possible next free cluster - if (setStart) allocSearchStart_ = bgnCluster + 1; + if (setStart) { + allocSearchStart_ = bgnCluster + 1; + } return true; } @@ -115,8 +127,12 @@ uint8_t SdVolume::cacheFlush(void) { //------------------------------------------------------------------------------ uint8_t SdVolume::cacheRawBlock(uint32_t blockNumber, uint8_t action) { if (cacheBlockNumber_ != blockNumber) { - if (!cacheFlush()) return false; - if (!sdCard_->readBlock(blockNumber, cacheBuffer_.data)) return false; + if (!cacheFlush()) { + return false; + } + if (!sdCard_->readBlock(blockNumber, cacheBuffer_.data)) { + return false; + } cacheBlockNumber_ = blockNumber; } cacheDirty_ |= action; @@ -125,7 +141,9 @@ uint8_t SdVolume::cacheRawBlock(uint32_t blockNumber, uint8_t action) { //------------------------------------------------------------------------------ // cache a zero block for blockNumber uint8_t SdVolume::cacheZeroBlock(uint32_t blockNumber) { - if (!cacheFlush()) return false; + if (!cacheFlush()) { + return false; + } // loop take less flash than memset(cacheBuffer_.data, 0, 512); for (uint16_t i = 0; i < 512; i++) { @@ -140,7 +158,9 @@ uint8_t SdVolume::cacheZeroBlock(uint32_t blockNumber) { uint8_t SdVolume::chainSize(uint32_t cluster, uint32_t* size) const { uint32_t s = 0; do { - if (!fatGet(cluster, &cluster)) return false; + if (!fatGet(cluster, &cluster)) { + return false; + } s += 512UL << clusterSizeShift_; } while (!isEOC(cluster)); *size = s; @@ -149,11 +169,15 @@ uint8_t SdVolume::chainSize(uint32_t cluster, uint32_t* size) const { //------------------------------------------------------------------------------ // Fetch a FAT entry uint8_t SdVolume::fatGet(uint32_t cluster, uint32_t* value) const { - if (cluster > (clusterCount_ + 1)) return false; + if (cluster > (clusterCount_ + 1)) { + return false; + } uint32_t lba = fatStartBlock_; lba += fatType_ == 16 ? cluster >> 8 : cluster >> 7; if (lba != cacheBlockNumber_) { - if (!cacheRawBlock(lba, CACHE_FOR_READ)) return false; + if (!cacheRawBlock(lba, CACHE_FOR_READ)) { + return false; + } } if (fatType_ == 16) { *value = cacheBuffer_.fat16[cluster & 0XFF]; @@ -166,17 +190,23 @@ uint8_t SdVolume::fatGet(uint32_t cluster, uint32_t* value) const { // Store a FAT entry uint8_t SdVolume::fatPut(uint32_t cluster, uint32_t value) { // error if reserved cluster - if (cluster < 2) return false; + if (cluster < 2) { + return false; + } // error if not in FAT - if (cluster > (clusterCount_ + 1)) return false; + if (cluster > (clusterCount_ + 1)) { + return false; + } // calculate block address for entry uint32_t lba = fatStartBlock_; lba += fatType_ == 16 ? cluster >> 8 : cluster >> 7; if (lba != cacheBlockNumber_) { - if (!cacheRawBlock(lba, CACHE_FOR_READ)) return false; + if (!cacheRawBlock(lba, CACHE_FOR_READ)) { + return false; + } } // store entry if (fatType_ == 16) { @@ -187,7 +217,9 @@ uint8_t SdVolume::fatPut(uint32_t cluster, uint32_t value) { cacheSetDirty(); // mirror second FAT - if (fatCount_ > 1) cacheMirrorBlock_ = lba + blocksPerFat_; + if (fatCount_ > 1) { + cacheMirrorBlock_ = lba + blocksPerFat_; + } return true; } //------------------------------------------------------------------------------ @@ -198,10 +230,14 @@ uint8_t SdVolume::freeChain(uint32_t cluster) { do { uint32_t next; - if (!fatGet(cluster, &next)) return false; + if (!fatGet(cluster, &next)) { + return false; + } // free cluster - if (!fatPut(cluster, 0)) return false; + if (!fatPut(cluster, 0)) { + return false; + } cluster = next; } while (!isEOC(cluster)); @@ -210,45 +246,51 @@ uint8_t SdVolume::freeChain(uint32_t cluster) { } //------------------------------------------------------------------------------ /** - * Initialize a FAT volume. - * - * \param[in] dev The SD card where the volume is located. - * - * \param[in] part The partition to be used. Legal values for \a part are - * 1-4 to use the corresponding partition on a device formatted with - * a MBR, Master Boot Record, or zero if the device is formatted as - * a super floppy with the FAT boot sector in block zero. - * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. Reasons for - * failure include not finding a valid partition, not finding a valid - * FAT file system in the specified partition or an I/O error. - */ + Initialize a FAT volume. + + \param[in] dev The SD card where the volume is located. + + \param[in] part The partition to be used. Legal values for \a part are + 1-4 to use the corresponding partition on a device formatted with + a MBR, Master Boot Record, or zero if the device is formatted as + a super floppy with the FAT boot sector in block zero. + + \return The value one, true, is returned for success and + the value zero, false, is returned for failure. Reasons for + failure include not finding a valid partition, not finding a valid + FAT file system in the specified partition or an I/O error. +*/ uint8_t SdVolume::init(Sd2Card* dev, uint8_t part) { uint32_t volumeStartBlock = 0; sdCard_ = dev; // 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)return false; - if (!cacheRawBlock(volumeStartBlock, CACHE_FOR_READ)) return false; - part_t* p = &cacheBuffer_.mbr.part[part-1]; - if ((p->boot & 0X7F) !=0 || - p->totalSectors < 100 || - p->firstSector == 0) { + if (part > 4) { + return false; + } + if (!cacheRawBlock(volumeStartBlock, CACHE_FOR_READ)) { + return false; + } + part_t* p = &cacheBuffer_.mbr.part[part - 1]; + if ((p->boot & 0X7F) != 0 || + p->totalSectors < 100 || + p->firstSector == 0) { // not a valid partition return false; } volumeStartBlock = p->firstSector; } - if (!cacheRawBlock(volumeStartBlock, CACHE_FOR_READ)) return false; + if (!cacheRawBlock(volumeStartBlock, CACHE_FOR_READ)) { + return false; + } bpb_t* bpb = &cacheBuffer_.fbs.bpb; if (bpb->bytesPerSector != 512 || - bpb->fatCount == 0 || - bpb->reservedSectorCount == 0 || - bpb->sectorsPerCluster == 0) { - // not valid FAT volume - return false; + bpb->fatCount == 0 || + bpb->reservedSectorCount == 0 || + bpb->sectorsPerCluster == 0) { + // not valid FAT volume + return false; } fatCount_ = bpb->fatCount; blocksPerCluster_ = bpb->sectorsPerCluster; @@ -257,10 +299,12 @@ uint8_t SdVolume::init(Sd2Card* dev, uint8_t part) { clusterSizeShift_ = 0; while (blocksPerCluster_ != (1 << clusterSizeShift_)) { // error if not power of 2 - if (clusterSizeShift_++ > 7) return false; + if (clusterSizeShift_++ > 7) { + return false; + } } blocksPerFat_ = bpb->sectorsPerFat16 ? - bpb->sectorsPerFat16 : bpb->sectorsPerFat32; + bpb->sectorsPerFat16 : bpb->sectorsPerFat32; fatStartBlock_ = volumeStartBlock + bpb->reservedSectorCount; @@ -271,11 +315,11 @@ uint8_t SdVolume::init(Sd2Card* dev, uint8_t part) { rootDirStart_ = fatStartBlock_ + bpb->fatCount * blocksPerFat_; // data start for FAT16 and FAT32 - dataStartBlock_ = rootDirStart_ + ((32 * bpb->rootDirEntryCount + 511)/512); + dataStartBlock_ = rootDirStart_ + ((32 * bpb->rootDirEntryCount + 511) / 512); // total blocks for FAT16 or FAT32 uint32_t totalBlocks = bpb->totalSectors16 ? - bpb->totalSectors16 : bpb->totalSectors32; + bpb->totalSectors16 : bpb->totalSectors32; // total data blocks clusterCount_ = totalBlocks - (dataStartBlock_ - volumeStartBlock);