diff --git a/Marlin/Marlin_main.cpp b/Marlin/Marlin_main.cpp index ed4efcfc2..ec62ca85a 100644 --- a/Marlin/Marlin_main.cpp +++ b/Marlin/Marlin_main.cpp @@ -1276,19 +1276,25 @@ inline void get_serial_commands() { || ((sd_char == '#' || sd_char == ':') && !sd_comment_mode) ) { if (card_eof) { - SERIAL_PROTOCOLLNPGM(MSG_FILE_PRINTED); + card.printingHasFinished(); - #if ENABLED(PRINTER_EVENT_LEDS) - LCD_MESSAGEPGM(MSG_INFO_COMPLETED_PRINTS); - set_led_color(0, 255, 0); // Green - #if HAS_RESUME_CONTINUE - enqueue_and_echo_commands_P(PSTR("M0")); // end of the queue! - #else - safe_delay(1000); + + if (card.sdprinting) + sd_count = 0; // If a sub-file was printing, continue from call point + else { + SERIAL_PROTOCOLLNPGM(MSG_FILE_PRINTED); + #if ENABLED(PRINTER_EVENT_LEDS) + LCD_MESSAGEPGM(MSG_INFO_COMPLETED_PRINTS); + set_led_color(0, 255, 0); // Green + #if HAS_RESUME_CONTINUE + enqueue_and_echo_commands_P(PSTR("M0")); // end of the queue! + #else + safe_delay(1000); + #endif + set_led_color(0, 0, 0); // OFF #endif - set_led_color(0, 0, 0); // OFF - #endif - card.checkautostart(true); + card.checkautostart(true); + } } else if (n == -1) { SERIAL_ERROR_START(); @@ -6897,19 +6903,23 @@ inline void gcode_M31() { /** * M32: Select file and start SD Print + * + * Examples: + * + * M32 !PATH/TO/FILE.GCO# ; Start FILE.GCO + * M32 P !PATH/TO/FILE.GCO# ; Start FILE.GCO as a procedure + * M32 S60 !PATH/TO/FILE.GCO# ; Start FILE.GCO at byte 60 + * */ inline void gcode_M32() { - if (card.sdprinting) - stepper.synchronize(); - - char* namestartpos = parser.string_arg; - const bool call_procedure = parser.boolval('P'); + if (card.sdprinting) stepper.synchronize(); if (card.cardOK) { - card.openFile(namestartpos, true, call_procedure); + const bool call_procedure = parser.boolval('P'); + + card.openFile(parser.string_arg, true, call_procedure); - if (parser.seenval('S')) - card.setIndex(parser.value_long()); + if (parser.seenval('S')) card.setIndex(parser.value_long()); card.startFileprint(); diff --git a/Marlin/Sd2Card.cpp b/Marlin/Sd2Card.cpp index 2afe9a8b4..6683e4b4f 100644 --- a/Marlin/Sd2Card.cpp +++ b/Marlin/Sd2Card.cpp @@ -26,19 +26,19 @@ * * This file is part of the Arduino Sd2Card Library */ -#include "Marlin.h" +#include "MarlinConfig.h" #if ENABLED(SDSUPPORT) + #include "Sd2Card.h" #if ENABLED(USE_WATCHDOG) #include "watchdog.h" #endif -//------------------------------------------------------------------------------ #if DISABLED(SOFTWARE_SPI) // functions for hardware SPI - //------------------------------------------------------------------------------ + // make sure SPCR rate is in expected bits #if (SPR0 != 0 || SPR1 != 1) #error "unexpected SPCR bits" @@ -52,14 +52,14 @@ SPCR = _BV(SPE) | _BV(MSTR) | (spiRate >> 1); SPSR = spiRate & 1 || spiRate == 6 ? 0 : _BV(SPI2X); } - //------------------------------------------------------------------------------ + /** SPI receive a byte */ static uint8_t spiRec() { SPDR = 0xFF; while (!TEST(SPSR, SPIF)) { /* Intentionally left empty */ } return SPDR; } - //------------------------------------------------------------------------------ + /** SPI read data - only one call so force inline */ static inline __attribute__((always_inline)) void spiRead(uint8_t* buf, uint16_t nbyte) { @@ -73,13 +73,13 @@ while (!TEST(SPSR, SPIF)) { /* Intentionally left empty */ } buf[nbyte] = SPDR; } - //------------------------------------------------------------------------------ + /** SPI send a byte */ static void spiSend(uint8_t b) { SPDR = b; while (!TEST(SPSR, SPIF)) { /* Intentionally left empty */ } } - //------------------------------------------------------------------------------ + /** SPI send block - only one call so force inline */ static inline __attribute__((always_inline)) void spiSendBlock(uint8_t token, const uint8_t* buf) { @@ -95,9 +95,10 @@ //------------------------------------------------------------------------------ #else // SOFTWARE_SPI //------------------------------------------------------------------------------ + /** nop to tune soft SPI timing */ #define nop asm volatile ("nop\n\t") - //------------------------------------------------------------------------------ + /** Soft SPI receive byte */ static uint8_t spiRec() { uint8_t data = 0; @@ -123,13 +124,13 @@ sei(); return data; } - //------------------------------------------------------------------------------ + /** Soft SPI read data */ static void spiRead(uint8_t* buf, uint16_t nbyte) { for (uint16_t i = 0; i < nbyte; i++) buf[i] = spiRec(); } - //------------------------------------------------------------------------------ + /** Soft SPI send byte */ static void spiSend(uint8_t data) { // no interrupts during byte send - about 8 us @@ -153,7 +154,7 @@ // enable interrupts sei(); } - //------------------------------------------------------------------------------ + /** Soft SPI send block */ void spiSendBlock(uint8_t token, const uint8_t* buf) { spiSend(token); @@ -161,7 +162,7 @@ spiSend(buf[i]); } #endif // SOFTWARE_SPI -//------------------------------------------------------------------------------ + // send command and return error code. Return zero for OK uint8_t Sd2Card::cardCommand(uint8_t cmd, uint32_t arg) { // select card @@ -189,7 +190,7 @@ uint8_t Sd2Card::cardCommand(uint8_t cmd, uint32_t arg) { for (uint8_t i = 0; ((status_ = spiRec()) & 0x80) && i != 0xFF; i++) { /* Intentionally left empty */ } return status_; } -//------------------------------------------------------------------------------ + /** * Determine the size of an SD flash memory card. * @@ -217,19 +218,20 @@ uint32_t Sd2Card::cardSize() { return 0; } } -//------------------------------------------------------------------------------ + void Sd2Card::chipSelectHigh() { digitalWrite(chipSelectPin_, HIGH); } -//------------------------------------------------------------------------------ + void Sd2Card::chipSelectLow() { #if DISABLED(SOFTWARE_SPI) spiInit(spiRate_); #endif // SOFTWARE_SPI digitalWrite(chipSelectPin_, LOW); } -//------------------------------------------------------------------------------ -/** Erase a range of blocks. + +/** + * 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. @@ -239,8 +241,7 @@ void Sd2Card::chipSelectLow() { * 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. + * \return true for success, false for failure. */ bool Sd2Card::erase(uint32_t firstBlock, uint32_t lastBlock) { csd_t csd; @@ -275,26 +276,26 @@ bool Sd2Card::erase(uint32_t firstBlock, uint32_t lastBlock) { chipSelectHigh(); return false; } -//------------------------------------------------------------------------------ -/** Determine if card supports single block erase. + +/** + * 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 true if single block erase is supported. + * false if single block erase is not supported. */ bool Sd2Card::eraseSingleBlockEnable() { csd_t csd; return readCSD(&csd) ? csd.v1.erase_blk_en : false; } -//------------------------------------------------------------------------------ + /** * 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(). + * \return true for success, false for failure. + * The reason for failure can be determined by calling errorCode() and errorData(). */ bool Sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin) { errorCode_ = type_ = 0; @@ -384,14 +385,13 @@ bool Sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin) { chipSelectHigh(); return false; } -//------------------------------------------------------------------------------ + /** * Read a 512 byte block from an SD card. * * \param[in] blockNumber Logical block to be read. * \param[out] dst Pointer to the location that will receive the data. - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. + * \return true for success, false for failure. */ bool Sd2Card::readBlock(uint32_t blockNumber, uint8_t* dst) { // use address if not SDHC card @@ -399,19 +399,18 @@ bool Sd2Card::readBlock(uint32_t blockNumber, uint8_t* dst) { #if ENABLED(SD_CHECK_AND_RETRY) uint8_t retryCnt = 3; - do { - if (!cardCommand(CMD17, blockNumber)) { - if (readData(dst, 512)) return true; - } - else + for(;;) { + if (cardCommand(CMD17, blockNumber)) error(SD_CARD_ERROR_CMD17); + else if (readData(dst, 512)) + return true; if (!--retryCnt) break; chipSelectHigh(); cardCommand(CMD12, 0); // Try sending a stop command, ignore the result. errorCode_ = 0; - } while (true); + } #else if (cardCommand(CMD17, blockNumber)) error(SD_CARD_ERROR_CMD17); @@ -422,13 +421,13 @@ bool Sd2Card::readBlock(uint32_t blockNumber, uint8_t* dst) { chipSelectHigh(); return false; } -//------------------------------------------------------------------------------ -/** Read one data block in a multiple block read sequence + +/** + * Read one data block in a multiple block read sequence * * \param[in] dst Pointer to the location for the data to be read. * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. + * \return true for success, false for failure. */ bool Sd2Card::readData(uint8_t* dst) { chipSelectLow(); @@ -436,50 +435,49 @@ bool Sd2Card::readData(uint8_t* dst) { } #if ENABLED(SD_CHECK_AND_RETRY) -static const uint16_t crctab[] PROGMEM = { - 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, - 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, - 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, - 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, - 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, - 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, - 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, - 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, - 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, - 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, - 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, - 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, - 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, - 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, - 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, - 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, - 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, - 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, - 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, - 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, - 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, - 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, - 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, - 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, - 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, - 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, - 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, - 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, - 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, - 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, - 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, - 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 -}; -static uint16_t CRC_CCITT(const uint8_t* data, size_t n) { - uint16_t crc = 0; - for (size_t i = 0; i < n; i++) { - crc = pgm_read_word(&crctab[(crc >> 8 ^ data[i]) & 0xFF]) ^ (crc << 8); - } - return crc; -} -#endif + static const uint16_t crctab[] PROGMEM = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, + 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, + 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, + 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, + 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, + 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, + 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, + 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, + 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, + 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, + 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, + 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, + 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, + 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, + 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, + 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, + 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, + 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, + 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, + 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, + 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, + 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, + 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 + }; + static uint16_t CRC_CCITT(const uint8_t* data, size_t n) { + uint16_t crc = 0; + for (size_t i = 0; i < n; i++) { + crc = pgm_read_word(&crctab[(crc >> 8 ^ data[i]) & 0xFF]) ^ (crc << 8); + } + return crc; + } +#endif // SD_CHECK_AND_RETRY -//------------------------------------------------------------------------------ bool Sd2Card::readData(uint8_t* dst, uint16_t count) { // wait for start block token uint16_t t0 = millis(); @@ -521,61 +519,55 @@ bool Sd2Card::readData(uint8_t* dst, uint16_t count) { spiSend(0XFF); return false; } -//------------------------------------------------------------------------------ + /** read CID or CSR register */ bool Sd2Card::readRegister(uint8_t cmd, void* buf) { uint8_t* dst = reinterpret_cast(buf); if (cardCommand(cmd, 0)) { error(SD_CARD_ERROR_READ_REG); - goto FAIL; + chipSelectHigh(); + return false; } return readData(dst, 16); - FAIL: - chipSelectHigh(); - return false; } -//------------------------------------------------------------------------------ -/** Start a read multiple blocks sequence. + +/** + * Start a read multiple blocks sequence. * * \param[in] blockNumber Address of first block in sequence. * * \note This function is used with readData() and readStop() for optimized * multiple block reads. SPI chipSelect must be low for the entire sequence. * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. + * \return true for success, false for failure. */ bool Sd2Card::readStart(uint32_t blockNumber) { if (type() != SD_CARD_TYPE_SDHC) blockNumber <<= 9; if (cardCommand(CMD18, blockNumber)) { error(SD_CARD_ERROR_CMD18); - goto FAIL; + chipSelectHigh(); + return false; } chipSelectHigh(); return true; - FAIL: - chipSelectHigh(); - return false; } -//------------------------------------------------------------------------------ -/** End a read multiple blocks sequence. + +/** + * End a read multiple blocks sequence. * -* \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. + * \return true for success, false for failure. */ bool Sd2Card::readStop() { chipSelectLow(); if (cardCommand(CMD12, 0)) { error(SD_CARD_ERROR_CMD12); - goto FAIL; + chipSelectHigh(); + return false; } chipSelectHigh(); return true; - FAIL: - chipSelectHigh(); - return false; } -//------------------------------------------------------------------------------ + /** * Set the SPI clock rate. * @@ -596,25 +588,22 @@ bool Sd2Card::setSckRate(uint8_t sckRateID) { spiRate_ = sckRateID; return true; } -//------------------------------------------------------------------------------ + // wait for card to go not busy bool Sd2Card::waitNotBusy(uint16_t timeoutMillis) { uint16_t t0 = millis(); - while (spiRec() != 0XFF) { - if (((uint16_t)millis() - t0) >= timeoutMillis) goto FAIL; - } + while (spiRec() != 0XFF) + if (((uint16_t)millis() - t0) >= timeoutMillis) return false; + return true; - FAIL: - return false; } -//------------------------------------------------------------------------------ + /** * Writes a 512 byte block to an SD card. * * \param[in] blockNumber Logical block to be written. * \param[in] src Pointer to the location of the data to be written. - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. + * \return true for success, false for failure. */ bool Sd2Card::writeBlock(uint32_t blockNumber, const uint8_t* src) { // use address if not SDHC card @@ -641,25 +630,24 @@ bool Sd2Card::writeBlock(uint32_t blockNumber, const uint8_t* src) { chipSelectHigh(); return false; } -//------------------------------------------------------------------------------ -/** Write one data block in a multiple block write sequence + +/** + * Write one data block in a multiple block write sequence * \param[in] src Pointer to the location of the data to be written. - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. + * \return true for success, false for failure. */ bool Sd2Card::writeData(const uint8_t* src) { chipSelectLow(); // wait for previous write to finish - if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto FAIL; - if (!writeData(WRITE_MULTIPLE_TOKEN, src)) goto FAIL; + if (!waitNotBusy(SD_WRITE_TIMEOUT) || !writeData(WRITE_MULTIPLE_TOKEN, src)) { + error(SD_CARD_ERROR_WRITE_MULTIPLE); + chipSelectHigh(); + return false; + } chipSelectHigh(); return true; - FAIL: - error(SD_CARD_ERROR_WRITE_MULTIPLE); - chipSelectHigh(); - return false; } -//------------------------------------------------------------------------------ + // send one block of data for write block or write multiple blocks bool Sd2Card::writeData(uint8_t token, const uint8_t* src) { spiSendBlock(token, src); @@ -670,15 +658,14 @@ bool Sd2Card::writeData(uint8_t token, const uint8_t* src) { status_ = spiRec(); if ((status_ & DATA_RES_MASK) != DATA_RES_ACCEPTED) { error(SD_CARD_ERROR_WRITE); - goto FAIL; + chipSelectHigh(); + return false; } return true; - FAIL: - chipSelectHigh(); - return false; } -//------------------------------------------------------------------------------ -/** Start a write multiple blocks sequence. + +/** + * Start a write multiple blocks sequence. * * \param[in] blockNumber Address of first block in sequence. * \param[in] eraseCount The number of blocks to be pre-erased. @@ -686,8 +673,7 @@ bool Sd2Card::writeData(uint8_t token, const uint8_t* src) { * \note This function is used with writeData() and writeStop() * for optimized multiple block writes. * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. + * \return true for success, false for failure. */ bool Sd2Card::writeStart(uint32_t blockNumber, uint32_t eraseCount) { // send pre-erase count @@ -707,11 +693,11 @@ bool Sd2Card::writeStart(uint32_t blockNumber, uint32_t eraseCount) { chipSelectHigh(); return false; } -//------------------------------------------------------------------------------ -/** End a write multiple blocks sequence. + +/** + * End a write multiple blocks sequence. * -* \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. + * \return true for success, false for failure. */ bool Sd2Card::writeStop() { chipSelectLow(); @@ -726,4 +712,4 @@ bool Sd2Card::writeStop() { return false; } -#endif +#endif // SDSUPPORT diff --git a/Marlin/Sd2Card.h b/Marlin/Sd2Card.h index 1fbd52747..9849980a2 100644 --- a/Marlin/Sd2Card.h +++ b/Marlin/Sd2Card.h @@ -20,165 +20,119 @@ * */ +/** + * \file + * \brief Sd2Card class for V2 SD/SDHC cards + */ + /** * Arduino Sd2Card Library * Copyright (C) 2009 by William Greiman * * This file is part of the Arduino Sd2Card Library */ +#ifndef _SD2CARD_H_ +#define _SD2CARD_H_ -#include "Marlin.h" -#if ENABLED(SDSUPPORT) - -#ifndef Sd2Card_h -#define Sd2Card_h -/** - * \file - * \brief Sd2Card class for V2 SD/SDHC cards - */ #include "SdFatConfig.h" #include "SdInfo.h" -//------------------------------------------------------------------------------ + // SPI speed is F_CPU/2^(1 + index), 0 <= index <= 6 -/** Set SCK to max rate of F_CPU/2. See Sd2Card::setSckRate(). */ -uint8_t const SPI_FULL_SPEED = 0; -/** Set SCK rate to F_CPU/4. See Sd2Card::setSckRate(). */ -uint8_t const SPI_HALF_SPEED = 1; -/** Set SCK rate to F_CPU/8. See Sd2Card::setSckRate(). */ -uint8_t const SPI_QUARTER_SPEED = 2; -/** Set SCK rate to F_CPU/16. See Sd2Card::setSckRate(). */ -uint8_t const SPI_EIGHTH_SPEED = 3; -/** Set SCK rate to F_CPU/32. See Sd2Card::setSckRate(). */ -uint8_t const SPI_SIXTEENTH_SPEED = 4; -//------------------------------------------------------------------------------ -/** init timeout ms */ -uint16_t const SD_INIT_TIMEOUT = 2000; -/** erase timeout ms */ -uint16_t const SD_ERASE_TIMEOUT = 10000; -/** read timeout ms */ -uint16_t const SD_READ_TIMEOUT = 300; -/** write time out ms */ -uint16_t const SD_WRITE_TIMEOUT = 600; -//------------------------------------------------------------------------------ +uint8_t const SPI_FULL_SPEED = 0, // Set SCK to max rate of F_CPU/2. See Sd2Card::setSckRate(). + SPI_HALF_SPEED = 1, // Set SCK rate to F_CPU/4. See Sd2Card::setSckRate(). + SPI_QUARTER_SPEED = 2, // Set SCK rate to F_CPU/8. See Sd2Card::setSckRate(). + SPI_EIGHTH_SPEED = 3, // Set SCK rate to F_CPU/16. See Sd2Card::setSckRate(). + SPI_SIXTEENTH_SPEED = 4; // Set SCK rate to F_CPU/32. See Sd2Card::setSckRate(). + +uint16_t const SD_INIT_TIMEOUT = 2000, // init timeout ms + SD_ERASE_TIMEOUT = 10000, // erase timeout ms + SD_READ_TIMEOUT = 300, // read timeout ms + SD_WRITE_TIMEOUT = 600; // write time out ms + // SD card errors -/** timeout error for command CMD0 (initialize card in SPI mode) */ -uint8_t const SD_CARD_ERROR_CMD0 = 0X1; -/** CMD8 was not accepted - not a valid SD card*/ -uint8_t const SD_CARD_ERROR_CMD8 = 0X2; -/** card returned an error response for CMD12 (write stop) */ -uint8_t const SD_CARD_ERROR_CMD12 = 0X3; -/** card returned an error response for CMD17 (read block) */ -uint8_t const SD_CARD_ERROR_CMD17 = 0X4; -/** card returned an error response for CMD18 (read multiple block) */ -uint8_t const SD_CARD_ERROR_CMD18 = 0X5; -/** card returned an error response for CMD24 (write block) */ -uint8_t const SD_CARD_ERROR_CMD24 = 0X6; -/** WRITE_MULTIPLE_BLOCKS command failed */ -uint8_t const SD_CARD_ERROR_CMD25 = 0X7; -/** card returned an error response for CMD58 (read OCR) */ -uint8_t const SD_CARD_ERROR_CMD58 = 0X8; -/** SET_WR_BLK_ERASE_COUNT failed */ -uint8_t const SD_CARD_ERROR_ACMD23 = 0X9; -/** ACMD41 initialization process timeout */ -uint8_t const SD_CARD_ERROR_ACMD41 = 0XA; -/** card returned a bad CSR version field */ -uint8_t const SD_CARD_ERROR_BAD_CSD = 0XB; -/** erase block group command failed */ -uint8_t const SD_CARD_ERROR_ERASE = 0XC; -/** card not capable of single block erase */ -uint8_t const SD_CARD_ERROR_ERASE_SINGLE_BLOCK = 0XD; -/** Erase sequence timed out */ -uint8_t const SD_CARD_ERROR_ERASE_TIMEOUT = 0XE; -/** card returned an error token instead of read data */ -uint8_t const SD_CARD_ERROR_READ = 0XF; -/** read CID or CSD failed */ -uint8_t const SD_CARD_ERROR_READ_REG = 0x10; -/** timeout while waiting for start of read data */ -uint8_t const SD_CARD_ERROR_READ_TIMEOUT = 0x11; -/** card did not accept STOP_TRAN_TOKEN */ -uint8_t const SD_CARD_ERROR_STOP_TRAN = 0x12; -/** card returned an error token as a response to a write operation */ -uint8_t const SD_CARD_ERROR_WRITE = 0x13; -/** attempt to write protected block zero */ -uint8_t const SD_CARD_ERROR_WRITE_BLOCK_ZERO = 0x14; // REMOVE - not used -/** card did not go ready for a multiple block write */ -uint8_t const SD_CARD_ERROR_WRITE_MULTIPLE = 0x15; -/** card returned an error to a CMD13 status check after a write */ -uint8_t const SD_CARD_ERROR_WRITE_PROGRAMMING = 0x16; -/** timeout occurred during write programming */ -uint8_t const SD_CARD_ERROR_WRITE_TIMEOUT = 0x17; -/** incorrect rate selected */ -uint8_t const SD_CARD_ERROR_SCK_RATE = 0x18; -/** init() not called */ -uint8_t const SD_CARD_ERROR_INIT_NOT_CALLED = 0x19; -/** crc check error */ -uint8_t const SD_CARD_ERROR_CRC = 0x20; -//------------------------------------------------------------------------------ +uint8_t const SD_CARD_ERROR_CMD0 = 0X1, // timeout error for command CMD0 (initialize card in SPI mode) + SD_CARD_ERROR_CMD8 = 0X2, // CMD8 was not accepted - not a valid SD card + SD_CARD_ERROR_CMD12 = 0X3, // card returned an error response for CMD12 (write stop) + SD_CARD_ERROR_CMD17 = 0X4, // card returned an error response for CMD17 (read block) + SD_CARD_ERROR_CMD18 = 0X5, // card returned an error response for CMD18 (read multiple block) + SD_CARD_ERROR_CMD24 = 0X6, // card returned an error response for CMD24 (write block) + SD_CARD_ERROR_CMD25 = 0X7, // WRITE_MULTIPLE_BLOCKS command failed + SD_CARD_ERROR_CMD58 = 0X8, // card returned an error response for CMD58 (read OCR) + SD_CARD_ERROR_ACMD23 = 0X9, // SET_WR_BLK_ERASE_COUNT failed + SD_CARD_ERROR_ACMD41 = 0XA, // ACMD41 initialization process timeout + SD_CARD_ERROR_BAD_CSD = 0XB, // card returned a bad CSR version field + SD_CARD_ERROR_ERASE = 0XC, // erase block group command failed + SD_CARD_ERROR_ERASE_SINGLE_BLOCK = 0XD, // card not capable of single block erase + SD_CARD_ERROR_ERASE_TIMEOUT = 0XE, // Erase sequence timed out + SD_CARD_ERROR_READ = 0XF, // card returned an error token instead of read data + SD_CARD_ERROR_READ_REG = 0x10, // read CID or CSD failed + SD_CARD_ERROR_READ_TIMEOUT = 0x11, // timeout while waiting for start of read data + SD_CARD_ERROR_STOP_TRAN = 0x12, // card did not accept STOP_TRAN_TOKEN + SD_CARD_ERROR_WRITE = 0x13, // card returned an error token as a response to a write operation + SD_CARD_ERROR_WRITE_BLOCK_ZERO = 0x14, // REMOVE - not used ... attempt to write protected block zero + SD_CARD_ERROR_WRITE_MULTIPLE = 0x15, // card did not go ready for a multiple block write + SD_CARD_ERROR_WRITE_PROGRAMMING = 0x16, // card returned an error to a CMD13 status check after a write + SD_CARD_ERROR_WRITE_TIMEOUT = 0x17, // timeout occurred during write programming + SD_CARD_ERROR_SCK_RATE = 0x18, // incorrect rate selected + SD_CARD_ERROR_INIT_NOT_CALLED = 0x19, // init() not called + SD_CARD_ERROR_CRC = 0x20; // crc check error + // card types -/** Standard capacity V1 SD card */ -uint8_t const SD_CARD_TYPE_SD1 = 1; -/** Standard capacity V2 SD card */ -uint8_t const SD_CARD_TYPE_SD2 = 2; -/** High Capacity SD card */ -uint8_t const SD_CARD_TYPE_SDHC = 3; +uint8_t const SD_CARD_TYPE_SD1 = 1, // Standard capacity V1 SD card + SD_CARD_TYPE_SD2 = 2, // Standard capacity V2 SD card + SD_CARD_TYPE_SDHC = 3; // High Capacity SD card + /** * define SOFTWARE_SPI to use bit-bang SPI */ -//------------------------------------------------------------------------------ #if MEGA_SOFT_SPI #define SOFTWARE_SPI #elif USE_SOFTWARE_SPI #define SOFTWARE_SPI -#endif // MEGA_SOFT_SPI -//------------------------------------------------------------------------------ +#endif + // SPI pin definitions - do not edit here - change in SdFatConfig.h -// #if DISABLED(SOFTWARE_SPI) // hardware pin defs - /** The default chip select pin for the SD card is SS. */ - #define SD_CHIP_SELECT_PIN SS_PIN + #define SD_CHIP_SELECT_PIN SS_PIN // The default chip select pin for the SD card is SS. // The following three pins must not be redefined for hardware SPI. - /** SPI Master Out Slave In pin */ - #define SPI_MOSI_PIN MOSI_PIN - /** SPI Master In Slave Out pin */ - #define SPI_MISO_PIN MISO_PIN - /** SPI Clock pin */ - #define SPI_SCK_PIN SCK_PIN - + #define SPI_MOSI_PIN MOSI_PIN // SPI Master Out Slave In pin + #define SPI_MISO_PIN MISO_PIN // SPI Master In Slave Out pin + #define SPI_SCK_PIN SCK_PIN // SPI Clock pin #else // SOFTWARE_SPI - - /** SPI chip select pin */ - #define SD_CHIP_SELECT_PIN SOFT_SPI_CS_PIN - /** SPI Master Out Slave In pin */ - #define SPI_MOSI_PIN SOFT_SPI_MOSI_PIN - /** SPI Master In Slave Out pin */ - #define SPI_MISO_PIN SOFT_SPI_MISO_PIN - /** SPI Clock pin */ - #define SPI_SCK_PIN SOFT_SPI_SCK_PIN + #define SD_CHIP_SELECT_PIN SOFT_SPI_CS_PIN // SPI chip select pin + #define SPI_MOSI_PIN SOFT_SPI_MOSI_PIN // SPI Master Out Slave In pin + #define SPI_MISO_PIN SOFT_SPI_MISO_PIN // SPI Master In Slave Out pin + #define SPI_SCK_PIN SOFT_SPI_SCK_PIN // SPI Clock pin #endif // SOFTWARE_SPI -//------------------------------------------------------------------------------ + /** * \class Sd2Card * \brief Raw access to SD and SDHC flash memory cards. */ class Sd2Card { - public: - /** Construct an instance of Sd2Card. */ + public: + Sd2Card() : errorCode_(SD_CARD_ERROR_INIT_NOT_CALLED), type_(0) {} + uint32_t cardSize(); bool erase(uint32_t firstBlock, uint32_t lastBlock); bool eraseSingleBlockEnable(); + /** * Set SD error code. * \param[in] code value for error code. */ void error(uint8_t code) {errorCode_ = code;} + /** * \return error code for last error. See Sd2Card.h for a list of error codes. */ int errorCode() const {return errorCode_;} + /** \return error data for last error. */ int errorData() 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). @@ -188,6 +142,7 @@ class Sd2Card { bool init(uint8_t sckRateID = SPI_FULL_SPEED, uint8_t chipSelectPin = SD_CHIP_SELECT_PIN); bool readBlock(uint32_t block, uint8_t* dst); + /** * Read a card's CID register. The CID contains card identification * information such as Manufacturer ID, Product name, Product serial @@ -197,9 +152,8 @@ class Sd2Card { * * \return true for success or false for failure. */ - bool readCID(cid_t* cid) { - return readRegister(CMD10, cid); - } + bool readCID(cid_t* cid) { return readRegister(CMD10, cid); } + /** * Read a card's CSD register. The CSD contains Card-Specific Data that * provides information regarding access to the card's contents. @@ -208,14 +162,14 @@ class Sd2Card { * * \return true for success or false for failure. */ - bool readCSD(csd_t* csd) { - return readRegister(CMD9, csd); - } + bool readCSD(csd_t* csd) { return readRegister(CMD9, csd); } + bool readData(uint8_t* dst); bool readStart(uint32_t blockNumber); bool readStop(); bool setSckRate(uint8_t sckRateID); - /** Return the card type: SD V1, SD V2 or SDHC + /** + * Return the card type: SD V1, SD V2 or SDHC * \return 0 - SD V1, 1 - SD V2, or 3 - SDHC. */ int type() const {return type_;} @@ -223,13 +177,14 @@ class Sd2Card { bool writeData(const uint8_t* src); bool writeStart(uint32_t blockNumber, uint32_t eraseCount); bool writeStop(); - private: - //---------------------------------------------------------------------------- - uint8_t chipSelectPin_; - uint8_t errorCode_; - uint8_t spiRate_; - uint8_t status_; - uint8_t type_; + + private: + uint8_t chipSelectPin_, + errorCode_, + spiRate_, + status_, + type_; + // private functions uint8_t cardAcmd(uint8_t cmd, uint32_t arg) { cardCommand(CMD55, 0); @@ -241,11 +196,9 @@ class Sd2Card { bool readRegister(uint8_t cmd, void* buf); void chipSelectHigh(); void chipSelectLow(); - void type(uint8_t value) {type_ = value;} + void type(uint8_t value) { type_ = value; } bool waitNotBusy(uint16_t timeoutMillis); bool writeData(uint8_t token, const uint8_t* src); }; -#endif // Sd2Card_h - -#endif +#endif // _SD2CARD_H_ diff --git a/Marlin/SdBaseFile.cpp b/Marlin/SdBaseFile.cpp index 241f07b0f..15de6f725 100644 --- a/Marlin/SdBaseFile.cpp +++ b/Marlin/SdBaseFile.cpp @@ -27,19 +27,21 @@ * This file is part of the Arduino Sd2Card Library */ -#include "Marlin.h" +#include "MarlinConfig.h" + #if ENABLED(SDSUPPORT) #include "SdBaseFile.h" -//------------------------------------------------------------------------------ -// pointer to cwd directory -SdBaseFile* SdBaseFile::cwd_ = 0; +#include "Marlin.h" + +SdBaseFile* SdBaseFile::cwd_ = 0; // Pointer to Current Working Directory + // callback function for date/time void (*SdBaseFile::dateTime_)(uint16_t* date, uint16_t* time) = 0; -//------------------------------------------------------------------------------ + // add a cluster to a file bool SdBaseFile::addCluster() { - if (!vol_->allocContiguous(1, &curCluster_)) goto FAIL; + if (!vol_->allocContiguous(1, &curCluster_)) return false; // if first cluster of file link to directory entry if (firstCluster_ == 0) { @@ -47,20 +49,17 @@ bool SdBaseFile::addCluster() { flags_ |= F_FILE_DIR_DIRTY; } return true; - - FAIL: - return false; } -//------------------------------------------------------------------------------ + // Add a cluster to a directory file and zero the cluster. // return with first block of cluster in the cache bool SdBaseFile::addDirCluster() { uint32_t block; // max folder size - if (fileSize_ / sizeof(dir_t) >= 0xFFFF) goto FAIL; + if (fileSize_ / sizeof(dir_t) >= 0xFFFF) return false; - if (!addCluster()) goto FAIL; - if (!vol_->cacheFlush()) goto FAIL; + if (!addCluster()) return false; + if (!vol_->cacheFlush()) return false; block = vol_->clusterStartBlock(curCluster_); @@ -72,29 +71,25 @@ bool SdBaseFile::addDirCluster() { // zero rest of cluster for (uint8_t i = 1; i < vol_->blocksPerCluster_; i++) { - if (!vol_->writeBlock(block + i, vol_->cacheBuffer_.data)) goto FAIL; + if (!vol_->writeBlock(block + i, vol_->cacheBuffer_.data)) return false; } // Increase directory file size by cluster size fileSize_ += 512UL << vol_->clusterSizeShift_; return true; - FAIL: - return false; } -//------------------------------------------------------------------------------ + // cache a file's directory entry // return pointer to cached entry or null for failure dir_t* SdBaseFile::cacheDirEntry(uint8_t action) { - if (!vol_->cacheRawBlock(dirBlock_, action)) goto FAIL; + if (!vol_->cacheRawBlock(dirBlock_, action)) return NULL; return vol_->cache()->dir + dirIndex_; - FAIL: - return 0; } -//------------------------------------------------------------------------------ -/** Close a file and force cached data and directory information + +/** + * 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. + * \return true for success, false for failure. * Reasons for failure include no file is open or an I/O error. */ bool SdBaseFile::close() { @@ -102,41 +97,40 @@ bool SdBaseFile::close() { type_ = FAT_FILE_TYPE_CLOSED; return rtn; } -//------------------------------------------------------------------------------ -/** Check for contiguous file and return its raw block range. + +/** + * 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. + * \return true for success, false for failure. * Reasons for failure include file is not contiguous, file has zero length * or an I/O error occurred. */ bool SdBaseFile::contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock) { // error if no blocks - if (firstCluster_ == 0) goto FAIL; + if (firstCluster_ == 0) return false; for (uint32_t c = firstCluster_; ; c++) { uint32_t next; - if (!vol_->fatGet(c, &next)) goto FAIL; + if (!vol_->fatGet(c, &next)) return false; // check for contiguous if (next != (c + 1)) { // error if not end of chain - if (!vol_->isEOC(next)) goto FAIL; + if (!vol_->isEOC(next)) return false; *bgnBlock = vol_->clusterStartBlock(firstCluster_); *endBlock = vol_->clusterStartBlock(c) + vol_->blocksPerCluster_ - 1; return true; } } - - FAIL: return false; } -//------------------------------------------------------------------------------ -/** Create and open a new contiguous file of a specified size. + +/** + * 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. @@ -145,20 +139,18 @@ bool SdBaseFile::contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock) { * \param[in] path A path with a valid DOS 8.3 file name. * \param[in] size The desired file size. * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. + * \return true for success, false for failure. * Reasons for failure include \a path contains * an invalid DOS 8.3 file name, the FAT volume has not been initialized, * a file is already open, the file already exists, the root * directory is full or an I/O error. * */ -bool SdBaseFile::createContiguous(SdBaseFile* dirFile, - const char* path, uint32_t size) { +bool SdBaseFile::createContiguous(SdBaseFile* dirFile, const char* path, uint32_t size) { uint32_t count; // don't allow zero length file - if (size == 0) goto FAIL; - if (!open(dirFile, path, O_CREAT | O_EXCL | O_RDWR)) goto FAIL; + if (size == 0) return false; + if (!open(dirFile, path, O_CREAT | O_EXCL | O_RDWR)) return false; // calculate number of clusters needed count = ((size - 1) >> (vol_->clusterSizeShift_ + 9)) + 1; @@ -166,7 +158,7 @@ bool SdBaseFile::createContiguous(SdBaseFile* dirFile, // allocate clusters if (!vol_->allocContiguous(count, &firstCluster_)) { remove(); - goto FAIL; + return false; } fileSize_ = size; @@ -174,34 +166,31 @@ bool SdBaseFile::createContiguous(SdBaseFile* dirFile, flags_ |= F_FILE_DIR_DIRTY; return sync(); - FAIL: - return false; } -//------------------------------------------------------------------------------ -/** Return a file's directory entry. + +/** + * Return a file's directory entry. * * \param[out] dir Location for return of the file's directory entry. * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. + * \return true for success, false for failure. */ bool SdBaseFile::dirEntry(dir_t* dir) { dir_t* p; // make sure fields on SD are correct - if (!sync()) goto FAIL; + if (!sync()) return false; // read entry p = cacheDirEntry(SdVolume::CACHE_FOR_READ); - if (!p) goto FAIL; + if (!p) return false; // copy to caller's struct memcpy(dir, p, sizeof(dir_t)); return true; - FAIL: - return false; } -//------------------------------------------------------------------------------ -/** Format the name field of \a dir into the 13 byte array + +/** + * 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. @@ -216,8 +205,9 @@ void SdBaseFile::dirName(const dir_t& dir, char* name) { } name[j] = 0; } -//------------------------------------------------------------------------------ -/** Test for the existence of a file in a directory + +/** + * Test for the existence of a file in a directory * * \param[in] name Name of the file to be tested for. * @@ -232,7 +222,7 @@ bool SdBaseFile::exists(const char* name) { SdBaseFile file; return file.open(this, name, O_READ); } -//------------------------------------------------------------------------------ + /** * Get a string from a file. * @@ -275,15 +265,15 @@ int16_t SdBaseFile::fgets(char* str, int16_t num, char* delim) { str[n] = '\0'; return n; } -//------------------------------------------------------------------------------ -/** Get a file's name + +/** + * Get a file's name * * \param[out] name An array of 13 characters for the file's name. * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. + * \return true for success, false for failure. */ -bool SdBaseFile::getFilename(char* name) { +bool SdBaseFile::getFilename(char * const name) { if (!isOpen()) return false; if (isRoot()) { @@ -299,14 +289,14 @@ bool SdBaseFile::getFilename(char* name) { dirName(*p, name); return true; } -//------------------------------------------------------------------------------ + void SdBaseFile::getpos(filepos_t* pos) { pos->position = curPosition_; pos->cluster = curCluster_; } -//------------------------------------------------------------------------------ -/** List directory contents. +/** + * List directory contents. * * \param[in] pr Print stream for list. * @@ -333,7 +323,7 @@ void SdBaseFile::ls(uint8_t flags, uint8_t indent) { } } } -//------------------------------------------------------------------------------ + // saves 32 bytes on stack for ls recursion // return 0 - EOF, 1 - normal file, or 2 - directory int8_t SdBaseFile::lsPrintNext(uint8_t flags, uint8_t indent) { @@ -383,41 +373,33 @@ int8_t SdBaseFile::lsPrintNext(uint8_t flags, uint8_t indent) { MYSERIAL.println(); return DIR_IS_FILE(&dir) ? 1 : 2; } -//------------------------------------------------------------------------------ -// format directory name field from a 8.3 name string + +// Format directory name field from a 8.3 name string bool SdBaseFile::make83Name(const char* str, uint8_t* name, const char** ptr) { - uint8_t c; - uint8_t n = 7; // max index for part before dot - uint8_t i = 0; - // blank fill name and extension - while (i < 11) name[i++] = ' '; - i = 0; - while (*str != '\0' && *str != '/') { - c = *str++; - if (c == '.') { - if (n == 10) goto FAIL; // only one dot allowed - n = 10; // max index for full 8.3 name - i = 8; // place for extension + uint8_t n = 7, // Max index until a dot is found + i = 11; + while (i) name[--i] = ' '; // Set whole FILENAME.EXT to spaces + while (*str && *str != '/') { // For each character, until nul or '/' + uint8_t c = *str++; // Get char and advance + if (c == '.') { // For a dot... + if (n == 10) return false; // Already moved the max index? fail! + n = 10; // Move the max index for full 8.3 name + i = 8; // Move up to the extension place } else { - // illegal FAT characters + // Fail for illegal characters PGM_P p = PSTR("|<>^+=?/[];,*\"\\"); - uint8_t b; - while ((b = pgm_read_byte(p++))) if (b == c) goto FAIL; - // check size and only allow ASCII printable characters - if (i > n || c < 0x21 || c == 0x7F) goto FAIL; - // only upper case allowed in 8.3 names - convert lower to upper - name[i++] = (c < 'a' || c > 'z') ? (c) : (c + ('A' - 'a')); + while (uint8_t b = pgm_read_byte(p++)) if (b == c) return false; + if (i > n || c < 0x21 || c == 0x7F) return false; // Check size, non-printable characters + name[i++] = (c < 'a' || c > 'z') ? (c) : (c + ('A' - 'a')); // Uppercase required for 8.3 name } } - *ptr = str; - // must have a file name, extension is optional - return name[0] != ' '; - FAIL: - return false; + *ptr = str; // Set passed pointer to the end + return name[0] != ' '; // Return true if any name was set } -//------------------------------------------------------------------------------ -/** Make a new directory. + +/** + * Make a new directory. * * \param[in] parent An open SdFat instance for the directory that will contain * the new directory. @@ -426,8 +408,7 @@ bool SdBaseFile::make83Name(const char* str, uint8_t* name, const char** ptr) { * * \param[in] pFlag Create missing parent directories if true. * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. + * \return true for success, false for failure. * Reasons for failure include this file is already open, \a parent is not a * directory, \a path is invalid or already exists in \a parent. */ @@ -437,56 +418,53 @@ bool SdBaseFile::mkdir(SdBaseFile* parent, const char* path, bool pFlag) { SdBaseFile* sub = &dir1; SdBaseFile* start = parent; - if (!parent || isOpen()) goto FAIL; + if (!parent || isOpen()) return false; if (*path == '/') { while (*path == '/') path++; if (!parent->isRoot()) { - if (!dir2.openRoot(parent->vol_)) goto FAIL; + if (!dir2.openRoot(parent->vol_)) return false; parent = &dir2; } } while (1) { - if (!make83Name(path, dname, &path)) goto FAIL; + if (!make83Name(path, dname, &path)) return false; while (*path == '/') path++; if (!*path) break; if (!sub->open(parent, dname, O_READ)) { - if (!pFlag || !sub->mkdir(parent, dname)) { - goto FAIL; - } + if (!pFlag || !sub->mkdir(parent, dname)) + return false; } if (parent != start) parent->close(); parent = sub; sub = parent != &dir1 ? &dir1 : &dir2; } return mkdir(parent, dname); - FAIL: - return false; } -//------------------------------------------------------------------------------ + bool SdBaseFile::mkdir(SdBaseFile* parent, const uint8_t dname[11]) { uint32_t block; dir_t d; dir_t* p; - if (!parent->isDir()) goto FAIL; + if (!parent->isDir()) return false; // create a normal file - if (!open(parent, dname, O_CREAT | O_EXCL | O_RDWR)) goto FAIL; + if (!open(parent, dname, O_CREAT | O_EXCL | O_RDWR)) return false; // convert file to directory flags_ = O_READ; type_ = FAT_FILE_TYPE_SUBDIR; // allocate and zero first cluster - if (!addDirCluster())goto FAIL; + if (!addDirCluster()) return false; // force entry to SD - if (!sync()) goto FAIL; + if (!sync()) return false; // cache entry - should already be in cache due to sync() call p = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); - if (!p) goto FAIL; + if (!p) return false; // change directory entry attribute p->attributes = DIR_ATT_DIRECTORY; @@ -498,7 +476,7 @@ bool SdBaseFile::mkdir(SdBaseFile* parent, const uint8_t dname[11]) { // cache block for '.' and '..' block = vol_->clusterStartBlock(firstCluster_); - if (!vol_->cacheRawBlock(block, SdVolume::CACHE_FOR_WRITE)) goto FAIL; + if (!vol_->cacheRawBlock(block, SdVolume::CACHE_FOR_WRITE)) return false; // copy '.' to block memcpy(&vol_->cache()->dir[0], &d, sizeof(d)); @@ -518,25 +496,24 @@ bool SdBaseFile::mkdir(SdBaseFile* parent, const uint8_t dname[11]) { // write first block return vol_->cacheFlush(); - FAIL: - return false; } -//------------------------------------------------------------------------------ -/** Open a file in the current working directory. + +/** + * Open a file in the current working directory. * * \param[in] path A path with a valid 8.3 DOS name for a file to be opened. * * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive * OR of open flags. see SdBaseFile::open(SdBaseFile*, const char*, uint8_t). * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. + * \return true for success, false for failure. */ bool SdBaseFile::open(const char* path, uint8_t oflag) { return open(cwd_, path, oflag); } -//------------------------------------------------------------------------------ -/** Open a file or directory by name. + +/** + * Open a file or directory by name. * * \param[in] dirFile An open SdFat instance for the directory containing the * file to be opened. @@ -580,8 +557,7 @@ bool SdBaseFile::open(const char* path, uint8_t oflag) { * \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. + * \return true for success, false for failure. * Reasons for failure include this file is already open, \a dirFile is not * a directory, \a path is invalid, the file does not exist * or can't be opened in the access mode specified by oflag. @@ -589,40 +565,33 @@ bool SdBaseFile::open(const char* path, uint8_t oflag) { bool SdBaseFile::open(SdBaseFile* dirFile, const char* path, uint8_t oflag) { uint8_t dname[11]; SdBaseFile dir1, dir2; - SdBaseFile* parent = dirFile; - SdBaseFile* sub = &dir1; + SdBaseFile *parent = dirFile, *sub = &dir1; - if (!dirFile) goto FAIL; + if (!dirFile || isOpen()) return false; - // error if already open - if (isOpen()) goto FAIL; - - if (*path == '/') { - while (*path == '/') path++; - if (!dirFile->isRoot()) { - if (!dir2.openRoot(dirFile->vol_)) goto FAIL; - parent = &dir2; + if (*path == '/') { // Path starts with '/' + if (!dirFile->isRoot()) { // Is the passed dirFile the root? + if (!dir2.openRoot(dirFile->vol_)) return false; // Get the root in dir2, if possible + parent = &dir2; // Change 'parent' to point at the root dir } + while (*path == '/') path++; // Skip all leading slashes } - while (1) { - if (!make83Name(path, dname, &path)) goto FAIL; + + for (;;) { + if (!make83Name(path, dname, &path)) return false; while (*path == '/') path++; if (!*path) break; - if (!sub->open(parent, dname, O_READ)) goto FAIL; + if (!sub->open(parent, dname, O_READ)) return false; if (parent != dirFile) parent->close(); parent = sub; sub = parent != &dir1 ? &dir1 : &dir2; } return open(parent, dname, oflag); - FAIL: - return false; } -//------------------------------------------------------------------------------ + // open with filename in dname -bool SdBaseFile::open(SdBaseFile* dirFile, - const uint8_t dname[11], uint8_t oflag) { - bool emptyFound = false; - bool fileFound = false; +bool SdBaseFile::open(SdBaseFile* dirFile, const uint8_t dname[11], uint8_t oflag) { + bool emptyFound = false, fileFound = false; uint8_t index; dir_t* p; @@ -634,7 +603,7 @@ bool SdBaseFile::open(SdBaseFile* dirFile, while (dirFile->curPosition_ < dirFile->fileSize_) { index = 0XF & (dirFile->curPosition_ >> 5); p = dirFile->readDirCache(); - if (!p) goto FAIL; + if (!p) return false; if (p->name[0] == DIR_NAME_FREE || p->name[0] == DIR_NAME_DELETED) { // remember first empty slot @@ -653,21 +622,21 @@ bool SdBaseFile::open(SdBaseFile* dirFile, } if (fileFound) { // don't open existing file if O_EXCL - if (oflag & O_EXCL) goto FAIL; + if (oflag & O_EXCL) return false; } else { // don't create unless O_CREAT and O_WRITE - if (!(oflag & O_CREAT) || !(oflag & O_WRITE)) goto FAIL; + if (!(oflag & O_CREAT) || !(oflag & O_WRITE)) return false; if (emptyFound) { index = dirIndex_; p = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); - if (!p) goto FAIL; + if (!p) return false; } else { - if (dirFile->type_ == FAT_FILE_TYPE_ROOT_FIXED) goto FAIL; + if (dirFile->type_ == FAT_FILE_TYPE_ROOT_FIXED) return false; // add and zero cluster for dirFile - first cluster is in cache for write - if (!dirFile->addDirCluster()) goto FAIL; + if (!dirFile->addDirCluster()) return false; // use first entry in cluster p = dirFile->vol_->cache()->dir; @@ -692,15 +661,14 @@ bool SdBaseFile::open(SdBaseFile* dirFile, p->lastWriteTime = p->creationTime; // write entry to SD - if (!dirFile->vol_->cacheFlush()) goto FAIL; + if (!dirFile->vol_->cacheFlush()) return false; } // open entry in cache return openCachedEntry(index, oflag); - FAIL: - return false; } -//------------------------------------------------------------------------------ -/** Open a file by index. + +/** + * Open a file by index. * * \param[in] dirFile An open SdFat instance for the directory. * @@ -719,29 +687,27 @@ bool SdBaseFile::open(SdBaseFile* dirFile, uint16_t index, uint8_t oflag) { vol_ = dirFile->vol_; // error if already open - if (isOpen() || !dirFile) goto FAIL; + if (isOpen() || !dirFile) return false; // don't open existing file if O_EXCL - user call error - if (oflag & O_EXCL) goto FAIL; + if (oflag & O_EXCL) return false; // seek to location of entry - if (!dirFile->seekSet(32 * index)) goto FAIL; + if (!dirFile->seekSet(32 * index)) return false; // read entry into cache p = dirFile->readDirCache(); - if (!p) goto FAIL; + if (!p) return false; // error if empty slot or '.' or '..' if (p->name[0] == DIR_NAME_FREE || p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') { - goto FAIL; + return false; } // open cached entry return openCachedEntry(index & 0XF, oflag); - FAIL: - return false; } -//------------------------------------------------------------------------------ + // open a cached directory entry. Assumes vol_ is initialized bool SdBaseFile::openCachedEntry(uint8_t dirIndex, uint8_t oflag) { // location of entry in cache @@ -768,9 +734,9 @@ bool SdBaseFile::openCachedEntry(uint8_t dirIndex, uint8_t oflag) { if (!vol_->chainSize(firstCluster_, &fileSize_)) goto FAIL; type_ = FAT_FILE_TYPE_SUBDIR; } - else { + else goto FAIL; - } + // save open flags for read/write flags_ = oflag & F_OFLAG; @@ -779,12 +745,14 @@ bool SdBaseFile::openCachedEntry(uint8_t dirIndex, uint8_t oflag) { curPosition_ = 0; if ((oflag & O_TRUNC) && !truncate(0)) return false; return oflag & O_AT_END ? seekEnd(0) : true; + FAIL: type_ = FAT_FILE_TYPE_CLOSED; return false; } -//------------------------------------------------------------------------------ -/** Open the next file or subdirectory in a directory. + +/** + * Open the next file or subdirectory in a directory. * * \param[in] dirFile An open SdFat instance for the directory containing the * file to be opened. @@ -799,10 +767,10 @@ bool SdBaseFile::openNext(SdBaseFile* dirFile, uint8_t oflag) { dir_t* p; uint8_t index; - if (!dirFile) goto FAIL; + if (!dirFile) return false; // error if already open - if (isOpen()) goto FAIL; + if (isOpen()) return false; vol_ = dirFile->vol_; @@ -811,10 +779,10 @@ bool SdBaseFile::openNext(SdBaseFile* dirFile, uint8_t oflag) { // read entry into cache p = dirFile->readDirCache(); - if (!p) goto FAIL; + if (!p) return false; // done if last entry - if (p->name[0] == DIR_NAME_FREE) goto FAIL; + if (p->name[0] == DIR_NAME_FREE) return false; // skip empty slot or '.' or '..' if (p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') { @@ -825,16 +793,16 @@ bool SdBaseFile::openNext(SdBaseFile* dirFile, uint8_t oflag) { return openCachedEntry(index, oflag); } } - FAIL: return false; } -//------------------------------------------------------------------------------ -/** Open a directory's parent directory. + +#if 0 +/** + * Open a directory's parent directory. * * \param[in] dir Parent of this directory will be opened. Must not be root. * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. + * \return true for success, false for failure. */ bool SdBaseFile::openParent(SdBaseFile* dir) { dir_t entry; @@ -844,14 +812,14 @@ bool SdBaseFile::openParent(SdBaseFile* dir) { uint32_t cluster; uint32_t lbn; // error if already open or dir is root or dir is not a directory - if (isOpen() || !dir || dir->isRoot() || !dir->isDir()) goto FAIL; + if (isOpen() || !dir || dir->isRoot() || !dir->isDir()) return false; vol_ = dir->vol_; // position to '..' - if (!dir->seekSet(32)) goto FAIL; + if (!dir->seekSet(32)) return false; // read '..' entry - if (dir->read(&entry, sizeof(entry)) != 32) goto FAIL; + if (dir->read(&entry, sizeof(entry)) != 32) return false; // verify it is '..' - if (entry.name[0] != '.' || entry.name[1] != '.') goto FAIL; + if (entry.name[0] != '.' || entry.name[1] != '.') return false; // start cluster for '..' cluster = entry.firstClusterLow; cluster |= (uint32_t)entry.firstClusterHigh << 16; @@ -859,43 +827,42 @@ bool SdBaseFile::openParent(SdBaseFile* dir) { // start block for '..' lbn = vol_->clusterStartBlock(cluster); // first block of parent dir - if (!vol_->cacheRawBlock(lbn, SdVolume::CACHE_FOR_READ)) { - goto FAIL; - } + if (!vol_->cacheRawBlock(lbn, SdVolume::CACHE_FOR_READ)) return false; + p = &vol_->cacheBuffer_.dir[1]; // verify name for '../..' - if (p->name[0] != '.' || p->name[1] != '.') goto FAIL; + if (p->name[0] != '.' || p->name[1] != '.') return false; // '..' is pointer to first cluster of parent. open '../..' to find parent if (p->firstClusterHigh == 0 && p->firstClusterLow == 0) { - if (!file.openRoot(dir->volume())) goto FAIL; - } - else if (!file.openCachedEntry(1, O_READ)) { - goto FAIL; + if (!file.openRoot(dir->volume())) return false; } + else if (!file.openCachedEntry(1, O_READ)) + return false; + // search for parent in '../..' do { - if (file.readDir(&entry, NULL) != 32) goto FAIL; + if (file.readDir(&entry, NULL) != 32) return false; c = entry.firstClusterLow; c |= (uint32_t)entry.firstClusterHigh << 16; } while (c != cluster); + // open parent return open(&file, file.curPosition() / 32 - 1, O_READ); - FAIL: - return false; } -//------------------------------------------------------------------------------ -/** Open a volume's root directory. +#endif + +/** + * 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. + * \return true for success, false for failure. * Reasons for failure include the file is already open, the FAT volume has * not been initialized or it a FAT12 volume. */ bool SdBaseFile::openRoot(SdVolume* vol) { // error if file is already open - if (isOpen()) goto FAIL; + if (isOpen()) return false; if (vol->fatType() == 16 || (FAT12_SUPPORT && vol->fatType() == 12)) { type_ = FAT_FILE_TYPE_ROOT_FIXED; @@ -905,29 +872,25 @@ bool SdBaseFile::openRoot(SdVolume* vol) { else if (vol->fatType() == 32) { type_ = FAT_FILE_TYPE_ROOT32; firstCluster_ = vol->rootDirStart(); - if (!vol->chainSize(firstCluster_, &fileSize_)) goto FAIL; + if (!vol->chainSize(firstCluster_, &fileSize_)) return false; } - else { - // volume is not initialized, invalid, or FAT12 without support + else // volume is not initialized, invalid, or FAT12 without support return false; - } + vol_ = vol; // read only flags_ = O_READ; // set to start of file - curCluster_ = 0; - curPosition_ = 0; + curCluster_ = curPosition_ = 0; // root has no directory entry - dirBlock_ = 0; - dirIndex_ = 0; + dirBlock_ = dirIndex_ = 0; return true; - FAIL: - return false; } -//------------------------------------------------------------------------------ -/** Return the next available byte without consuming it. + +/** + * Return the next available byte without consuming it. * * \return The byte if no error and not at eof else -1; */ @@ -939,50 +902,24 @@ int SdBaseFile::peek() { return c; } -//------------------------------------------------------------------------------ -/** %Print the name field of a directory entry in 8.3 format. - * \param[in] pr Print stream for output. - * \param[in] dir The directory structure containing the name. - * \param[in] width Blank fill name if length is less than \a width. - * \param[in] printSlash Print '/' after directory names if true. - */ -void SdBaseFile::printDirName(const dir_t& dir, - uint8_t width, bool printSlash) { - uint8_t w = 0; - for (uint8_t i = 0; i < 11; i++) { - if (dir.name[i] == ' ')continue; - if (i == 8) { - MYSERIAL.write('.'); - w++; - } - MYSERIAL.write(dir.name[i]); - w++; - } - if (DIR_IS_SUBDIR(&dir) && printSlash) { - MYSERIAL.write('/'); - w++; - } - while (w < width) { - MYSERIAL.write(' '); - w++; - } -} -//------------------------------------------------------------------------------ + // print uint8_t with width 2 static void print2u(uint8_t v) { if (v < 10) MYSERIAL.write('0'); MYSERIAL.print(v, DEC); } -//------------------------------------------------------------------------------ -/** %Print a directory date field to Serial. + +/** + * %Print a directory date field to Serial. * * Format is yyyy-mm-dd. * * \param[in] fatDate The date field from a directory entry. */ -//------------------------------------------------------------------------------ -/** %Print a directory date field. + +/** + * %Print a directory date field. * * Format is yyyy-mm-dd. * @@ -997,8 +934,9 @@ void SdBaseFile::printFatDate(uint16_t fatDate) { print2u(FAT_DAY(fatDate)); } -//------------------------------------------------------------------------------ -/** %Print a directory time field. + +/** + * %Print a directory time field. * * Format is hh:mm:ss. * @@ -1012,11 +950,11 @@ void SdBaseFile::printFatTime(uint16_t fatTime) { MYSERIAL.write(':'); print2u(FAT_SECOND(fatTime)); } -//------------------------------------------------------------------------------ -/** Print a file's name to Serial + +/** + * Print a file's name to Serial * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. + * \return true for success, false for failure. */ bool SdBaseFile::printName() { char name[FILENAME_LENGTH]; @@ -1024,8 +962,9 @@ bool SdBaseFile::printName() { MYSERIAL.print(name); return true; } -//------------------------------------------------------------------------------ -/** Read the next byte from a file. + +/** + * 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. @@ -1034,8 +973,9 @@ int16_t SdBaseFile::read() { uint8_t b; return read(&b, 1) == 1 ? b : -1; } -//------------------------------------------------------------------------------ -/** Read data from a file starting at the current position. + +/** + * Read data from a file starting at the current position. * * \param[out] buf Pointer to the location that will receive the data. * @@ -1050,12 +990,11 @@ int16_t SdBaseFile::read() { */ int16_t SdBaseFile::read(void* buf, uint16_t nbyte) { uint8_t* dst = reinterpret_cast(buf); - uint16_t offset; - uint16_t toRead; + uint16_t offset, toRead; uint32_t block; // raw device block number // error if not open or write only - if (!isOpen() || !(flags_ & O_READ)) goto FAIL; + if (!isOpen() || !(flags_ & O_READ)) return -1; // max bytes left in file NOMORE(nbyte, fileSize_ - curPosition_); @@ -1071,14 +1010,10 @@ int16_t SdBaseFile::read(void* buf, uint16_t nbyte) { uint8_t blockOfCluster = vol_->blockOfCluster(curPosition_); if (offset == 0 && blockOfCluster == 0) { // start of new cluster - if (curPosition_ == 0) { - // use first cluster in file - curCluster_ = firstCluster_; - } - else { - // get next cluster from FAT - if (!vol_->fatGet(curCluster_, &curCluster_)) goto FAIL; - } + if (curPosition_ == 0) + curCluster_ = firstCluster_; // use first cluster in file + else if (!vol_->fatGet(curCluster_, &curCluster_)) // get next cluster from FAT + return -1; } block = vol_->clusterStartBlock(curCluster_) + blockOfCluster; } @@ -1089,11 +1024,11 @@ int16_t SdBaseFile::read(void* buf, uint16_t nbyte) { // no buffering needed if n == 512 if (n == 512 && block != vol_->cacheBlockNumber()) { - if (!vol_->readBlock(block, dst)) goto FAIL; + if (!vol_->readBlock(block, dst)) return -1; } else { // read block to cache and copy data to caller - if (!vol_->cacheRawBlock(block, SdVolume::CACHE_FOR_READ)) goto FAIL; + if (!vol_->cacheRawBlock(block, SdVolume::CACHE_FOR_READ)) return -1; uint8_t* src = vol_->cache()->data + offset; memcpy(dst, src, n); } @@ -1102,8 +1037,6 @@ int16_t SdBaseFile::read(void* buf, uint16_t nbyte) { toRead -= n; } return nbyte; - FAIL: - return -1; } /** @@ -1155,30 +1088,29 @@ int8_t SdBaseFile::readDir(dir_t* dir, char* longFilename) { } } -//------------------------------------------------------------------------------ + // Read next directory entry into the cache // Assumes file is correctly positioned dir_t* SdBaseFile::readDirCache() { uint8_t i; // error if not directory - if (!isDir()) goto FAIL; + if (!isDir()) return 0; // index of entry in cache i = (curPosition_ >> 5) & 0XF; // use read to locate and cache block - if (read() < 0) goto FAIL; + if (read() < 0) return 0; // advance to next entry curPosition_ += 31; // return pointer to entry return vol_->cache()->dir + i; - FAIL: - return 0; } -//------------------------------------------------------------------------------ -/** Remove a file. + +/** + * Remove a file. * * The directory entry and all data for the file are deleted. * @@ -1186,19 +1118,18 @@ dir_t* SdBaseFile::readDirCache() { * 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. + * \return true for success, false for failure. * Reasons for failure include the file read-only, is a directory, * or an I/O error occurred. */ bool SdBaseFile::remove() { dir_t* d; // free any clusters - will fail if read-only or directory - if (!truncate(0)) goto FAIL; + if (!truncate(0)) return false; // cache directory entry d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); - if (!d) goto FAIL; + if (!d) return false; // mark entry deleted d->name[0] = DIR_NAME_DELETED; @@ -1209,11 +1140,10 @@ bool SdBaseFile::remove() { // write entry to SD return vol_->cacheFlush(); return true; - FAIL: - return false; } -//------------------------------------------------------------------------------ -/** Remove a file. + +/** + * Remove a file. * * The directory entry and all data for the file are deleted. * @@ -1224,28 +1154,23 @@ bool SdBaseFile::remove() { * 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. + * \return true for success, false for failure. * Reasons for failure include the file is a directory, is read only, * \a dirFile is not a directory, \a path is not found * or an I/O error occurred. */ bool SdBaseFile::remove(SdBaseFile* dirFile, const char* path) { SdBaseFile file; - if (!file.open(dirFile, path, O_WRITE)) goto FAIL; - return file.remove(); - FAIL: - // can't set iostate - static function - return false; + return file.open(dirFile, path, O_WRITE) ? file.remove() : false; } -//------------------------------------------------------------------------------ -/** Rename a file or subdirectory. + +/** + * Rename a file or subdirectory. * * \param[in] dirFile Directory for the new path. * \param[in] newPath New path name for the file/directory. * - * \return The value one, true, is returned for success and - * the value zero, false, is returned for failure. + * \return true for success, false for failure. * Reasons for failure include \a dirFile is not open or is not a directory * file, newPath is invalid or already exists, or an I/O error occurs. */ @@ -1256,15 +1181,15 @@ bool SdBaseFile::rename(SdBaseFile* dirFile, const char* newPath) { dir_t* d; // must be an open file or subdirectory - if (!(isFile() || isSubDir())) goto FAIL; + if (!(isFile() || isSubDir())) return false; // can't move file - if (vol_ != dirFile->vol_) goto FAIL; + if (vol_ != dirFile->vol_) return false; // sync() and cache directory entry sync(); d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); - if (!d) goto FAIL; + if (!d) return false; // save directory entry memcpy(&entry, d, sizeof(entry)); @@ -1295,7 +1220,7 @@ bool SdBaseFile::rename(SdBaseFile* dirFile, const char* newPath) { // cache new directory entry d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); - if (!d) goto FAIL; + if (!d) return false; // copy all but name field to new directory entry memcpy(&d->attributes, &entry.attributes, sizeof(entry) - sizeof(d->name)); @@ -1304,31 +1229,30 @@ bool SdBaseFile::rename(SdBaseFile* dirFile, const char* newPath) { if (dirCluster) { // get new dot dot uint32_t block = vol_->clusterStartBlock(dirCluster); - if (!vol_->cacheRawBlock(block, SdVolume::CACHE_FOR_READ)) goto FAIL; + if (!vol_->cacheRawBlock(block, SdVolume::CACHE_FOR_READ)) return false; memcpy(&entry, &vol_->cache()->dir[1], sizeof(entry)); // free unused cluster - if (!vol_->freeChain(dirCluster)) goto FAIL; + if (!vol_->freeChain(dirCluster)) return false; // store new dot dot block = vol_->clusterStartBlock(firstCluster_); - if (!vol_->cacheRawBlock(block, SdVolume::CACHE_FOR_WRITE)) goto FAIL; + if (!vol_->cacheRawBlock(block, SdVolume::CACHE_FOR_WRITE)) return false; memcpy(&vol_->cache()->dir[1], &entry, sizeof(entry)); } return vol_->cacheFlush(); restore: - d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); - if (!d) goto FAIL; - // restore entry - d->name[0] = entry.name[0]; - vol_->cacheFlush(); - - FAIL: + if ((d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE))) { + // restore entry + d->name[0] = entry.name[0]; + vol_->cacheFlush(); + } return false; } -//------------------------------------------------------------------------------ -/** Remove a directory file. + +/** + * 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 @@ -1338,37 +1262,35 @@ restore: * 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. + * \return true for success, false for failure. * Reasons for failure include the file is not a directory, is the root * directory, is not empty, or an I/O error occurred. */ bool SdBaseFile::rmdir() { // must be open subdirectory - if (!isSubDir()) goto FAIL; + if (!isSubDir()) return false; rewind(); // make sure directory is empty while (curPosition_ < fileSize_) { dir_t* p = readDirCache(); - if (!p) goto FAIL; + if (!p) return false; // done if past last used entry if (p->name[0] == DIR_NAME_FREE) break; // skip empty slot, '.' or '..' if (p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') continue; // error not empty - if (DIR_IS_FILE_OR_SUBDIR(p)) goto FAIL; + if (DIR_IS_FILE_OR_SUBDIR(p)) return false; } // convert empty directory to normal file for remove type_ = FAT_FILE_TYPE_NORMAL; flags_ |= O_WRITE; return remove(); - FAIL: - return false; } -//------------------------------------------------------------------------------ -/** Recursively delete a directory and all contained files. + +/** + * 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. @@ -1380,8 +1302,7 @@ bool SdBaseFile::rmdir() { * \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. + * \return true for success, false for failure. */ bool SdBaseFile::rmRfStar() { uint32_t index; @@ -1392,7 +1313,7 @@ bool SdBaseFile::rmRfStar() { index = curPosition_ / 32; dir_t* p = readDirCache(); - if (!p) goto FAIL; + if (!p) return false; // done if past last entry if (p->name[0] == DIR_NAME_FREE) break; @@ -1403,31 +1324,30 @@ bool SdBaseFile::rmRfStar() { // skip if part of long file name or volume label in root if (!DIR_IS_FILE_OR_SUBDIR(p)) continue; - if (!f.open(this, index, O_READ)) goto FAIL; + if (!f.open(this, index, O_READ)) return false; if (f.isSubDir()) { // recursively delete - if (!f.rmRfStar()) goto FAIL; + if (!f.rmRfStar()) return false; } else { // ignore read-only f.flags_ |= O_WRITE; - if (!f.remove()) goto FAIL; + if (!f.remove()) return false; } // position to next entry if required if (curPosition_ != (32 * (index + 1))) { - if (!seekSet(32 * (index + 1))) goto FAIL; + if (!seekSet(32 * (index + 1))) return false; } } // don't try to delete root if (!isRoot()) { - if (!rmdir()) goto FAIL; + if (!rmdir()) return false; } return true; - FAIL: - return false; } -//------------------------------------------------------------------------------ -/** Create a file object and open it in the current working directory. + +/** + * Create a file object and open it in the current working directory. * * \param[in] path A path with a valid 8.3 DOS name for a file to be opened. * @@ -1439,64 +1359,54 @@ SdBaseFile::SdBaseFile(const char* path, uint8_t oflag) { writeError = false; open(path, oflag); } -//------------------------------------------------------------------------------ -/** Sets a file's position. + +/** + * 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. + * \return true for success, false for failure. */ -bool SdBaseFile::seekSet(uint32_t pos) { - uint32_t nCur; - uint32_t nNew; +bool SdBaseFile::seekSet(const uint32_t pos) { + uint32_t nCur, nNew; // error if file not open or seek past end of file - if (!isOpen() || pos > fileSize_) goto FAIL; + if (!isOpen() || pos > fileSize_) return false; if (type_ == FAT_FILE_TYPE_ROOT_FIXED) { curPosition_ = pos; - goto done; + return true; } if (pos == 0) { - // set position to start of file - curCluster_ = 0; - curPosition_ = 0; - goto done; + curCluster_ = curPosition_ = 0; // set position to start of file + return true; } + // calculate cluster index for cur and new position nCur = (curPosition_ - 1) >> (vol_->clusterSizeShift_ + 9); nNew = (pos - 1) >> (vol_->clusterSizeShift_ + 9); - if (nNew < nCur || curPosition_ == 0) { - // must follow chain from first cluster - curCluster_ = firstCluster_; - } - else { - // advance from curPosition - nNew -= nCur; - } - while (nNew--) { - if (!vol_->fatGet(curCluster_, &curCluster_)) goto FAIL; - } - curPosition_ = pos; + if (nNew < nCur || curPosition_ == 0) + curCluster_ = firstCluster_; // must follow chain from first cluster + else + nNew -= nCur; // advance from curPosition -done: - return true; + while (nNew--) + if (!vol_->fatGet(curCluster_, &curCluster_)) return false; - FAIL: - return false; + curPosition_ = pos; + return true; } -//------------------------------------------------------------------------------ + void SdBaseFile::setpos(filepos_t* pos) { curPosition_ = pos->position; curCluster_ = pos->cluster; } -//------------------------------------------------------------------------------ -/** The sync() call causes all modified data and directory fields + +/** + * 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. + * \return true for success, false for failure. * Reasons for failure include a call to sync() before a file has been * opened or an I/O error. */ @@ -1530,8 +1440,9 @@ bool SdBaseFile::sync() { writeError = true; return false; } -//------------------------------------------------------------------------------ -/** Copy a file's timestamps + +/** + * Copy a file's timestamps * * \param[in] file File to copy timestamps from. * @@ -1539,21 +1450,20 @@ bool SdBaseFile::sync() { * 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. + * \return true for success, false for failure. */ bool SdBaseFile::timestamp(SdBaseFile* file) { dir_t* d; dir_t dir; // get timestamps - if (!file->dirEntry(&dir)) goto FAIL; + if (!file->dirEntry(&dir)) return false; // update directory fields - if (!sync()) goto FAIL; + if (!sync()) return false; d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); - if (!d) goto FAIL; + if (!d) return false; // copy timestamps d->lastAccessDate = dir.lastAccessDate; @@ -1565,12 +1475,10 @@ bool SdBaseFile::timestamp(SdBaseFile* file) { // write back entry return vol_->cacheFlush(); - - FAIL: - return false; } -//------------------------------------------------------------------------------ -/** Set a file's timestamps in its directory entry. + +/** + * 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 @@ -1600,13 +1508,11 @@ bool SdBaseFile::timestamp(SdBaseFile* file) { * 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. + * \return true for success, false for failure. */ bool SdBaseFile::timestamp(uint8_t flags, uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second) { - uint16_t dirDate; - uint16_t dirTime; + uint16_t dirDate, dirTime; dir_t* d; if (!isOpen() @@ -1619,13 +1525,13 @@ bool SdBaseFile::timestamp(uint8_t flags, uint16_t year, uint8_t month, || hour > 23 || minute > 59 || second > 59) { - goto FAIL; + return false; } // update directory entry - if (!sync()) goto FAIL; + if (!sync()) return false; d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); - if (!d) goto FAIL; + if (!d) return false; dirDate = FAT_DATE(year, month, day); dirTime = FAT_TIME(hour, minute, second); @@ -1643,28 +1549,26 @@ bool SdBaseFile::timestamp(uint8_t flags, uint16_t year, uint8_t month, d->lastWriteTime = dirTime; } return vol_->cacheFlush(); - FAIL: - return false; } -//------------------------------------------------------------------------------ -/** Truncate a file to a specified length. The current file position + +/** + * 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. + * \return true for success, false for failure. * Reasons for failure include file is read only, file is a directory, * \a length is greater than the current file size or an I/O error occurs. */ bool SdBaseFile::truncate(uint32_t length) { uint32_t newPos; // error if not a normal file or read-only - if (!isFile() || !(flags_ & O_WRITE)) goto FAIL; + if (!isFile() || !(flags_ & O_WRITE)) return false; // error if length is greater than current size - if (length > fileSize_) goto FAIL; + if (length > fileSize_) return false; // fileSize and length are zero - nothing to do if (fileSize_ == 0) return true; @@ -1673,23 +1577,23 @@ bool SdBaseFile::truncate(uint32_t length) { newPos = curPosition_ > length ? length : curPosition_; // position to last cluster in truncated file - if (!seekSet(length)) goto FAIL; + if (!seekSet(length)) return false; if (length == 0) { // free all clusters - if (!vol_->freeChain(firstCluster_)) goto FAIL; + if (!vol_->freeChain(firstCluster_)) return false; firstCluster_ = 0; } else { uint32_t toFree; - if (!vol_->fatGet(curCluster_, &toFree)) goto FAIL; + if (!vol_->fatGet(curCluster_, &toFree)) return false; if (!vol_->isEOC(toFree)) { // free extra clusters - if (!vol_->freeChain(toFree)) goto FAIL; + if (!vol_->freeChain(toFree)) return false; // current cluster is end of chain - if (!vol_->fatPutEOC(curCluster_)) goto FAIL; + if (!vol_->fatPutEOC(curCluster_)) return false; } } fileSize_ = length; @@ -1697,16 +1601,14 @@ bool SdBaseFile::truncate(uint32_t length) { // need to update directory entry flags_ |= F_FILE_DIR_DIRTY; - if (!sync()) goto FAIL; + if (!sync()) return false; // set file to correct position return seekSet(newPos); - - FAIL: - return false; } -//------------------------------------------------------------------------------ -/** Write data to an open file. + +/** + * 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. @@ -1816,11 +1718,9 @@ int16_t SdBaseFile::write(const void* buf, uint16_t nbyte) { writeError = true; return -1; } -//------------------------------------------------------------------------------ -// suppress cpplint warnings with NOLINT comment -#if ALLOW_DEPRECATED_FUNCTIONS && !defined(DOXYGEN) - void (*SdBaseFile::oldDateTime_)(uint16_t &date, uint16_t &time) = 0; // NOLINT -#endif // ALLOW_DEPRECATED_FUNCTIONS - +#if ALLOW_DEPRECATED_FUNCTIONS + void (*SdBaseFile::oldDateTime_)(uint16_t &date, uint16_t &time) = 0; #endif + +#endif // SDSUPPORT diff --git a/Marlin/SdBaseFile.h b/Marlin/SdBaseFile.h index 02daa7b7f..425c65f9b 100644 --- a/Marlin/SdBaseFile.h +++ b/Marlin/SdBaseFile.h @@ -20,208 +20,196 @@ * */ +/** + * \file + * \brief SdBaseFile class + */ + /** * Arduino SdFat Library * Copyright (C) 2009 by William Greiman * * This file is part of the Arduino Sd2Card Library */ -#include "Marlin.h" -#if ENABLED(SDSUPPORT) +#ifndef _SDBASEFILE_H_ +#define _SDBASEFILE_H_ -#ifndef SdBaseFile_h -#define SdBaseFile_h -/** - * \file - * \brief SdBaseFile class - */ -#include "Marlin.h" #include "SdFatConfig.h" #include "SdVolume.h" -//------------------------------------------------------------------------------ + /** * \struct filepos_t * \brief internal type for istream * do not use in user apps */ struct filepos_t { - /** stream position */ - uint32_t position; - /** cluster for position */ - uint32_t cluster; + uint32_t position; // stream byte position + uint32_t cluster; // cluster of position filepos_t() : position(0), cluster(0) {} }; // use the gnu style oflag in open() -/** open() oflag for reading */ -uint8_t const O_READ = 0x01; -/** open() oflag - same as O_IN */ -uint8_t const O_RDONLY = O_READ; -/** open() oflag for write */ -uint8_t const O_WRITE = 0x02; -/** open() oflag - same as O_WRITE */ -uint8_t const O_WRONLY = O_WRITE; -/** open() oflag for reading and writing */ -uint8_t const O_RDWR = (O_READ | O_WRITE); -/** open() oflag mask for access modes */ -uint8_t const O_ACCMODE = (O_READ | O_WRITE); -/** The file offset shall be set to the end of the file prior to each write. */ -uint8_t const O_APPEND = 0x04; -/** synchronous writes - call sync() after each write */ -uint8_t const O_SYNC = 0x08; -/** truncate the file to zero length */ -uint8_t const O_TRUNC = 0x10; -/** set the initial position at the end of the file */ -uint8_t const O_AT_END = 0x20; -/** create the file if nonexistent */ -uint8_t const O_CREAT = 0x40; -/** If O_CREAT and O_EXCL are set, open() shall fail if the file exists */ -uint8_t const O_EXCL = 0x80; +uint8_t const O_READ = 0x01, // open() oflag for reading + O_RDONLY = O_READ, // open() oflag - same as O_IN + O_WRITE = 0x02, // open() oflag for write + O_WRONLY = O_WRITE, // open() oflag - same as O_WRITE + O_RDWR = (O_READ | O_WRITE), // open() oflag for reading and writing + O_ACCMODE = (O_READ | O_WRITE), // open() oflag mask for access modes + O_APPEND = 0x04, // The file offset shall be set to the end of the file prior to each write. + O_SYNC = 0x08, // Synchronous writes - call sync() after each write + O_TRUNC = 0x10, // Truncate the file to zero length + O_AT_END = 0x20, // Set the initial position at the end of the file + O_CREAT = 0x40, // Create the file if nonexistent + O_EXCL = 0x80; // If O_CREAT and O_EXCL are set, open() shall fail if the file exists // SdBaseFile class static and const definitions + // flags for ls() -/** ls() flag to print modify date */ -uint8_t const LS_DATE = 1; -/** ls() flag to print file size */ -uint8_t const LS_SIZE = 2; -/** ls() flag for recursive list of subdirectories */ -uint8_t const LS_R = 4; +uint8_t const LS_DATE = 1, // ls() flag to print modify date + LS_SIZE = 2, // ls() flag to print file size + LS_R = 4; // ls() flag for recursive list of subdirectories // flags for timestamp -/** set the file's last access date */ -uint8_t const T_ACCESS = 1; -/** set the file's creation date and time */ -uint8_t const T_CREATE = 2; -/** Set the file's write date and time */ -uint8_t const T_WRITE = 4; +uint8_t const T_ACCESS = 1, // Set the file's last access date + T_CREATE = 2, // Set the file's creation date and time + T_WRITE = 4; // Set the file's write date and time + // values for type_ -/** This file has not been opened. */ -uint8_t const FAT_FILE_TYPE_CLOSED = 0; -/** A normal file */ -uint8_t const FAT_FILE_TYPE_NORMAL = 1; -/** A FAT12 or FAT16 root directory */ -uint8_t const FAT_FILE_TYPE_ROOT_FIXED = 2; -/** A FAT32 root directory */ -uint8_t const FAT_FILE_TYPE_ROOT32 = 3; -/** A subdirectory file*/ -uint8_t const FAT_FILE_TYPE_SUBDIR = 4; -/** Test value for directory type */ -uint8_t const FAT_FILE_TYPE_MIN_DIR = FAT_FILE_TYPE_ROOT_FIXED; - -/** date field for FAT directory entry +uint8_t const FAT_FILE_TYPE_CLOSED = 0, // This file has not been opened. + FAT_FILE_TYPE_NORMAL = 1, // A normal file + FAT_FILE_TYPE_ROOT_FIXED = 2, // A FAT12 or FAT16 root directory + FAT_FILE_TYPE_ROOT32 = 3, // A FAT32 root directory + FAT_FILE_TYPE_SUBDIR = 4, // A subdirectory file + FAT_FILE_TYPE_MIN_DIR = FAT_FILE_TYPE_ROOT_FIXED; // Test value for directory type + +/** + * date field for FAT directory entry * \param[in] year [1980,2107] * \param[in] month [1,12] * \param[in] day [1,31] * * \return Packed date for dir_t entry. */ -static inline uint16_t FAT_DATE(uint16_t year, uint8_t month, uint8_t day) { - return (year - 1980) << 9 | month << 5 | day; -} -/** year part of FAT directory date field +static inline uint16_t FAT_DATE(uint16_t year, uint8_t month, uint8_t day) { return (year - 1980) << 9 | month << 5 | day; } + +/** + * year part of FAT directory date field * \param[in] fatDate Date in packed dir format. * * \return Extracted year [1980,2107] */ -static inline uint16_t FAT_YEAR(uint16_t fatDate) { - return 1980 + (fatDate >> 9); -} -/** month part of FAT directory date field +static inline uint16_t FAT_YEAR(uint16_t fatDate) { return 1980 + (fatDate >> 9); } + +/** + * month part of FAT directory date field * \param[in] fatDate Date in packed dir format. * * \return Extracted month [1,12] */ -static inline uint8_t FAT_MONTH(uint16_t fatDate) { - return (fatDate >> 5) & 0XF; -} -/** day part of FAT directory date field +static inline uint8_t FAT_MONTH(uint16_t fatDate) { return (fatDate >> 5) & 0XF; } + +/** + * day part of FAT directory date field * \param[in] fatDate Date in packed dir format. * * \return Extracted day [1,31] */ -static inline uint8_t FAT_DAY(uint16_t fatDate) { - return fatDate & 0x1F; -} -/** time field for FAT directory entry +static inline uint8_t FAT_DAY(uint16_t fatDate) { return fatDate & 0x1F; } + +/** + * time field for FAT directory entry * \param[in] hour [0,23] * \param[in] minute [0,59] * \param[in] second [0,59] * * \return Packed time for dir_t entry. */ -static inline uint16_t FAT_TIME(uint8_t hour, uint8_t minute, uint8_t second) { - return hour << 11 | minute << 5 | second >> 1; -} -/** hour part of FAT directory time field +static inline uint16_t FAT_TIME(uint8_t hour, uint8_t minute, uint8_t second) { return hour << 11 | minute << 5 | second >> 1; } + +/** + * hour part of FAT directory time field * \param[in] fatTime Time in packed dir format. * * \return Extracted hour [0,23] */ -static inline uint8_t FAT_HOUR(uint16_t fatTime) { - return fatTime >> 11; -} -/** minute part of FAT directory time field +static inline uint8_t FAT_HOUR(uint16_t fatTime) { return fatTime >> 11; } + +/** + * minute part of FAT directory time field * \param[in] fatTime Time in packed dir format. * * \return Extracted minute [0,59] */ -static inline uint8_t FAT_MINUTE(uint16_t fatTime) { - return (fatTime >> 5) & 0x3F; -} -/** second part of FAT directory time field +static inline uint8_t FAT_MINUTE(uint16_t fatTime) { return (fatTime >> 5) & 0x3F; } + +/** + * second part of FAT directory time field * Note second/2 is stored in packed time. * * \param[in] fatTime Time in packed dir format. * * \return Extracted second [0,58] */ -static inline uint8_t FAT_SECOND(uint16_t fatTime) { - return 2 * (fatTime & 0x1F); -} -/** Default date for file timestamps is 1 Jan 2000 */ +static inline uint8_t FAT_SECOND(uint16_t fatTime) { 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; -/** Default time for file timestamp is 1 am */ +// Default time for file timestamp is 1 am uint16_t const FAT_DEFAULT_TIME = (1 << 11); -//------------------------------------------------------------------------------ + /** * \class SdBaseFile * \brief Base class for SdFile with Print and C++ streams. */ class SdBaseFile { public: - /** Create an instance. */ SdBaseFile() : writeError(false), type_(FAT_FILE_TYPE_CLOSED) {} SdBaseFile(const char* path, uint8_t oflag); - ~SdBaseFile() {if (isOpen()) close();} + ~SdBaseFile() { if (isOpen()) close(); } + /** * 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; - //---------------------------------------------------------------------------- + // helpers for stream classes - /** get position for streams + + /** + * get position for streams * \param[out] pos struct to receive position */ void getpos(filepos_t* pos); - /** set position for streams + + /** + * set position for streams * \param[out] pos struct with value for new position */ void setpos(filepos_t* pos); - //---------------------------------------------------------------------------- + bool close(); bool contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock); bool createContiguous(SdBaseFile* dirFile, const char* path, uint32_t size); - /** \return The current cluster number for a file or directory. */ - uint32_t curCluster() const {return curCluster_;} - /** \return The current position for a file or directory. */ - uint32_t curPosition() const {return curPosition_;} - /** \return Current working directory */ - static SdBaseFile* cwd() {return cwd_;} - /** Set the date/time callback function + /** + * \return The current cluster number for a file or directory. + */ + uint32_t curCluster() const { return curCluster_; } + + /** + * \return The current position for a file or directory. + */ + uint32_t curPosition() const { return curPosition_; } + + /** + * \return Current working directory + */ + static SdBaseFile* cwd() { return cwd_; } + + /** + * Set the date/time callback function * * \param[in] dateTime The user's call back function. The callback * function is of the form: @@ -252,35 +240,55 @@ class SdBaseFile { void (*dateTime)(uint16_t* date, uint16_t* time)) { dateTime_ = dateTime; } - /** Cancel the date/time callback function. */ - static void dateTimeCallbackCancel() {dateTime_ = 0;} + + /** + * Cancel the date/time callback function. + */ + static void dateTimeCallbackCancel() { dateTime_ = 0; } bool dirEntry(dir_t* dir); static void dirName(const dir_t& dir, char* name); bool exists(const char* name); int16_t fgets(char* str, int16_t num, char* delim = 0); - /** \return The total number of bytes in a file or directory. */ - uint32_t fileSize() const {return fileSize_;} - /** \return The first cluster number for a file or directory. */ - uint32_t firstCluster() const {return firstCluster_;} - bool getFilename(char* name); - /** \return True if this is a directory else false. */ - bool isDir() const {return type_ >= FAT_FILE_TYPE_MIN_DIR;} - /** \return True if this is a normal file else false. */ - bool isFile() const {return type_ == FAT_FILE_TYPE_NORMAL;} - /** \return True if this is an open file/directory else false. */ - bool isOpen() const {return type_ != FAT_FILE_TYPE_CLOSED;} - /** \return True if this is a subdirectory else false. */ - bool isSubDir() const {return type_ == FAT_FILE_TYPE_SUBDIR;} - /** \return True if this is the root directory. */ - bool isRoot() const { - return type_ == FAT_FILE_TYPE_ROOT_FIXED || type_ == FAT_FILE_TYPE_ROOT32; - } + + /** + * \return The total number of bytes in a file or directory. + */ + uint32_t fileSize() const { return fileSize_; } + + /** + * \return The first cluster number for a file or directory. + */ + uint32_t firstCluster() const { return firstCluster_; } + + /** + * \return True if this is a directory else false. + */ + bool isDir() const { return type_ >= FAT_FILE_TYPE_MIN_DIR; } + + /** + * \return True if this is a normal file else false. + */ + bool isFile() const { return type_ == FAT_FILE_TYPE_NORMAL; } + + /** + * \return True if this is an open file/directory else false. + */ + bool isOpen() const { return type_ != FAT_FILE_TYPE_CLOSED; } + + /** + * \return True if this is a subdirectory else false. + */ + bool isSubDir() const { return type_ == FAT_FILE_TYPE_SUBDIR; } + + /** + * \return True if this is the root directory. + */ + bool isRoot() const { return type_ == FAT_FILE_TYPE_ROOT_FIXED || type_ == FAT_FILE_TYPE_ROOT32; } + + bool getFilename(char * const name); void ls(uint8_t flags = 0, uint8_t indent = 0); + bool mkdir(SdBaseFile* dir, const char* path, bool pFlag = true); - // alias for backward compactability - bool makeDir(SdBaseFile* dir, const char* path) { - return mkdir(dir, path, false); - } bool open(SdBaseFile* dirFile, uint16_t index, uint8_t oflag); bool open(SdBaseFile* dirFile, const char* path, uint8_t oflag); bool open(const char* path, uint8_t oflag = O_READ); @@ -295,53 +303,58 @@ class SdBaseFile { int8_t readDir(dir_t* dir, char* longFilename); static bool remove(SdBaseFile* dirFile, const char* path); bool remove(); - /** Set the file's current position to zero. */ - void rewind() {seekSet(0);} + + /** + * Set the file's current position to zero. + */ + void rewind() { seekSet(0); } bool rename(SdBaseFile* dirFile, const char* newPath); bool rmdir(); - // for backward compatibility - bool rmDir() {return rmdir();} bool rmRfStar(); - /** Set the files position to current position + \a pos. See seekSet(). + + /** + * Set the files position to current position + \a pos. See seekSet(). * \param[in] offset The new position in bytes from the current position. * \return true for success or false for failure. */ - bool seekCur(int32_t offset) { - return seekSet(curPosition_ + offset); - } - /** Set the files position to end-of-file + \a offset. See seekSet(). + bool seekCur(const int32_t offset) { return seekSet(curPosition_ + offset); } + + /** + * Set the files position to end-of-file + \a offset. See seekSet(). * \param[in] offset The new position in bytes from end-of-file. * \return true for success or false for failure. */ - bool seekEnd(int32_t offset = 0) {return seekSet(fileSize_ + offset);} - bool seekSet(uint32_t pos); + bool seekEnd(const int32_t offset = 0) { return seekSet(fileSize_ + offset); } + bool seekSet(const uint32_t pos); bool sync(); bool timestamp(SdBaseFile* file); bool timestamp(uint8_t flag, uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second); - /** Type of file. You should use isFile() or isDir() instead of type() - * if possible. + + /** + * Type of file. Use isFile() or isDir() instead of type() if possible. * * \return The file or directory type. */ - uint8_t type() const {return type_;} + uint8_t type() const { return type_; } bool truncate(uint32_t size); - /** \return SdVolume that contains this file. */ - SdVolume* volume() const {return vol_;} + + /** + * \return SdVolume that contains this file. + */ + SdVolume* volume() const { return vol_; } int16_t write(const void* buf, uint16_t nbyte); - //------------------------------------------------------------------------------ + private: - // allow SdFat to set cwd_ - friend class SdFat; - // global pointer to cwd dir - static SdBaseFile* cwd_; + friend class SdFat; // allow SdFat to set cwd_ + static SdBaseFile* cwd_; // global pointer to cwd dir + // data time callback function static void (*dateTime_)(uint16_t* date, uint16_t* time); + // bits defined in flags_ - // should be 0x0F - static uint8_t const F_OFLAG = (O_ACCMODE | O_APPEND | O_SYNC); - // sync of directory entry required - static uint8_t const F_FILE_DIR_DIRTY = 0x80; + static uint8_t const F_OFLAG = (O_ACCMODE | O_APPEND | O_SYNC), // should be 0x0F + F_FILE_DIR_DIRTY = 0x80; // sync of directory entry required // private data uint8_t flags_; // See above for definition of flags_ bits @@ -355,8 +368,11 @@ class SdBaseFile { uint32_t firstCluster_; // first cluster of file SdVolume* vol_; // volume where file is located - /** experimental don't use */ - bool openParent(SdBaseFile* dir); + /** + * EXPERIMENTAL - Don't use! + */ + //bool openParent(SdBaseFile* dir); + // private functions bool addCluster(); bool addDirCluster(); @@ -367,61 +383,48 @@ class SdBaseFile { bool open(SdBaseFile* dirFile, const uint8_t dname[11], uint8_t oflag); bool openCachedEntry(uint8_t cacheIndex, uint8_t oflags); dir_t* readDirCache(); - //------------------------------------------------------------------------------ - // to be deleted - static void printDirName(const dir_t& dir, - uint8_t width, bool printSlash); - //------------------------------------------------------------------------------ - // Deprecated functions - suppress cpplint warnings with NOLINT comment -#if ALLOW_DEPRECATED_FUNCTIONS && !defined(DOXYGEN) + +// Deprecated functions +#if ALLOW_DEPRECATED_FUNCTIONS public: - /** \deprecated Use: + + /** + * \deprecated Use: * bool contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock); * \param[out] bgnBlock the first block address for the file. * \param[out] endBlock the last block address for the file. * \return true for success or false for failure. */ - bool contiguousRange(uint32_t& bgnBlock, uint32_t& endBlock) { // NOLINT + bool contiguousRange(uint32_t& bgnBlock, uint32_t& endBlock) { return contiguousRange(&bgnBlock, &endBlock); } - /** \deprecated Use: - * bool createContiguous(SdBaseFile* dirFile, - * const char* path, uint32_t size) - * \param[in] dirFile The directory where the file will be created. - * \param[in] path A path with a valid DOS 8.3 file name. - * \param[in] size The desired file size. - * \return true for success or false for failure. - */ - bool createContiguous(SdBaseFile& dirFile, // NOLINT - const char* path, uint32_t size) { + + /** + * \deprecated Use: + * bool createContiguous(SdBaseFile* dirFile, const char* path, uint32_t size) + * \param[in] dirFile The directory where the file will be created. + * \param[in] path A path with a valid DOS 8.3 file name. + * \param[in] size The desired file size. + * \return true for success or false for failure. + */ + bool createContiguous(SdBaseFile& dirFile, const char* path, uint32_t size) { return createContiguous(&dirFile, path, size); } - /** \deprecated Use: + + /** + * \deprecated Use: * static void dateTimeCallback( * void (*dateTime)(uint16_t* date, uint16_t* time)); * \param[in] dateTime The user's call back function. */ static void dateTimeCallback( - void (*dateTime)(uint16_t &date, uint16_t &time)) { // NOLINT + void (*dateTime)(uint16_t &date, uint16_t &time)) { oldDateTime_ = dateTime; dateTime_ = dateTime ? oldToNew : 0; } - /** \deprecated Use: bool dirEntry(dir_t* dir); - * \param[out] dir Location for return of the file's directory entry. - * \return true for success or false for failure. - */ - bool dirEntry(dir_t& dir) {return dirEntry(&dir);} // NOLINT - /** \deprecated Use: - * bool mkdir(SdBaseFile* dir, const char* path); - * \param[in] dir An open SdFat instance for the directory that will contain - * the new directory. - * \param[in] path A path with a valid 8.3 DOS name for the new directory. - * \return true for success or false for failure. - */ - bool mkdir(SdBaseFile& dir, const char* path) { // NOLINT - return mkdir(&dir, path); - } - /** \deprecated Use: + + /** + * \deprecated Use: * bool open(SdBaseFile* dirFile, const char* path, uint8_t oflag); * \param[in] dirFile An open SdFat instance for the directory containing the * file to be opened. @@ -430,20 +433,23 @@ class SdBaseFile { * OR of flags O_READ, O_WRITE, O_TRUNC, and O_SYNC. * \return true for success or false for failure. */ - bool open(SdBaseFile& dirFile, // NOLINT - const char* path, uint8_t oflag) { + bool open(SdBaseFile& dirFile, const char* path, uint8_t oflag) { return open(&dirFile, path, oflag); } - /** \deprecated Do not use in new apps + + /** + * \deprecated Do not use in new apps * \param[in] dirFile An open SdFat instance for the directory containing the * file to be opened. * \param[in] path A path with a valid 8.3 DOS name for a file to be opened. * \return true for success or false for failure. */ - bool open(SdBaseFile& dirFile, const char* path) { // NOLINT + bool open(SdBaseFile& dirFile, const char* path) { return open(dirFile, path, O_RDWR); } - /** \deprecated Use: + + /** + * \deprecated Use: * bool open(SdBaseFile* dirFile, uint16_t index, uint8_t oflag); * \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 @@ -452,35 +458,39 @@ class SdBaseFile { * OR of flags O_READ, O_WRITE, O_TRUNC, and O_SYNC. * \return true for success or false for failure. */ - bool open(SdBaseFile& dirFile, uint16_t index, uint8_t oflag) { // NOLINT + bool open(SdBaseFile& dirFile, uint16_t index, uint8_t oflag) { return open(&dirFile, index, oflag); } - /** \deprecated Use: bool openRoot(SdVolume* vol); + + /** + * \deprecated Use: bool openRoot(SdVolume* vol); * \param[in] vol The FAT volume containing the root directory to be opened. * \return true for success or false for failure. */ - bool openRoot(SdVolume& vol) {return openRoot(&vol);} // NOLINT - /** \deprecated Use: int8_t readDir(dir_t* dir); + bool openRoot(SdVolume& vol) { return openRoot(&vol); } + + /** + * \deprecated Use: int8_t readDir(dir_t* dir); * \param[out] dir The dir_t struct that will receive the data. * \return bytes read for success zero for eof or -1 for failure. */ - int8_t readDir(dir_t& dir, char* longFilename) {return readDir(&dir, longFilename);} // NOLINT - /** \deprecated Use: + int8_t readDir(dir_t& dir, char* longFilename) { + return readDir(&dir, longFilename); + } + + /** + * \deprecated Use: * static uint8_t remove(SdBaseFile* dirFile, const char* path); * \param[in] dirFile The directory that contains the file. * \param[in] path The name of the file to be removed. * \return true for success or false for failure. */ - static bool remove(SdBaseFile& dirFile, const char* path) { // NOLINT - return remove(&dirFile, path); - } - //------------------------------------------------------------------------------ - // rest are private + static bool remove(SdBaseFile& dirFile, const char* path) { return remove(&dirFile, path); } + 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; + static void (*oldDateTime_)(uint16_t &date, uint16_t &time); + static void oldToNew(uint16_t * const date, uint16_t * const time) { + uint16_t d, t; oldDateTime_(d, t); *date = d; *time = t; @@ -488,5 +498,4 @@ class SdBaseFile { #endif // ALLOW_DEPRECATED_FUNCTIONS }; -#endif // SdBaseFile_h -#endif +#endif // _SDBASEFILE_H_ diff --git a/Marlin/SdFatConfig.h b/Marlin/SdFatConfig.h index 82865e871..606a66f17 100644 --- a/Marlin/SdFatConfig.h +++ b/Marlin/SdFatConfig.h @@ -33,8 +33,6 @@ #include "MarlinConfig.h" -//------------------------------------------------------------------------------ - /** * To use multiple SD cards set USE_MULTIPLE_CARDS nonzero. * @@ -44,8 +42,6 @@ */ #define USE_MULTIPLE_CARDS 0 -//------------------------------------------------------------------------------ - /** * Call flush for endl if ENDL_CALLS_FLUSH is nonzero * @@ -65,39 +61,29 @@ */ #define ENDL_CALLS_FLUSH 0 -//------------------------------------------------------------------------------ - /** * Allow use of deprecated functions if ALLOW_DEPRECATED_FUNCTIONS is nonzero */ #define ALLOW_DEPRECATED_FUNCTIONS 1 -//------------------------------------------------------------------------------ - /** * Allow FAT12 volumes if FAT12_SUPPORT is nonzero. * FAT12 has not been well tested. */ #define FAT12_SUPPORT 0 -//------------------------------------------------------------------------------ - /** * SPI init rate for SD initialization commands. Must be 5 (F_CPU/64) * or 6 (F_CPU/128). */ #define SPI_SD_INIT_RATE 5 -//------------------------------------------------------------------------------ - /** * Set the SS pin high for hardware SPI. If SS is chip select for another SPI * device this will disable that device during the SD init phase. */ #define SET_SPI_SS_HIGH 1 -//------------------------------------------------------------------------------ - /** * Define MEGA_SOFT_SPI nonzero to use software SPI on Mega Arduinos. * Pins used are SS 10, MOSI 11, MISO 12, and SCK 13. @@ -108,8 +94,6 @@ */ #define MEGA_SOFT_SPI 0 -//------------------------------------------------------------------------------ - // Set USE_SOFTWARE_SPI nonzero to ALWAYS use Software SPI. #define USE_SOFTWARE_SPI 0 @@ -119,8 +103,6 @@ #define SOFT_SPI_MISO_PIN 12 // Software SPI Master In Slave Out pin #define SOFT_SPI_SCK_PIN 13 // Software SPI Clock pin -//------------------------------------------------------------------------------ - /** * The __cxa_pure_virtual function is an error handler that is invoked when * a pure virtual function is called. diff --git a/Marlin/SdFatStructs.h b/Marlin/SdFatStructs.h index 52c815d76..7257f3617 100644 --- a/Marlin/SdFatStructs.h +++ b/Marlin/SdFatStructs.h @@ -20,35 +20,31 @@ * */ +/** + * \file + * \brief FAT file structures + */ + /** * Arduino SdFat Library * Copyright (C) 2009 by William Greiman * * This file is part of the Arduino Sd2Card Library */ -#include "Marlin.h" -#if ENABLED(SDSUPPORT) - -#ifndef SdFatStructs_h -#define SdFatStructs_h +#ifndef SDFATSTRUCTS_H +#define SDFATSTRUCTS_H #define PACKED __attribute__((__packed__)) -/** - * \file - * \brief FAT file structures - */ + /** * mostly from Microsoft document fatgen103.doc * http://www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx */ -//------------------------------------------------------------------------------ -/** Value for byte 510 of boot block or MBR */ -uint8_t const BOOTSIG0 = 0x55; -/** Value for byte 511 of boot block or MBR */ -uint8_t const BOOTSIG1 = 0xAA; -/** Value for bootSignature field int FAT/FAT32 boot sector */ -uint8_t const EXTENDED_BOOT_SIG = 0x29; -//------------------------------------------------------------------------------ + +uint8_t const BOOTSIG0 = 0x55, // Value for byte 510 of boot block or MBR + BOOTSIG1 = 0xAA, // Value for byte 511 of boot block or MBR + EXTENDED_BOOT_SIG = 0x29; // Value for bootSignature field int FAT/FAT32 boot sector + /** * \struct partitionTable * \brief MBR partition table entry @@ -57,59 +53,58 @@ uint8_t const EXTENDED_BOOT_SIG = 0x29; * The MBR partition table has four entries. */ struct partitionTable { - /** - * Boot Indicator . Indicates whether the volume is the active - * partition. Legal values include: 0x00. Do not use for booting. - * 0x80 Active partition. - */ + /** + * Boot Indicator . Indicates whether the volume is the active + * partition. Legal values include: 0x00. Do not use for booting. + * 0x80 Active partition. + */ uint8_t boot; - /** - * Head part of Cylinder-head-sector address of the first block in - * the partition. Legal values are 0-255. Only used in old PC BIOS. - */ + /** + * Head part of Cylinder-head-sector address of the first block in + * the partition. Legal values are 0-255. Only used in old PC BIOS. + */ uint8_t beginHead; - /** - * Sector part of Cylinder-head-sector address of the first block in - * the partition. Legal values are 1-63. Only used in old PC BIOS. - */ + /** + * Sector part of Cylinder-head-sector address of the first block in + * the partition. Legal values are 1-63. Only used in old PC BIOS. + */ unsigned beginSector : 6; - /** High bits cylinder for first block in partition. */ + /** High bits cylinder for first block in partition. */ unsigned beginCylinderHigh : 2; - /** - * Combine beginCylinderLow with beginCylinderHigh. Legal values - * are 0-1023. Only used in old PC BIOS. - */ + /** + * Combine beginCylinderLow with beginCylinderHigh. Legal values + * are 0-1023. Only used in old PC BIOS. + */ uint8_t beginCylinderLow; - /** - * Partition type. See defines that begin with PART_TYPE_ for - * some Microsoft partition types. - */ + /** + * Partition type. See defines that begin with PART_TYPE_ for + * some Microsoft partition types. + */ uint8_t type; - /** - * head part of cylinder-head-sector address of the last sector in the - * partition. Legal values are 0-255. Only used in old PC BIOS. - */ + /** + * head part of cylinder-head-sector address of the last sector in the + * partition. Legal values are 0-255. Only used in old PC BIOS. + */ uint8_t endHead; - /** - * Sector part of cylinder-head-sector address of the last sector in - * the partition. Legal values are 1-63. Only used in old PC BIOS. - */ + /** + * Sector part of cylinder-head-sector address of the last sector in + * the partition. Legal values are 1-63. Only used in old PC BIOS. + */ unsigned endSector : 6; - /** High bits of end cylinder */ + /** High bits of end cylinder */ unsigned endCylinderHigh : 2; - /** - * Combine endCylinderLow with endCylinderHigh. Legal values - * are 0-1023. Only used in old PC BIOS. - */ + /** + * Combine endCylinderLow with endCylinderHigh. Legal values + * are 0-1023. Only used in old PC BIOS. + */ uint8_t endCylinderLow; - /** Logical block address of the first block in the partition. */ - uint32_t firstSector; - /** Length of the partition, in blocks. */ - uint32_t totalSectors; + + uint32_t firstSector; // Logical block address of the first block in the partition. + uint32_t totalSectors; // Length of the partition, in blocks. } PACKED; -/** Type name for partitionTable */ -typedef struct partitionTable part_t; -//------------------------------------------------------------------------------ + +typedef struct partitionTable part_t; // Type name for partitionTable + /** * \struct masterBootRecord * @@ -118,22 +113,16 @@ typedef struct partitionTable part_t; * The first block of a storage device that is formatted with a MBR. */ struct masterBootRecord { - /** Code Area for master boot program. */ - uint8_t codeArea[440]; - /** Optional Windows NT disk signature. May contain boot code. */ - uint32_t diskSignature; - /** Usually zero but may be more boot code. */ - uint16_t usuallyZero; - /** Partition tables. */ - part_t part[4]; - /** First MBR signature byte. Must be 0x55 */ - uint8_t mbrSig0; - /** Second MBR signature byte. Must be 0xAA */ - uint8_t mbrSig1; + uint8_t codeArea[440]; // Code Area for master boot program. + uint32_t diskSignature; // Optional Windows NT disk signature. May contain boot code. + uint16_t usuallyZero; // Usually zero but may be more boot code. + part_t part[4]; // Partition tables. + uint8_t mbrSig0; // First MBR signature byte. Must be 0x55 + uint8_t mbrSig1; // Second MBR signature byte. Must be 0xAA } PACKED; /** Type name for masterBootRecord */ typedef struct masterBootRecord mbr_t; -//------------------------------------------------------------------------------ + /** * \struct fat_boot * @@ -141,285 +130,280 @@ typedef struct masterBootRecord mbr_t; * */ struct fat_boot { - /** - * The first three bytes of the boot sector must be valid, - * executable x 86-based CPU instructions. This includes a - * jump instruction that skips the next nonexecutable bytes. - */ + /** + * The first three bytes of the boot sector must be valid, + * executable x 86-based CPU instructions. This includes a + * jump instruction that skips the next nonexecutable bytes. + */ uint8_t jump[3]; - /** - * This is typically a string of characters that identifies - * the operating system that formatted the volume. - */ + /** + * This is typically a string of characters that identifies + * the operating system that formatted the volume. + */ char oemId[8]; - /** - * The size of a hardware sector. Valid decimal values for this - * field are 512, 1024, 2048, and 4096. For most disks used in - * the United States, the value of this field is 512. - */ + /** + * The size of a hardware sector. Valid decimal values for this + * field are 512, 1024, 2048, and 4096. For most disks used in + * the United States, the value of this field is 512. + */ uint16_t bytesPerSector; - /** - * Number of sectors per allocation unit. This value must be a - * power of 2 that is greater than 0. The legal values are - * 1, 2, 4, 8, 16, 32, 64, and 128. 128 should be avoided. - */ + /** + * Number of sectors per allocation unit. This value must be a + * power of 2 that is greater than 0. The legal values are + * 1, 2, 4, 8, 16, 32, 64, and 128. 128 should be avoided. + */ uint8_t sectorsPerCluster; - /** - * The number of sectors preceding the start of the first FAT, - * including the boot sector. The value of this field is always 1. - */ + /** + * The number of sectors preceding the start of the first FAT, + * including the boot sector. The value of this field is always 1. + */ uint16_t reservedSectorCount; - /** - * The number of copies of the FAT on the volume. - * The value of this field is always 2. - */ + /** + * The number of copies of the FAT on the volume. + * The value of this field is always 2. + */ uint8_t fatCount; - /** - * For FAT12 and FAT16 volumes, this field contains the count of - * 32-byte directory entries in the root directory. For FAT32 volumes, - * this field must be set to 0. For FAT12 and FAT16 volumes, this - * value should always specify a count that when multiplied by 32 - * results in a multiple of bytesPerSector. FAT16 volumes should - * use the value 512. - */ + /** + * For FAT12 and FAT16 volumes, this field contains the count of + * 32-byte directory entries in the root directory. For FAT32 volumes, + * this field must be set to 0. For FAT12 and FAT16 volumes, this + * value should always specify a count that when multiplied by 32 + * results in a multiple of bytesPerSector. FAT16 volumes should + * use the value 512. + */ uint16_t rootDirEntryCount; - /** - * This field is the old 16-bit total count of sectors on the volume. - * This count includes the count of all sectors in all four regions - * of the volume. This field can be 0; if it is 0, then totalSectors32 - * must be nonzero. For FAT32 volumes, this field must be 0. For - * FAT12 and FAT16 volumes, this field contains the sector count, and - * totalSectors32 is 0 if the total sector count fits - * (is less than 0x10000). - */ + /** + * This field is the old 16-bit total count of sectors on the volume. + * This count includes the count of all sectors in all four regions + * of the volume. This field can be 0; if it is 0, then totalSectors32 + * must be nonzero. For FAT32 volumes, this field must be 0. For + * FAT12 and FAT16 volumes, this field contains the sector count, and + * totalSectors32 is 0 if the total sector count fits + * (is less than 0x10000). + */ uint16_t totalSectors16; - /** - * This dates back to the old MS-DOS 1.x media determination and is - * no longer usually used for anything. 0xF8 is the standard value - * for fixed (nonremovable) media. For removable media, 0xF0 is - * frequently used. Legal values are 0xF0 or 0xF8-0xFF. - */ + /** + * This dates back to the old MS-DOS 1.x media determination and is + * no longer usually used for anything. 0xF8 is the standard value + * for fixed (nonremovable) media. For removable media, 0xF0 is + * frequently used. Legal values are 0xF0 or 0xF8-0xFF. + */ uint8_t mediaType; - /** - * Count of sectors occupied by one FAT on FAT12/FAT16 volumes. - * On FAT32 volumes this field must be 0, and sectorsPerFat32 - * contains the FAT size count. - */ + /** + * Count of sectors occupied by one FAT on FAT12/FAT16 volumes. + * On FAT32 volumes this field must be 0, and sectorsPerFat32 + * contains the FAT size count. + */ uint16_t sectorsPerFat16; - /** Sectors per track for interrupt 0x13. Not used otherwise. */ - uint16_t sectorsPerTrack; - /** Number of heads for interrupt 0x13. Not used otherwise. */ - uint16_t headCount; - /** - * Count of hidden sectors preceding the partition that contains this - * FAT volume. This field is generally only relevant for media - * visible on interrupt 0x13. - */ + + uint16_t sectorsPerTrack; // Sectors per track for interrupt 0x13. Not used otherwise. + uint16_t headCount; // Number of heads for interrupt 0x13. Not used otherwise. + + /** + * Count of hidden sectors preceding the partition that contains this + * FAT volume. This field is generally only relevant for media + * visible on interrupt 0x13. + */ uint32_t hidddenSectors; - /** - * This field is the new 32-bit total count of sectors on the volume. - * This count includes the count of all sectors in all four regions - * of the volume. This field can be 0; if it is 0, then - * totalSectors16 must be nonzero. - */ + /** + * This field is the new 32-bit total count of sectors on the volume. + * This count includes the count of all sectors in all four regions + * of the volume. This field can be 0; if it is 0, then + * totalSectors16 must be nonzero. + */ uint32_t totalSectors32; - /** - * Related to the BIOS physical drive number. Floppy drives are - * identified as 0x00 and physical hard disks are identified as - * 0x80, regardless of the number of physical disk drives. - * Typically, this value is set prior to issuing an INT 13h BIOS - * call to specify the device to access. The value is only - * relevant if the device is a boot device. - */ + /** + * Related to the BIOS physical drive number. Floppy drives are + * identified as 0x00 and physical hard disks are identified as + * 0x80, regardless of the number of physical disk drives. + * Typically, this value is set prior to issuing an INT 13h BIOS + * call to specify the device to access. The value is only + * relevant if the device is a boot device. + */ uint8_t driveNumber; - /** used by Windows NT - should be zero for FAT */ - uint8_t reserved1; - /** 0x29 if next three fields are valid */ - uint8_t bootSignature; - /** - * A random serial number created when formatting a disk, - * which helps to distinguish between disks. - * Usually generated by combining date and time. - */ + + uint8_t reserved1; // used by Windows NT - should be zero for FAT + uint8_t bootSignature; // 0x29 if next three fields are valid + + /** + * A random serial number created when formatting a disk, + * which helps to distinguish between disks. + * Usually generated by combining date and time. + */ uint32_t volumeSerialNumber; - /** - * A field once used to store the volume label. The volume label - * is now stored as a special file in the root directory. - */ + /** + * A field once used to store the volume label. The volume label + * is now stored as a special file in the root directory. + */ char volumeLabel[11]; - /** - * A field with a value of either FAT, FAT12 or FAT16, - * depending on the disk format. - */ + /** + * A field with a value of either FAT, FAT12 or FAT16, + * depending on the disk format. + */ char fileSystemType[8]; - /** X86 boot code */ - uint8_t bootCode[448]; - /** must be 0x55 */ - uint8_t bootSectorSig0; - /** must be 0xAA */ - uint8_t bootSectorSig1; + + uint8_t bootCode[448]; // X86 boot code + uint8_t bootSectorSig0; // must be 0x55 + uint8_t bootSectorSig1; // must be 0xAA } PACKED; -/** Type name for FAT Boot Sector */ -typedef struct fat_boot fat_boot_t; -//------------------------------------------------------------------------------ + +typedef struct fat_boot fat_boot_t; // Type name for FAT Boot Sector + /** * \struct fat32_boot * * \brief Boot sector for a FAT32 volume. - * */ struct fat32_boot { - /** - * The first three bytes of the boot sector must be valid, - * executable x 86-based CPU instructions. This includes a - * jump instruction that skips the next nonexecutable bytes. - */ + /** + * The first three bytes of the boot sector must be valid, + * executable x 86-based CPU instructions. This includes a + * jump instruction that skips the next nonexecutable bytes. + */ uint8_t jump[3]; - /** - * This is typically a string of characters that identifies - * the operating system that formatted the volume. - */ + /** + * This is typically a string of characters that identifies + * the operating system that formatted the volume. + */ char oemId[8]; - /** - * The size of a hardware sector. Valid decimal values for this - * field are 512, 1024, 2048, and 4096. For most disks used in - * the United States, the value of this field is 512. - */ + /** + * The size of a hardware sector. Valid decimal values for this + * field are 512, 1024, 2048, and 4096. For most disks used in + * the United States, the value of this field is 512. + */ uint16_t bytesPerSector; - /** - * Number of sectors per allocation unit. This value must be a - * power of 2 that is greater than 0. The legal values are - * 1, 2, 4, 8, 16, 32, 64, and 128. 128 should be avoided. - */ + /** + * Number of sectors per allocation unit. This value must be a + * power of 2 that is greater than 0. The legal values are + * 1, 2, 4, 8, 16, 32, 64, and 128. 128 should be avoided. + */ uint8_t sectorsPerCluster; - /** - * The number of sectors preceding the start of the first FAT, - * including the boot sector. Must not be zero - */ + /** + * The number of sectors preceding the start of the first FAT, + * including the boot sector. Must not be zero + */ uint16_t reservedSectorCount; - /** - * The number of copies of the FAT on the volume. - * The value of this field is always 2. - */ + /** + * The number of copies of the FAT on the volume. + * The value of this field is always 2. + */ uint8_t fatCount; - /** - * FAT12/FAT16 only. For FAT32 volumes, this field must be set to 0. - */ + /** + * FAT12/FAT16 only. For FAT32 volumes, this field must be set to 0. + */ uint16_t rootDirEntryCount; - /** - * For FAT32 volumes, this field must be 0. - */ + /** + * For FAT32 volumes, this field must be 0. + */ uint16_t totalSectors16; - /** - * This dates back to the old MS-DOS 1.x media determination and is - * no longer usually used for anything. 0xF8 is the standard value - * for fixed (nonremovable) media. For removable media, 0xF0 is - * frequently used. Legal values are 0xF0 or 0xF8-0xFF. - */ + /** + * This dates back to the old MS-DOS 1.x media determination and is + * no longer usually used for anything. 0xF8 is the standard value + * for fixed (nonremovable) media. For removable media, 0xF0 is + * frequently used. Legal values are 0xF0 or 0xF8-0xFF. + */ uint8_t mediaType; - /** - * On FAT32 volumes this field must be 0, and sectorsPerFat32 - * contains the FAT size count. - */ + /** + * On FAT32 volumes this field must be 0, and sectorsPerFat32 + * contains the FAT size count. + */ uint16_t sectorsPerFat16; - /** Sectors per track for interrupt 0x13. Not used otherwise. */ - uint16_t sectorsPerTrack; - /** Number of heads for interrupt 0x13. Not used otherwise. */ - uint16_t headCount; - /** - * Count of hidden sectors preceding the partition that contains this - * FAT volume. This field is generally only relevant for media - * visible on interrupt 0x13. - */ + + uint16_t sectorsPerTrack; // Sectors per track for interrupt 0x13. Not used otherwise. + uint16_t headCount; // Number of heads for interrupt 0x13. Not used otherwise. + + /** + * Count of hidden sectors preceding the partition that contains this + * FAT volume. This field is generally only relevant for media + * visible on interrupt 0x13. + */ uint32_t hidddenSectors; - /** - * Contains the total number of sectors in the FAT32 volume. - */ + /** + * Contains the total number of sectors in the FAT32 volume. + */ uint32_t totalSectors32; - /** - * Count of sectors occupied by one FAT on FAT32 volumes. - */ + /** + * Count of sectors occupied by one FAT on FAT32 volumes. + */ uint32_t sectorsPerFat32; - /** - * This field is only defined for FAT32 media and does not exist on - * FAT12 and FAT16 media. - * Bits 0-3 -- Zero-based number of active FAT. - * Only valid if mirroring is disabled. - * Bits 4-6 -- Reserved. - * Bit 7 -- 0 means the FAT is mirrored at runtime into all FATs. - * -- 1 means only one FAT is active; it is the one referenced - * in bits 0-3. - * Bits 8-15 -- Reserved. - */ + /** + * This field is only defined for FAT32 media and does not exist on + * FAT12 and FAT16 media. + * Bits 0-3 -- Zero-based number of active FAT. + * Only valid if mirroring is disabled. + * Bits 4-6 -- Reserved. + * Bit 7 -- 0 means the FAT is mirrored at runtime into all FATs. + * -- 1 means only one FAT is active; it is the one referenced + * in bits 0-3. + * Bits 8-15 -- Reserved. + */ uint16_t fat32Flags; - /** - * FAT32 version. High byte is major revision number. - * Low byte is minor revision number. Only 0.0 define. - */ + /** + * FAT32 version. High byte is major revision number. + * Low byte is minor revision number. Only 0.0 define. + */ uint16_t fat32Version; - /** - * Cluster number of the first cluster of the root directory for FAT32. - * This usually 2 but not required to be 2. - */ + /** + * Cluster number of the first cluster of the root directory for FAT32. + * This usually 2 but not required to be 2. + */ uint32_t fat32RootCluster; - /** - * Sector number of FSINFO structure in the reserved area of the - * FAT32 volume. Usually 1. - */ + /** + * Sector number of FSINFO structure in the reserved area of the + * FAT32 volume. Usually 1. + */ uint16_t fat32FSInfo; - /** - * If nonzero, indicates the sector number in the reserved area - * of the volume of a copy of the boot record. Usually 6. - * No value other than 6 is recommended. - */ + /** + * If nonzero, indicates the sector number in the reserved area + * of the volume of a copy of the boot record. Usually 6. + * No value other than 6 is recommended. + */ uint16_t fat32BackBootBlock; - /** - * Reserved for future expansion. Code that formats FAT32 volumes - * should always set all of the bytes of this field to 0. - */ + /** + * Reserved for future expansion. Code that formats FAT32 volumes + * should always set all of the bytes of this field to 0. + */ uint8_t fat32Reserved[12]; - /** - * Related to the BIOS physical drive number. Floppy drives are - * identified as 0x00 and physical hard disks are identified as - * 0x80, regardless of the number of physical disk drives. - * Typically, this value is set prior to issuing an INT 13h BIOS - * call to specify the device to access. The value is only - * relevant if the device is a boot device. - */ + /** + * Related to the BIOS physical drive number. Floppy drives are + * identified as 0x00 and physical hard disks are identified as + * 0x80, regardless of the number of physical disk drives. + * Typically, this value is set prior to issuing an INT 13h BIOS + * call to specify the device to access. The value is only + * relevant if the device is a boot device. + */ uint8_t driveNumber; - /** used by Windows NT - should be zero for FAT */ - uint8_t reserved1; - /** 0x29 if next three fields are valid */ - uint8_t bootSignature; - /** - * A random serial number created when formatting a disk, - * which helps to distinguish between disks. - * Usually generated by combining date and time. - */ + + uint8_t reserved1; // Used by Windows NT - should be zero for FAT + uint8_t bootSignature; // 0x29 if next three fields are valid + + /** + * A random serial number created when formatting a disk, + * which helps to distinguish between disks. + * Usually generated by combining date and time. + */ uint32_t volumeSerialNumber; - /** - * A field once used to store the volume label. The volume label - * is now stored as a special file in the root directory. - */ + /** + * A field once used to store the volume label. The volume label + * is now stored as a special file in the root directory. + */ char volumeLabel[11]; - /** - * A text field with a value of FAT32. - */ + /** + * A text field with a value of FAT32. + */ char fileSystemType[8]; - /** X86 boot code */ - uint8_t bootCode[420]; - /** must be 0x55 */ - uint8_t bootSectorSig0; - /** must be 0xAA */ - uint8_t bootSectorSig1; + + uint8_t bootCode[420]; // X86 boot code + uint8_t bootSectorSig0; // must be 0x55 + uint8_t bootSectorSig1; // must be 0xAA + } PACKED; -/** Type name for FAT32 Boot Sector */ -typedef struct fat32_boot fat32_boot_t; -//------------------------------------------------------------------------------ -/** Lead signature for a FSINFO sector */ -uint32_t const FSINFO_LEAD_SIG = 0x41615252; -/** Struct signature for a FSINFO sector */ -uint32_t const FSINFO_STRUCT_SIG = 0x61417272; + +typedef struct fat32_boot fat32_boot_t; // Type name for FAT32 Boot Sector + +uint32_t const FSINFO_LEAD_SIG = 0x41615252, // 'AaRR' Lead signature for a FSINFO sector + FSINFO_STRUCT_SIG = 0x61417272; // 'aArr' Struct signature for a FSINFO sector + /** * \struct fat32_fsinfo * @@ -427,12 +411,9 @@ uint32_t const FSINFO_STRUCT_SIG = 0x61417272; * */ struct fat32_fsinfo { - /** must be 0x52, 0x52, 0x61, 0x41 */ - uint32_t leadSignature; - /** must be zero */ - uint8_t reserved1[480]; - /** must be 0x72, 0x72, 0x41, 0x61 */ - uint32_t structSignature; + uint32_t leadSignature; // must be 0x52, 0x52, 0x61, 0x41 'RRaA' + uint8_t reserved1[480]; // must be zero + uint32_t structSignature; // must be 0x72, 0x72, 0x41, 0x61 'rrAa' /** * Contains the last known free cluster count on the volume. * If the value is 0xFFFFFFFF, then the free count is unknown @@ -448,30 +429,22 @@ struct fat32_fsinfo { * should start looking at cluster 2. */ uint32_t nextFree; - /** must be zero */ - uint8_t reserved2[12]; - /** must be 0x00, 0x00, 0x55, 0xAA */ - uint8_t tailSignature[4]; + + uint8_t reserved2[12]; // must be zero + uint8_t tailSignature[4]; // must be 0x00, 0x00, 0x55, 0xAA } PACKED; -/** Type name for FAT32 FSINFO Sector */ -typedef struct fat32_fsinfo fat32_fsinfo_t; -//------------------------------------------------------------------------------ + +typedef struct fat32_fsinfo fat32_fsinfo_t; // Type name for FAT32 FSINFO Sector + // End Of Chain values for FAT entries -/** FAT12 end of chain value used by Microsoft. */ -uint16_t const FAT12EOC = 0xFFF; -/** Minimum value for FAT12 EOC. Use to test for EOC. */ -uint16_t const FAT12EOC_MIN = 0xFF8; -/** FAT16 end of chain value used by Microsoft. */ -uint16_t const FAT16EOC = 0xFFFF; -/** Minimum value for FAT16 EOC. Use to test for EOC. */ -uint16_t const FAT16EOC_MIN = 0xFFF8; -/** FAT32 end of chain value used by Microsoft. */ -uint32_t const FAT32EOC = 0x0FFFFFFF; -/** Minimum value for FAT32 EOC. Use to test for EOC. */ -uint32_t const FAT32EOC_MIN = 0x0FFFFFF8; -/** Mask a for FAT32 entry. Entries are 28 bits. */ -uint32_t const FAT32MASK = 0x0FFFFFFF; -//------------------------------------------------------------------------------ +uint16_t const FAT12EOC = 0xFFF, // FAT12 end of chain value used by Microsoft. + FAT12EOC_MIN = 0xFF8, // Minimum value for FAT12 EOC. Use to test for EOC. + FAT16EOC = 0xFFFF, // FAT16 end of chain value used by Microsoft. + FAT16EOC_MIN = 0xFFF8; // Minimum value for FAT16 EOC. Use to test for EOC. +uint32_t const FAT32EOC = 0x0FFFFFFF, // FAT32 end of chain value used by Microsoft. + FAT32EOC_MIN = 0x0FFFFFF8, // Minimum value for FAT32 EOC. Use to test for EOC. + FAT32MASK = 0x0FFFFFFF; // Mask a for FAT32 entry. Entries are 28 bits. + /** * \struct directoryEntry * \brief FAT short directory entry @@ -503,54 +476,54 @@ uint32_t const FAT32MASK = 0x0FFFFFFF; * 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 it's 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 it's valid + * value range is 0-199 inclusive. (WHG note - seems to be hundredths) + */ uint8_t creationTimeTenths; - /** Time file was created. */ - uint16_t creationTime; - /** Date file was created. */ - uint16_t creationDate; - /** - * Last access date. Note that there is no last access time, only - * a date. This is the date of last read or write. In the case of - * a write, this should be set to the same date as lastWriteDate. - */ + + uint16_t creationTime; // Time file was created. + uint16_t creationDate; // Date file was created. + + /** + * 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. */ - uint16_t lastWriteTime; - /** Date of last write. File creation is considered a write. */ - uint16_t lastWriteDate; - /** Low word of this entry's first cluster number. */ - uint16_t firstClusterLow; - /** 32-bit unsigned holding this file's size in bytes. */ - uint32_t fileSize; + + uint16_t lastWriteTime; // Time of last write. File creation is considered a write. + uint16_t lastWriteDate; // Date of last write. File creation is considered a write. + uint16_t firstClusterLow; // Low word of this entry's first cluster number. + uint32_t fileSize; // 32-bit unsigned holding this file's size in bytes. } PACKED; + /** * \struct directoryVFATEntry * \brief VFAT long filename directory entry @@ -568,54 +541,36 @@ struct directoryVFATEntry { * bit 0-4: the position of this long filename block (first block is 1) */ uint8_t sequenceNumber; - /** First set of UTF-16 characters */ - uint16_t name1[5];//UTF-16 - /** attributes (at the same location as in directoryEntry), always 0x0F */ - uint8_t attributes; - /** Reserved for use by Windows NT. Always 0. */ - uint8_t reservedNT; - /** Checksum of the short 8.3 filename, can be used to checked if the file system as modified by a not-long-filename aware implementation. */ - uint8_t checksum; - /** Second set of UTF-16 characters */ - uint16_t name2[6];//UTF-16 - /** firstClusterLow is always zero for longFilenames */ - uint16_t firstClusterLow; - /** Third set of UTF-16 characters */ - uint16_t name3[2];//UTF-16 + + uint16_t name1[5]; // First set of UTF-16 characters + uint8_t attributes; // attributes (at the same location as in directoryEntry), always 0x0F + uint8_t reservedNT; // Reserved for use by Windows NT. Always 0. + uint8_t checksum; // Checksum of the short 8.3 filename, can be used to checked if the file system as modified by a not-long-filename aware implementation. + uint16_t name2[6]; // Second set of UTF-16 characters + uint16_t firstClusterLow; // firstClusterLow is always zero for longFilenames + uint16_t name3[2]; // Third set of UTF-16 characters } PACKED; -//------------------------------------------------------------------------------ + // Definitions for directory entries // -/** Type name for directoryEntry */ -typedef struct directoryEntry dir_t; -/** Type name for directoryVFATEntry */ -typedef struct directoryVFATEntry vfat_t; -/** escape for name[0] = 0xE5 */ -uint8_t const DIR_NAME_0xE5 = 0x05; -/** name[0] value for entry that is free after being "deleted" */ -uint8_t const DIR_NAME_DELETED = 0xE5; -/** name[0] value for entry that is free and no allocated entries follow */ -uint8_t const DIR_NAME_FREE = 0x00; -/** file is read-only */ -uint8_t const DIR_ATT_READ_ONLY = 0x01; -/** File should hidden in directory listings */ -uint8_t const DIR_ATT_HIDDEN = 0x02; -/** Entry is for a system file */ -uint8_t const DIR_ATT_SYSTEM = 0x04; -/** Directory entry contains the volume label */ -uint8_t const DIR_ATT_VOLUME_ID = 0x08; -/** Entry is for a directory */ -uint8_t const DIR_ATT_DIRECTORY = 0x10; -/** Old DOS archive bit for backup support */ -uint8_t const DIR_ATT_ARCHIVE = 0x20; -/** Test value for long name entry. Test is - (d->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME. */ -uint8_t const DIR_ATT_LONG_NAME = 0x0F; -/** Test mask for long name entry */ -uint8_t const DIR_ATT_LONG_NAME_MASK = 0x3F; -/** defined attribute bits */ -uint8_t const DIR_ATT_DEFINED_BITS = 0x3F; -/** Directory entry is part of a long name +typedef struct directoryEntry dir_t; // Type name for directoryEntry +typedef struct directoryVFATEntry vfat_t; // Type name for directoryVFATEntry + +uint8_t const DIR_NAME_0xE5 = 0x05, // escape for name[0] = 0xE5 + DIR_NAME_DELETED = 0xE5, // name[0] value for entry that is free after being "deleted" + DIR_NAME_FREE = 0x00, // name[0] value for entry that is free and no allocated entries follow + DIR_ATT_READ_ONLY = 0x01, // file is read-only + DIR_ATT_HIDDEN = 0x02, // File should hidden in directory listings + DIR_ATT_SYSTEM = 0x04, // Entry is for a system file + DIR_ATT_VOLUME_ID = 0x08, // Directory entry contains the volume label + DIR_ATT_DIRECTORY = 0x10, // Entry is for a directory + DIR_ATT_ARCHIVE = 0x20, // Old DOS archive bit for backup support + DIR_ATT_LONG_NAME = 0x0F, // Test value for long name entry. Test is (d->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME. + DIR_ATT_LONG_NAME_MASK = 0x3F, // Test mask for long name entry + DIR_ATT_DEFINED_BITS = 0x3F; // defined attribute bits + +/** + * Directory entry is part of a long name * \param[in] dir Pointer to a directory entry. * * \return true if the entry is for part of a long name else false. @@ -623,9 +578,12 @@ uint8_t const DIR_ATT_DEFINED_BITS = 0x3F; static inline uint8_t DIR_IS_LONG_NAME(const dir_t* dir) { return (dir->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME; } + /** Mask for file/subdirectory tests */ uint8_t const DIR_ATT_FILE_TYPE_MASK = (DIR_ATT_VOLUME_ID | DIR_ATT_DIRECTORY); -/** Directory entry is for a file + +/** + * Directory entry is for a file * \param[in] dir Pointer to a directory entry. * * \return true if the entry is for a normal file else false. @@ -633,7 +591,9 @@ uint8_t const DIR_ATT_FILE_TYPE_MASK = (DIR_ATT_VOLUME_ID | DIR_ATT_DIRECTORY); static inline uint8_t DIR_IS_FILE(const dir_t* dir) { return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == 0; } -/** Directory entry is for a subdirectory + +/** + * Directory entry is for a subdirectory * \param[in] dir Pointer to a directory entry. * * \return true if the entry is for a subdirectory else false. @@ -641,7 +601,9 @@ static inline uint8_t DIR_IS_FILE(const dir_t* dir) { static inline uint8_t DIR_IS_SUBDIR(const dir_t* dir) { return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == DIR_ATT_DIRECTORY; } -/** Directory entry is for a file or subdirectory + +/** + * Directory entry is for a file or subdirectory * \param[in] dir Pointer to a directory entry. * * \return true if the entry is for a normal file or subdirectory else false. @@ -649,7 +611,5 @@ static inline uint8_t DIR_IS_SUBDIR(const dir_t* dir) { static inline uint8_t DIR_IS_FILE_OR_SUBDIR(const dir_t* dir) { return (dir->attributes & DIR_ATT_VOLUME_ID) == 0; } -#endif // SdFatStructs_h - -#endif +#endif // SDFATSTRUCTS_H diff --git a/Marlin/SdFatUtil.cpp b/Marlin/SdFatUtil.cpp index 48d91df68..1d8cdb434 100644 --- a/Marlin/SdFatUtil.cpp +++ b/Marlin/SdFatUtil.cpp @@ -26,13 +26,15 @@ * * This file is part of the Arduino Sd2Card Library */ -#include "Marlin.h" +#include "MarlinConfig.h" #if ENABLED(SDSUPPORT) + #include "SdFatUtil.h" +#include "serial.h" -//------------------------------------------------------------------------------ -/** Amount of free RAM +/** + * Amount of free RAM * \return The number of free bytes. */ #ifdef __arm__ @@ -44,7 +46,8 @@ int SdFatUtil::FreeRam() { #else // __arm__ extern char* __brkval; extern char __bss_end; -/** Amount of free RAM +/** + * Amount of free RAM * \return The number of free bytes. */ int SdFatUtil::FreeRam() { @@ -53,8 +56,8 @@ int SdFatUtil::FreeRam() { } #endif // __arm -//------------------------------------------------------------------------------ -/** %Print a string in flash memory. +/** + * %Print a string in flash memory. * * \param[in] pr Print object for output. * \param[in] str Pointer to string stored in flash memory. @@ -62,30 +65,27 @@ int SdFatUtil::FreeRam() { void SdFatUtil::print_P(PGM_P str) { for (uint8_t c; (c = pgm_read_byte(str)); str++) MYSERIAL.write(c); } -//------------------------------------------------------------------------------ -/** %Print a string in flash memory followed by a CR/LF. + +/** + * %Print a string in flash memory followed by a CR/LF. * * \param[in] pr Print object for output. * \param[in] str Pointer to string stored in flash memory. */ -void SdFatUtil::println_P(PGM_P str) { - print_P(str); - MYSERIAL.println(); -} -//------------------------------------------------------------------------------ -/** %Print a string in flash memory to Serial. +void SdFatUtil::println_P(PGM_P str) { print_P(str); MYSERIAL.println(); } + +/** + * %Print a string in flash memory to Serial. * * \param[in] str Pointer to string stored in flash memory. */ -void SdFatUtil::SerialPrint_P(PGM_P str) { - print_P(str); -} -//------------------------------------------------------------------------------ -/** %Print a string in flash memory to Serial followed by a CR/LF. +void SdFatUtil::SerialPrint_P(PGM_P str) { print_P(str); } + +/** + * %Print a string in flash memory to Serial followed by a CR/LF. * * \param[in] str Pointer to string stored in flash memory. */ -void SdFatUtil::SerialPrintln_P(PGM_P str) { - println_P(str); -} -#endif +void SdFatUtil::SerialPrintln_P(PGM_P str) { println_P(str); } + +#endif // SDSUPPORT diff --git a/Marlin/SdFatUtil.h b/Marlin/SdFatUtil.h index 2e6435bbd..793ba2f07 100644 --- a/Marlin/SdFatUtil.h +++ b/Marlin/SdFatUtil.h @@ -26,11 +26,8 @@ * * This file is part of the Arduino Sd2Card Library */ -#ifndef SdFatUtil_h -#define SdFatUtil_h - -#include "Marlin.h" -#if ENABLED(SDSUPPORT) +#ifndef _SDFATUTIL_H_ +#define _SDFATUTIL_H_ /** * \file @@ -51,6 +48,4 @@ namespace SdFatUtil { using namespace SdFatUtil; // NOLINT -#endif // SDSUPPORT - -#endif // SdFatUtil_h +#endif // _SDFATUTIL_H_ diff --git a/Marlin/SdFile.cpp b/Marlin/SdFile.cpp index fc66f4173..45d18e01b 100644 --- a/Marlin/SdFile.cpp +++ b/Marlin/SdFile.cpp @@ -26,21 +26,24 @@ * * This file is part of the Arduino Sd2Card Library */ -#include "Marlin.h" +#include "MarlinConfig.h" #if ENABLED(SDSUPPORT) + #include "SdFile.h" -/** Create a file object and open it in the current working directory. + +/** + * Create a file object and open it in the current working directory. * * \param[in] path A path with a valid 8.3 DOS name for a file to be opened. * * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive * OR of open flags. see SdBaseFile::open(SdBaseFile*, const char*, uint8_t). */ -SdFile::SdFile(const char* path, uint8_t oflag) : SdBaseFile(path, oflag) { -} -//------------------------------------------------------------------------------ -/** Write data to an open file. +SdFile::SdFile(const char* path, uint8_t oflag) : SdBaseFile(path, oflag) { } + +/** + * Write data to an open file. * * \note Data is moved to the cache but may not be written to the * storage device until sync() is called. @@ -55,41 +58,37 @@ SdFile::SdFile(const char* path, uint8_t oflag) : SdBaseFile(path, oflag) { * for a read-only file, device is full, a corrupt file system or an I/O error. * */ -int16_t SdFile::write(const void* buf, uint16_t nbyte) { - return SdBaseFile::write(buf, nbyte); -} -//------------------------------------------------------------------------------ -/** Write a byte to a file. Required by the Arduino Print class. +int16_t SdFile::write(const void* buf, uint16_t nbyte) { return SdBaseFile::write(buf, nbyte); } + +/** + * Write a byte to a file. Required by the Arduino Print class. * \param[in] b the byte to be written. * Use writeError to check for errors. */ #if ARDUINO >= 100 - size_t SdFile::write(uint8_t b) { - return SdBaseFile::write(&b, 1); - } + size_t SdFile::write(uint8_t b) { return SdBaseFile::write(&b, 1); } #else - void SdFile::write(uint8_t b) { - SdBaseFile::write(&b, 1); - } + void SdFile::write(uint8_t b) { SdBaseFile::write(&b, 1); } #endif -//------------------------------------------------------------------------------ -/** Write a string to a file. Used by the Arduino Print class. + +/** + * Write a string to a file. Used by the Arduino Print class. * \param[in] str Pointer to the string. * Use writeError to check for errors. */ -void SdFile::write(const char* str) { - SdBaseFile::write(str, strlen(str)); -} -//------------------------------------------------------------------------------ -/** Write a PROGMEM string to a file. +void SdFile::write(const char* str) { SdBaseFile::write(str, strlen(str)); } + +/** + * Write a PROGMEM string to a file. * \param[in] str Pointer to the PROGMEM string. * Use writeError to check for errors. */ void SdFile::write_P(PGM_P str) { for (uint8_t c; (c = pgm_read_byte(str)); str++) write(c); } -//------------------------------------------------------------------------------ -/** Write a PROGMEM string followed by CR/LF to a file. + +/** + * Write a PROGMEM string followed by CR/LF to a file. * \param[in] str Pointer to the PROGMEM string. * Use writeError to check for errors. */ @@ -98,5 +97,4 @@ void SdFile::writeln_P(PGM_P str) { write_P(PSTR("\r\n")); } - -#endif +#endif // SDSUPPORT diff --git a/Marlin/SdFile.h b/Marlin/SdFile.h index 53f38255c..36d6862d8 100644 --- a/Marlin/SdFile.h +++ b/Marlin/SdFile.h @@ -20,24 +20,23 @@ * */ +/** + * \file + * \brief SdFile class + */ + /** * Arduino SdFat Library * Copyright (C) 2009 by William Greiman * * This file is part of the Arduino Sd2Card Library */ -/** - * \file - * \brief SdFile class - */ -#include "Marlin.h" +#ifndef _SDFILE_H_ +#define _SDFILE_H_ -#if ENABLED(SDSUPPORT) #include "SdBaseFile.h" #include -#ifndef SdFile_h -#define SdFile_h -//------------------------------------------------------------------------------ + /** * \class SdFile * \brief SdBaseFile with Print. @@ -57,7 +56,5 @@ class SdFile : public SdBaseFile, public Print { void write_P(PGM_P str); void writeln_P(PGM_P str); }; -#endif // SdFile_h - -#endif +#endif // _SDFILE_H_ diff --git a/Marlin/SdInfo.h b/Marlin/SdInfo.h index 88b465690..9fe121f16 100644 --- a/Marlin/SdInfo.h +++ b/Marlin/SdInfo.h @@ -26,12 +26,11 @@ * * This file is part of the Arduino Sd2Card Library */ -#include "Marlin.h" -#if ENABLED(SDSUPPORT) +#ifndef _SDINFO_H_ +#define _SDINFO_H_ -#ifndef SdInfo_h -#define SdInfo_h #include + // Based on the document: // // SD Specifications @@ -42,46 +41,26 @@ // May 18, 2010 // // http://www.sdcard.org/developers/tech/sdcard/pls/simplified_specs -//------------------------------------------------------------------------------ + // SD card commands -/** GO_IDLE_STATE - init card in spi mode if CS low */ -uint8_t const CMD0 = 0x00; -/** SEND_IF_COND - verify SD Memory Card interface operating condition.*/ -uint8_t const CMD8 = 0x08; -/** SEND_CSD - read the Card Specific Data (CSD register) */ -uint8_t const CMD9 = 0x09; -/** SEND_CID - read the card identification information (CID register) */ -uint8_t const CMD10 = 0x0A; -/** STOP_TRANSMISSION - end multiple block read sequence */ -uint8_t const CMD12 = 0x0C; -/** SEND_STATUS - read the card status register */ -uint8_t const CMD13 = 0x0D; -/** READ_SINGLE_BLOCK - read a single data block from the card */ -uint8_t const CMD17 = 0x11; -/** READ_MULTIPLE_BLOCK - read a multiple data blocks from the card */ -uint8_t const CMD18 = 0x12; -/** WRITE_BLOCK - write a single data block to the card */ -uint8_t const CMD24 = 0x18; -/** WRITE_MULTIPLE_BLOCK - write blocks of data until a STOP_TRANSMISSION */ -uint8_t const CMD25 = 0x19; -/** ERASE_WR_BLK_START - sets the address of the first block to be erased */ -uint8_t const CMD32 = 0x20; -/** ERASE_WR_BLK_END - sets the address of the last block of the continuous - range to be erased*/ -uint8_t const CMD33 = 0x21; -/** ERASE - erase all previously selected blocks */ -uint8_t const CMD38 = 0x26; -/** APP_CMD - escape for application specific command */ -uint8_t const CMD55 = 0x37; -/** READ_OCR - read the OCR register of a card */ -uint8_t const CMD58 = 0x3A; -/** SET_WR_BLK_ERASE_COUNT - Set the number of write blocks to be - pre-erased before writing */ -uint8_t const ACMD23 = 0x17; -/** SD_SEND_OP_COMD - Sends host capacity support information and - activates the card's initialization process */ -uint8_t const ACMD41 = 0x29; -//------------------------------------------------------------------------------ +uint8_t const CMD0 = 0x00, // GO_IDLE_STATE - init card in spi mode if CS low + CMD8 = 0x08, // SEND_IF_COND - verify SD Memory Card interface operating condition + CMD9 = 0x09, // SEND_CSD - read the Card Specific Data (CSD register) + CMD10 = 0x0A, // SEND_CID - read the card identification information (CID register) + CMD12 = 0x0C, // STOP_TRANSMISSION - end multiple block read sequence + CMD13 = 0x0D, // SEND_STATUS - read the card status register + CMD17 = 0x11, // READ_SINGLE_BLOCK - read a single data block from the card + CMD18 = 0x12, // READ_MULTIPLE_BLOCK - read a multiple data blocks from the card + CMD24 = 0x18, // WRITE_BLOCK - write a single data block to the card + CMD25 = 0x19, // WRITE_MULTIPLE_BLOCK - write blocks of data until a STOP_TRANSMISSION + CMD32 = 0x20, // ERASE_WR_BLK_START - sets the address of the first block to be erased + CMD33 = 0x21, // ERASE_WR_BLK_END - sets the address of the last block of the continuous range to be erased*/ + CMD38 = 0x26, // ERASE - erase all previously selected blocks */ + CMD55 = 0x37, // APP_CMD - escape for application specific command */ + CMD58 = 0x3A, // READ_OCR - read the OCR register of a card */ + ACMD23 = 0x17, // SET_WR_BLK_ERASE_COUNT - Set the number of write blocks to be pre-erased before writing */ + ACMD41 = 0x29; // SD_SEND_OP_COMD - Sends host capacity support information and activates the card's initialization process */ + /** status for card in the ready state */ uint8_t const R1_READY_STATE = 0x00; /** status for card in the idle state */ @@ -98,7 +77,7 @@ uint8_t const WRITE_MULTIPLE_TOKEN = 0xFC; uint8_t const DATA_RES_MASK = 0x1F; /** write data accepted token */ uint8_t const DATA_RES_ACCEPTED = 0x05; -//------------------------------------------------------------------------------ + /** Card IDentification (CID) register */ typedef struct CID { // byte 0 @@ -134,7 +113,7 @@ typedef struct CID { /** CRC7 checksum */ unsigned char crc : 7; } cid_t; -//------------------------------------------------------------------------------ + /** CSD for version 1.00 cards */ typedef struct CSDV1 { // byte 0 @@ -196,7 +175,7 @@ typedef struct CSDV1 { unsigned char always1 : 1; unsigned char crc : 7; } csd1_t; -//------------------------------------------------------------------------------ + /** CSD for version 2.00 cards */ typedef struct CSDV2 { // byte 0 @@ -278,12 +257,11 @@ typedef struct CSDV2 { /** checksum */ unsigned char crc : 7; } csd2_t; -//------------------------------------------------------------------------------ + /** union of old and new style CSD register */ union csd_t { csd1_t v1; csd2_t v2; }; -#endif // SdInfo_h -#endif +#endif // _SDINFO_H_ diff --git a/Marlin/SdVolume.cpp b/Marlin/SdVolume.cpp index 4093cb5e0..bf8abc579 100644 --- a/Marlin/SdVolume.cpp +++ b/Marlin/SdVolume.cpp @@ -26,11 +26,12 @@ * * This file is part of the Arduino Sd2Card Library */ -#include "Marlin.h" +#include "MarlinConfig.h" + #if ENABLED(SDSUPPORT) #include "SdVolume.h" -//------------------------------------------------------------------------------ + #if !USE_MULTIPLE_CARDS // raw block cache uint32_t SdVolume::cacheBlockNumber_; // current block number @@ -39,7 +40,7 @@ bool SdVolume::cacheDirty_; // cacheFlush() will write block if true uint32_t SdVolume::cacheMirrorBlock_; // mirror block for second FAT #endif // USE_MULTIPLE_CARDS -//------------------------------------------------------------------------------ + // find a contiguous group of clusters bool SdVolume::allocContiguous(uint32_t count, uint32_t* curCluster) { // start of group @@ -73,14 +74,14 @@ bool 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_) goto FAIL; + 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)) goto FAIL; + if (!fatGet(endCluster, &f)) return false; if (f != 0) { // cluster in use try next cluster as bgnCluster @@ -92,16 +93,16 @@ bool SdVolume::allocContiguous(uint32_t count, uint32_t* curCluster) { } } // mark end of chain - if (!fatPutEOC(endCluster)) goto FAIL; + if (!fatPutEOC(endCluster)) return false; // link clusters while (endCluster > bgnCluster) { - if (!fatPut(endCluster - 1, endCluster)) goto FAIL; + if (!fatPut(endCluster - 1, endCluster)) return false; endCluster--; } if (*curCluster != 0) { // connect chains - if (!fatPut(*curCluster, bgnCluster)) goto FAIL; + if (!fatPut(*curCluster, bgnCluster)) return false; } // return first cluster number to caller *curCluster = bgnCluster; @@ -110,111 +111,94 @@ bool SdVolume::allocContiguous(uint32_t count, uint32_t* curCluster) { if (setStart) allocSearchStart_ = bgnCluster + 1; return true; - FAIL: - return false; } -//------------------------------------------------------------------------------ + bool SdVolume::cacheFlush() { if (cacheDirty_) { - if (!sdCard_->writeBlock(cacheBlockNumber_, cacheBuffer_.data)) { - goto FAIL; - } + if (!sdCard_->writeBlock(cacheBlockNumber_, cacheBuffer_.data)) + return false; + // mirror FAT tables if (cacheMirrorBlock_) { - if (!sdCard_->writeBlock(cacheMirrorBlock_, cacheBuffer_.data)) { - goto FAIL; - } + if (!sdCard_->writeBlock(cacheMirrorBlock_, cacheBuffer_.data)) + return false; cacheMirrorBlock_ = 0; } cacheDirty_ = 0; } return true; - FAIL: - return false; } -//------------------------------------------------------------------------------ + bool SdVolume::cacheRawBlock(uint32_t blockNumber, bool dirty) { if (cacheBlockNumber_ != blockNumber) { - if (!cacheFlush()) goto FAIL; - if (!sdCard_->readBlock(blockNumber, cacheBuffer_.data)) goto FAIL; + if (!cacheFlush()) return false; + if (!sdCard_->readBlock(blockNumber, cacheBuffer_.data)) return false; cacheBlockNumber_ = blockNumber; } if (dirty) cacheDirty_ = true; return true; - FAIL: - return false; } -//------------------------------------------------------------------------------ + // return the size in bytes of a cluster chain bool SdVolume::chainSize(uint32_t cluster, uint32_t* size) { uint32_t s = 0; do { - if (!fatGet(cluster, &cluster)) goto FAIL; + if (!fatGet(cluster, &cluster)) return false; s += 512UL << clusterSizeShift_; } while (!isEOC(cluster)); *size = s; return true; - FAIL: - return false; } -//------------------------------------------------------------------------------ + // Fetch a FAT entry bool SdVolume::fatGet(uint32_t cluster, uint32_t* value) { uint32_t lba; - if (cluster > (clusterCount_ + 1)) goto FAIL; + if (cluster > (clusterCount_ + 1)) return false; if (FAT12_SUPPORT && fatType_ == 12) { uint16_t index = cluster; index += index >> 1; lba = fatStartBlock_ + (index >> 9); - if (!cacheRawBlock(lba, CACHE_FOR_READ)) goto FAIL; + if (!cacheRawBlock(lba, CACHE_FOR_READ)) return false; index &= 0x1FF; uint16_t tmp = cacheBuffer_.data[index]; index++; if (index == 512) { - if (!cacheRawBlock(lba + 1, CACHE_FOR_READ)) goto FAIL; + if (!cacheRawBlock(lba + 1, CACHE_FOR_READ)) return false; index = 0; } tmp |= cacheBuffer_.data[index] << 8; *value = cluster & 1 ? tmp >> 4 : tmp & 0xFFF; return true; } - if (fatType_ == 16) { + + if (fatType_ == 16) lba = fatStartBlock_ + (cluster >> 8); - } - else if (fatType_ == 32) { + else if (fatType_ == 32) lba = fatStartBlock_ + (cluster >> 7); - } - else { - goto FAIL; - } - if (lba != cacheBlockNumber_) { - if (!cacheRawBlock(lba, CACHE_FOR_READ)) goto FAIL; - } - if (fatType_ == 16) { - *value = cacheBuffer_.fat16[cluster & 0xFF]; - } - else { - *value = cacheBuffer_.fat32[cluster & 0x7F] & FAT32MASK; - } + else + return false; + + if (lba != cacheBlockNumber_ && !cacheRawBlock(lba, CACHE_FOR_READ)) + return false; + + *value = (fatType_ == 16) ? cacheBuffer_.fat16[cluster & 0xFF] : (cacheBuffer_.fat32[cluster & 0x7F] & FAT32MASK); return true; - FAIL: - return false; } -//------------------------------------------------------------------------------ + // Store a FAT entry bool SdVolume::fatPut(uint32_t cluster, uint32_t value) { uint32_t lba; // error if reserved cluster - if (cluster < 2) goto FAIL; + if (cluster < 2) return false; // error if not in FAT - if (cluster > (clusterCount_ + 1)) goto FAIL; + if (cluster > (clusterCount_ + 1)) return false; if (FAT12_SUPPORT && fatType_ == 12) { uint16_t index = cluster; index += index >> 1; lba = fatStartBlock_ + (index >> 9); - if (!cacheRawBlock(lba, CACHE_FOR_WRITE)) goto FAIL; + if (!cacheRawBlock(lba, CACHE_FOR_WRITE)) return false; // mirror second FAT if (fatCount_ > 1) cacheMirrorBlock_ = lba + blocksPerFat_; index &= 0x1FF; @@ -227,7 +211,7 @@ bool SdVolume::fatPut(uint32_t cluster, uint32_t value) { if (index == 512) { lba++; index = 0; - if (!cacheRawBlock(lba, CACHE_FOR_WRITE)) goto FAIL; + if (!cacheRawBlock(lba, CACHE_FOR_WRITE)) return false; // mirror second FAT if (fatCount_ > 1) cacheMirrorBlock_ = lba + blocksPerFat_; } @@ -238,51 +222,45 @@ bool SdVolume::fatPut(uint32_t cluster, uint32_t value) { cacheBuffer_.data[index] = tmp; return true; } - if (fatType_ == 16) { + + if (fatType_ == 16) lba = fatStartBlock_ + (cluster >> 8); - } - else if (fatType_ == 32) { + else if (fatType_ == 32) lba = fatStartBlock_ + (cluster >> 7); - } - else { - goto FAIL; - } - if (!cacheRawBlock(lba, CACHE_FOR_WRITE)) goto FAIL; + else + return false; + + if (!cacheRawBlock(lba, CACHE_FOR_WRITE)) return false; + // store entry - if (fatType_ == 16) { + if (fatType_ == 16) cacheBuffer_.fat16[cluster & 0xFF] = value; - } - else { + else cacheBuffer_.fat32[cluster & 0x7F] = value; - } + // mirror second FAT if (fatCount_ > 1) cacheMirrorBlock_ = lba + blocksPerFat_; return true; - FAIL: - return false; } -//------------------------------------------------------------------------------ + // free a cluster chain bool SdVolume::freeChain(uint32_t cluster) { - uint32_t next; - // clear free cluster location allocSearchStart_ = 2; do { - if (!fatGet(cluster, &next)) goto FAIL; + uint32_t next; + if (!fatGet(cluster, &next)) return false; // free cluster - if (!fatPut(cluster, 0)) goto FAIL; + if (!fatPut(cluster, 0)) return false; cluster = next; } while (!isEOC(cluster)); return true; - FAIL: - return false; } -//------------------------------------------------------------------------------ + /** Volume free space in clusters. * * \return Count of free clusters for success or -1 if an error occurs. @@ -292,34 +270,28 @@ int32_t SdVolume::freeClusterCount() { uint16_t n; uint32_t todo = clusterCount_ + 2; - if (fatType_ == 16) { + if (fatType_ == 16) n = 256; - } - else if (fatType_ == 32) { + else if (fatType_ == 32) n = 128; - } - else { - // put FAT12 here + else // put FAT12 here return -1; - } for (uint32_t lba = fatStartBlock_; todo; todo -= n, lba++) { if (!cacheRawBlock(lba, CACHE_FOR_READ)) return -1; NOMORE(n, todo); if (fatType_ == 16) { - for (uint16_t i = 0; i < n; i++) { + for (uint16_t i = 0; i < n; i++) if (cacheBuffer_.fat16[i] == 0) free++; - } } else { - for (uint16_t i = 0; i < n; i++) { + for (uint16_t i = 0; i < n; i++) if (cacheBuffer_.fat32[i] == 0) free++; - } } } return free; } -//------------------------------------------------------------------------------ + /** Initialize a FAT volume. * * \param[in] dev The SD card where the volume is located. @@ -329,14 +301,12 @@ int32_t SdVolume::freeClusterCount() { * 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 + * \return true for success, false for failure. + * Reasons for failure include not finding a valid partition, not finding a valid * FAT file system in the specified partition or an I/O error. */ bool SdVolume::init(Sd2Card* dev, uint8_t part) { - uint32_t totalBlocks; - uint32_t volumeStartBlock = 0; + uint32_t totalBlocks, volumeStartBlock = 0; fat32_boot_t* fbs; sdCard_ = dev; @@ -349,25 +319,21 @@ bool SdVolume::init(Sd2Card* dev, uint8_t part) { // 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)goto FAIL; - if (!cacheRawBlock(volumeStartBlock, CACHE_FOR_READ)) goto FAIL; + 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 - goto FAIL; - } + if ((p->boot & 0x7F) != 0 || p->totalSectors < 100 || p->firstSector == 0) + return false; // not a valid partition volumeStartBlock = p->firstSector; } - if (!cacheRawBlock(volumeStartBlock, CACHE_FOR_READ)) goto FAIL; + if (!cacheRawBlock(volumeStartBlock, CACHE_FOR_READ)) return false; fbs = &cacheBuffer_.fbs32; if (fbs->bytesPerSector != 512 || fbs->fatCount == 0 || fbs->reservedSectorCount == 0 || fbs->sectorsPerCluster == 0) { // not valid FAT volume - goto FAIL; + return false; } fatCount_ = fbs->fatCount; blocksPerCluster_ = fbs->sectorsPerCluster; @@ -375,7 +341,7 @@ bool SdVolume::init(Sd2Card* dev, uint8_t part) { clusterSizeShift_ = 0; while (blocksPerCluster_ != _BV(clusterSizeShift_)) { // error if not power of 2 - if (clusterSizeShift_++ > 7) goto FAIL; + if (clusterSizeShift_++ > 7) return false; } blocksPerFat_ = fbs->sectorsPerFat16 ? fbs->sectorsPerFat16 : fbs->sectorsPerFat32; @@ -404,17 +370,15 @@ bool SdVolume::init(Sd2Card* dev, uint8_t part) { // FAT type is determined by cluster count if (clusterCount_ < 4085) { fatType_ = 12; - if (!FAT12_SUPPORT) goto FAIL; + if (!FAT12_SUPPORT) return false; } - else if (clusterCount_ < 65525) { + else if (clusterCount_ < 65525) fatType_ = 16; - } else { rootDirStart_ = fbs->fat32RootCluster; fatType_ = 32; } return true; - FAIL: - return false; } -#endif + +#endif // SDSUPPORT diff --git a/Marlin/SdVolume.h b/Marlin/SdVolume.h index 3041a6dfb..7cde194eb 100644 --- a/Marlin/SdVolume.h +++ b/Marlin/SdVolume.h @@ -20,20 +20,20 @@ * */ +/** + * \file + * \brief SdVolume class + */ + /** * Arduino SdFat Library * Copyright (C) 2009 by William Greiman * * This file is part of the Arduino Sd2Card Library */ -#include "Marlin.h" -#if ENABLED(SDSUPPORT) -#ifndef SdVolume_h -#define SdVolume_h -/** - * \file - * \brief SdVolume class - */ +#ifndef _SDVOLUME_H_ +#define _SDVOLUME_H_ + #include "SdFatConfig.h" #include "Sd2Card.h" #include "SdFatStructs.h" @@ -44,33 +44,26 @@ * \brief Cache for an SD data block */ union cache_t { - /** Used to access cached file data blocks. */ - uint8_t data[512]; - /** Used to access cached FAT16 entries. */ - uint16_t fat16[256]; - /** Used to access cached FAT32 entries. */ - uint32_t fat32[128]; - /** Used to access cached directory entries. */ - dir_t dir[16]; - /** Used to access a cached Master Boot Record. */ - mbr_t mbr; - /** Used to access to a cached FAT boot sector. */ - fat_boot_t fbs; - /** Used to access to a cached FAT32 boot sector. */ - fat32_boot_t fbs32; - /** Used to access to a cached FAT32 FSINFO sector. */ - fat32_fsinfo_t fsinfo; + uint8_t data[512]; // Used to access cached file data blocks. + uint16_t fat16[256]; // Used to access cached FAT16 entries. + uint32_t fat32[128]; // Used to access cached FAT32 entries. + dir_t dir[16]; // Used to access cached directory entries. + mbr_t mbr; // Used to access a cached Master Boot Record. + fat_boot_t fbs; // Used to access to a cached FAT boot sector. + fat32_boot_t fbs32; // Used to access to a cached FAT32 boot sector. + fat32_fsinfo_t fsinfo; // Used to access to a cached FAT32 FSINFO sector. }; -//------------------------------------------------------------------------------ + /** * \class SdVolume * \brief Access FAT16 and FAT32 volumes on SD and SDHC cards. */ class SdVolume { public: - /** Create an instance of SdVolume */ + // Create an instance of SdVolume SdVolume() : fatType_(0) {} - /** Clear the cache and returns a pointer to the cache. Used by the WaveRP + /** + * Clear the cache and returns a pointer to the cache. Used by the WaveRP * recorder to do raw write to the SD card. Not for normal apps. * \return A pointer to the cache buffer or zero if an error occurs. */ @@ -79,54 +72,53 @@ class SdVolume { cacheBlockNumber_ = 0xFFFFFFFF; return &cacheBuffer_; } - /** Initialize a FAT volume. Try partition one first then try super + + /** + * 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. + * \return true for success, false for failure. + * Reasons for failure include not finding a valid partition, not finding + * a valid FAT file system or an I/O error. */ - bool init(Sd2Card* dev) { return init(dev, 1) ? true : init(dev, 0);} + bool init(Sd2Card* dev) { return init(dev, 1) ? true : init(dev, 0); } bool init(Sd2Card* dev, uint8_t part); // inline functions that return volume info - /** \return The volume's cluster size in blocks. */ - uint8_t blocksPerCluster() const {return blocksPerCluster_;} - /** \return The number of blocks in one FAT. */ - uint32_t blocksPerFat() const {return blocksPerFat_;} - /** \return The total number of clusters in the volume. */ - uint32_t clusterCount() const {return clusterCount_;} - /** \return The shift count required to multiply by blocksPerCluster. */ - uint8_t clusterSizeShift() const {return clusterSizeShift_;} - /** \return The logical block number for the start of file data. */ - uint32_t dataStartBlock() const {return dataStartBlock_;} - /** \return The number of FAT structures on the volume. */ - uint8_t fatCount() const {return fatCount_;} - /** \return The logical block number for the start of the first FAT. */ - uint32_t fatStartBlock() const {return fatStartBlock_;} - /** \return The FAT type of the volume. Values are 12, 16 or 32. */ - uint8_t fatType() const {return fatType_;} + uint8_t blocksPerCluster() const { return blocksPerCluster_; } //> \return The volume's cluster size in blocks. + uint32_t blocksPerFat() const { return blocksPerFat_; } //> \return The number of blocks in one FAT. + uint32_t clusterCount() const { return clusterCount_; } //> \return The total number of clusters in the volume. + uint8_t clusterSizeShift() const { return clusterSizeShift_; } //> \return The shift count required to multiply by blocksPerCluster. + uint32_t dataStartBlock() const { return dataStartBlock_; } //> \return The logical block number for the start of file data. + uint8_t fatCount() const { return fatCount_; } //> \return The number of FAT structures on the volume. + uint32_t fatStartBlock() const { return fatStartBlock_; } //> \return The logical block number for the start of the first FAT. + uint8_t fatType() const { return fatType_; } //> \return The FAT type of the volume. Values are 12, 16 or 32. int32_t freeClusterCount(); - /** \return The number of entries in the root directory for FAT16 volumes. */ - uint32_t rootDirEntryCount() 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() const {return rootDirStart_;} - /** Sd2Card object for this volume + uint32_t rootDirEntryCount() const { return rootDirEntryCount_; } /** \return The number of entries in the root directory for FAT16 volumes. */ + + /** + * \return The logical block number for the start of the root directory + * on FAT16 volumes or the first cluster number on FAT32 volumes. + */ + uint32_t rootDirStart() const { return rootDirStart_; } + + /** + * Sd2Card object for this volume * \return pointer to Sd2Card object. */ - Sd2Card* sdCard() {return sdCard_;} - /** Debug access to FAT table + Sd2Card* sdCard() { return sdCard_; } + + /** + * Debug access to FAT table * * \param[in] n cluster number. * \param[out] v value of entry * \return true for success or false for failure */ - bool dbgFat(uint32_t n, uint32_t* v) {return fatGet(n, v);} - //------------------------------------------------------------------------------ + bool dbgFat(uint32_t n, uint32_t* v) { return fatGet(n, v); } + private: // Allow SdBaseFile access to SdVolume private data. friend class SdBaseFile; @@ -136,19 +128,20 @@ class SdVolume { // value for dirty argument in cacheRawBlock to indicate write to cache static bool const CACHE_FOR_WRITE = true; -#if USE_MULTIPLE_CARDS - cache_t cacheBuffer_; // 512 byte cache for device blocks - uint32_t cacheBlockNumber_; // Logical number of block in the cache - Sd2Card* sdCard_; // Sd2Card object for cache - bool cacheDirty_; // cacheFlush() will write block if true - uint32_t cacheMirrorBlock_; // block number for mirror FAT -#else // USE_MULTIPLE_CARDS - 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 bool cacheDirty_; // cacheFlush() will write block if true - static uint32_t cacheMirrorBlock_; // block number for mirror FAT -#endif // USE_MULTIPLE_CARDS + #if USE_MULTIPLE_CARDS + cache_t cacheBuffer_; // 512 byte cache for device blocks + uint32_t cacheBlockNumber_; // Logical number of block in the cache + Sd2Card* sdCard_; // Sd2Card object for cache + bool cacheDirty_; // cacheFlush() will write block if true + uint32_t cacheMirrorBlock_; // block number for mirror FAT + #else + 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 bool cacheDirty_; // cacheFlush() will write block if true + static uint32_t cacheMirrorBlock_; // block number for mirror FAT + #endif + uint32_t allocSearchStart_; // start cluster for alloc search uint8_t blocksPerCluster_; // cluster size in blocks uint32_t blocksPerFat_; // FAT size in blocks @@ -160,68 +153,59 @@ class SdVolume { 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 - //---------------------------------------------------------------------------- + bool 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); - } - cache_t* cache() {return &cacheBuffer_;} - uint32_t cacheBlockNumber() {return cacheBlockNumber_;} -#if USE_MULTIPLE_CARDS - bool cacheFlush(); - bool cacheRawBlock(uint32_t blockNumber, bool dirty); -#else // USE_MULTIPLE_CARDS - static bool cacheFlush(); - static bool cacheRawBlock(uint32_t blockNumber, bool dirty); -#endif // USE_MULTIPLE_CARDS + 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); } + + cache_t* cache() { return &cacheBuffer_; } + uint32_t cacheBlockNumber() const { return cacheBlockNumber_; } + + #if USE_MULTIPLE_CARDS + bool cacheFlush(); + bool cacheRawBlock(uint32_t blockNumber, bool dirty); + #else + static bool cacheFlush(); + static bool cacheRawBlock(uint32_t blockNumber, bool dirty); + #endif + // used by SdBaseFile write to assign cache to SD location void cacheSetBlockNumber(uint32_t blockNumber, bool dirty) { cacheDirty_ = dirty; cacheBlockNumber_ = blockNumber; } - void cacheSetDirty() {cacheDirty_ |= CACHE_FOR_WRITE;} + void cacheSetDirty() { cacheDirty_ |= CACHE_FOR_WRITE; } bool chainSize(uint32_t beginCluster, uint32_t* size); bool fatGet(uint32_t cluster, uint32_t* value); bool fatPut(uint32_t cluster, uint32_t value); - bool fatPutEOC(uint32_t cluster) { - return fatPut(cluster, 0x0FFFFFFF); - } + bool fatPutEOC(uint32_t cluster) { return fatPut(cluster, 0x0FFFFFFF); } bool freeChain(uint32_t cluster); bool isEOC(uint32_t cluster) const { if (FAT12_SUPPORT && fatType_ == 12) return cluster >= FAT12EOC_MIN; if (fatType_ == 16) return cluster >= FAT16EOC_MIN; return cluster >= FAT32EOC_MIN; } - bool readBlock(uint32_t block, uint8_t* dst) { - return sdCard_->readBlock(block, dst); - } - bool writeBlock(uint32_t block, const uint8_t* dst) { - return sdCard_->writeBlock(block, dst); - } - //------------------------------------------------------------------------------ - // Deprecated functions - suppress cpplint warnings with NOLINT comment -#if ALLOW_DEPRECATED_FUNCTIONS && !defined(DOXYGEN) - public: - /** \deprecated Use: bool SdVolume::init(Sd2Card* dev); - * \param[in] dev The SD card where the volume is located. - * \return true for success or false for failure. - */ - bool init(Sd2Card& dev) {return init(&dev);} // NOLINT - /** \deprecated Use: bool SdVolume::init(Sd2Card* dev, uint8_t vol); - * \param[in] dev The SD card where the volume is located. - * \param[in] part The partition to be used. - * \return true for success or false for failure. - */ - bool init(Sd2Card& dev, uint8_t part) { // NOLINT - return init(&dev, part); - } -#endif // ALLOW_DEPRECATED_FUNCTIONS + bool readBlock(uint32_t block, uint8_t* dst) { return sdCard_->readBlock(block, dst); } + bool writeBlock(uint32_t block, const uint8_t* dst) { return sdCard_->writeBlock(block, dst); } + + // Deprecated functions + #if ALLOW_DEPRECATED_FUNCTIONS + public: + /** + * \deprecated Use: bool SdVolume::init(Sd2Card* dev); + * \param[in] dev The SD card where the volume is located. + * \return true for success or false for failure. + */ + bool init(Sd2Card& dev) { return init(&dev); } + /** + * \deprecated Use: bool SdVolume::init(Sd2Card* dev, uint8_t vol); + * \param[in] dev The SD card where the volume is located. + * \param[in] part The partition to be used. + * \return true for success or false for failure. + */ + bool init(Sd2Card& dev, uint8_t part) { return init(&dev, part); } + #endif // ALLOW_DEPRECATED_FUNCTIONS }; -#endif // SdVolume -#endif + +#endif // _SDVOLUME_H_ diff --git a/Marlin/cardreader.cpp b/Marlin/cardreader.cpp index 0d5031b3a..40a5d5d42 100644 --- a/Marlin/cardreader.cpp +++ b/Marlin/cardreader.cpp @@ -20,16 +20,16 @@ * */ +#include "MarlinConfig.h" + +#if ENABLED(SDSUPPORT) + #include "cardreader.h" #include "ultralcd.h" #include "stepper.h" #include "language.h" -#include "Marlin.h" - -#if ENABLED(SDSUPPORT) - #define LONGEST_FILENAME (longFilename[0] ? longFilename : filename) CardReader::CardReader() { @@ -44,8 +44,9 @@ CardReader::CardReader() { sdprinting = cardOK = saving = logging = false; filesize = 0; sdpos = 0; - workDirDepth = 0; file_subcall_ctr = 0; + + workDirDepth = 0; ZERO(workDirParents); autostart_stilltocheck = true; //the SD start is delayed, because otherwise the serial cannot answer fast enough to make contact with the host software. @@ -73,9 +74,12 @@ char *createFilename(char *buffer, const dir_t &p) { //buffer > 12characters /** * Dive into a folder and recurse depth-first to perform a pre-set operation lsAction: * LS_Count - Add +1 to nrFiles for every file within the parent - * LS_GetFilename - Get the filename of the file indexed by nrFiles + * LS_GetFilename - Get the filename of the file indexed by nrFile_index * LS_SerialPrint - Print the full path and size of each file to serial output */ + +uint16_t nrFile_index; + void CardReader::lsDive(const char *prepend, SdFile parent, const char * const match/*=NULL*/) { dir_t p; uint8_t cnt = 0; @@ -129,7 +133,7 @@ void CardReader::lsDive(const char *prepend, SdFile parent, const char * const m if (!filenameIsDir && (p.name[8] != 'G' || p.name[9] == '~')) continue; - switch (lsAction) { + switch (lsAction) { // 1 based file count case LS_Count: nrFiles++; break; @@ -147,7 +151,7 @@ void CardReader::lsDive(const char *prepend, SdFile parent, const char * const m if (match != NULL) { if (strcasecmp(match, filename) == 0) return; } - else if (cnt == nrFiles) return; + else if (cnt == nrFile_index) return; // 0 based index cnt++; break; } @@ -255,16 +259,7 @@ void CardReader::initsd() { SERIAL_ECHO_START(); SERIAL_ECHOLNPGM(MSG_SD_CARD_OK); } - workDir = root; - curDir = &root; - #if ENABLED(SDCARD_SORT_ALPHA) - presort(); - #endif - /** - if (!workDir.openRoot(&volume)) { - SERIAL_ECHOLNPGM(MSG_SD_WORKDIR_FAIL); - } - */ + setroot(); } void CardReader::setroot() { @@ -310,26 +305,33 @@ void CardReader::openLogFile(char* name) { openFile(name, false); } +void appendAtom(SdFile &file, char *& dst, uint8_t &cnt) { + file.getFilename(dst); + while (*dst && cnt < MAXPATHNAMELENGTH) { dst++; cnt++; } + if (cnt < MAXPATHNAMELENGTH) { *dst = '/'; dst++; cnt++; } +} + void CardReader::getAbsFilename(char *t) { - uint8_t cnt = 0; - *t = '/'; t++; cnt++; - for (uint8_t i = 0; i < workDirDepth; i++) { - workDirParents[i].getFilename(t); //SDBaseFile.getfilename! - while (*t && cnt < MAXPATHNAMELENGTH) { t++; cnt++; } //crawl counter forward. + *t++ = '/'; // Root folder + uint8_t cnt = 1; + + for (uint8_t i = 0; i < workDirDepth; i++) // Loop to current work dir + appendAtom(workDirParents[i], t, cnt); + + if (cnt < MAXPATHNAMELENGTH - (FILENAME_LENGTH)) { + appendAtom(file, t, cnt); + --t; } - if (cnt < MAXPATHNAMELENGTH - (FILENAME_LENGTH)) - file.getFilename(t); - else - t[0] = 0; + *t = '\0'; } -void CardReader::openFile(char* name, bool read, bool push_current/*=false*/) { +void CardReader::openFile(char* name, const bool read, const bool subcall/*=false*/) { if (!cardOK) return; uint8_t doing = 0; - if (isFileOpen()) { //replacing current file by new file, or subfile call - if (push_current) { + if (isFileOpen()) { // Replacing current file or doing a subroutine + if (subcall) { if (file_subcall_ctr > SD_PROCEDURE_DEPTH - 1) { SERIAL_ERROR_START(); SERIAL_ERRORPGM("trying to call sub-gcode files with too many levels. MAX level is:"); @@ -338,21 +340,24 @@ void CardReader::openFile(char* name, bool read, bool push_current/*=false*/) { return; } - // Store current filename and position + // Store current filename (based on workDirParents) and position getAbsFilename(proc_filenames[file_subcall_ctr]); + filespos[file_subcall_ctr] = sdpos; SERIAL_ECHO_START(); SERIAL_ECHOPAIR("SUBROUTINE CALL target:\"", name); SERIAL_ECHOPAIR("\" parent:\"", proc_filenames[file_subcall_ctr]); SERIAL_ECHOLNPAIR("\" pos", sdpos); - filespos[file_subcall_ctr] = sdpos; file_subcall_ctr++; } - else { + else doing = 1; - } } - else { // Opening fresh file + else if (subcall) { // Returning from a subcall? + SERIAL_ECHO_START(); + SERIAL_ECHOLNPGM("END SUBROUTINE"); + } + else { // Opening fresh file doing = 2; file_subcall_ctr = 0; // Reset procedure depth in case user cancels print while in procedure } @@ -360,7 +365,7 @@ void CardReader::openFile(char* name, bool read, bool push_current/*=false*/) { if (doing) { SERIAL_ECHO_START(); SERIAL_ECHOPGM("Now "); - SERIAL_ECHO(doing == 1 ? "doing" : "fresh"); + serialprintPGM(doing == 1 ? PSTR("doing") : PSTR("fresh")); SERIAL_ECHOLNPAIR(" file: ", name); } @@ -380,8 +385,7 @@ void CardReader::openFile(char* name, bool read, bool push_current/*=false*/) { if (dirname_end != NULL && dirname_end > dirname_start) { char subdirname[FILENAME_LENGTH]; strncpy(subdirname, dirname_start, dirname_end - dirname_start); - subdirname[dirname_end - dirname_start] = 0; - SERIAL_ECHOLN(subdirname); + subdirname[dirname_end - dirname_start] = '\0'; if (!myDir.open(curDir, subdirname, O_READ)) { SERIAL_PROTOCOLPGM(MSG_SD_OPEN_FILE_FAIL); SERIAL_PROTOCOL(subdirname); @@ -403,17 +407,15 @@ void CardReader::openFile(char* name, bool read, bool push_current/*=false*/) { } } } - else { //relative path - curDir = &workDir; - } + else + curDir = &workDir; // Relative paths start in current directory if (read) { if (file.open(curDir, fname, O_READ)) { filesize = file.fileSize(); + sdpos = 0; SERIAL_PROTOCOLPAIR(MSG_SD_FILE_OPENED, fname); SERIAL_PROTOCOLLNPAIR(MSG_SD_SIZE, filesize); - sdpos = 0; - SERIAL_PROTOCOLLNPGM(MSG_SD_FILE_SELECTED); getfilename(0, fname); lcd_setstatus(longFilename[0] ? longFilename : fname); @@ -438,14 +440,14 @@ void CardReader::openFile(char* name, bool read, bool push_current/*=false*/) { } } -void CardReader::removeFile(char* name) { +void CardReader::removeFile(const char * const name) { if (!cardOK) return; stopSDPrint(); SdFile myDir; curDir = &root; - char *fname = name; + const char *fname = name; char *dirname_start, *dirname_end; if (name[0] == '/') { @@ -460,29 +462,23 @@ void CardReader::removeFile(char* name) { subdirname[dirname_end - dirname_start] = 0; SERIAL_ECHOLN(subdirname); if (!myDir.open(curDir, subdirname, O_READ)) { - SERIAL_PROTOCOLPAIR("open failed, File: ", subdirname); + SERIAL_PROTOCOLPAIR(MSG_SD_OPEN_FILE_FAIL, subdirname); SERIAL_PROTOCOLCHAR('.'); SERIAL_EOL(); return; } - else { - //SERIAL_ECHOLNPGM("dive ok"); - } curDir = &myDir; dirname_start = dirname_end + 1; } - else { // the remainder after all /fsa/fdsa/ is the filename + else { fname = dirname_start; - //SERIAL_ECHOLNPGM("remainder"); - //SERIAL_ECHOLN(fname); break; } } } - else { // relative path + else // Relative paths are rooted in the current directory curDir = &workDir; - } if (file.remove(curDir, fname)) { SERIAL_PROTOCOLPGM("File deleted:"); @@ -506,14 +502,13 @@ void CardReader::getStatus() { SERIAL_PROTOCOLCHAR('/'); SERIAL_PROTOCOLLN(filesize); } - else { + else SERIAL_PROTOCOLLNPGM(MSG_SD_NOT_PRINTING); - } } void CardReader::write_command(char *buf) { char* begin = buf; - char* npos = 0; + char* npos = NULL; char* end = buf + strlen(buf) - 1; file.writeError = false; @@ -595,7 +590,7 @@ void CardReader::getfilename(uint16_t nr, const char * const match/*=NULL*/) { #endif // SDSORT_CACHE_NAMES curDir = &workDir; lsAction = LS_GetFilename; - nrFiles = nr; + nrFile_index = nr; curDir->rewind(); lsDive("", *curDir, match); } @@ -611,20 +606,20 @@ uint16_t CardReader::getnrfilenames() { } void CardReader::chdir(const char * relpath) { - SdFile newfile; + SdFile newDir; SdFile *parent = &root; if (workDir.isOpen()) parent = &workDir; - if (!newfile.open(*parent, relpath, O_READ)) { + if (!newDir.open(*parent, relpath, O_READ)) { SERIAL_ECHO_START(); SERIAL_ECHOPGM(MSG_SD_CANT_ENTER_SUBDIR); SERIAL_ECHOLN(relpath); } else { + workDir = newDir; if (workDirDepth < MAX_DIR_DEPTH) - workDirParents[workDirDepth++] = *parent; - workDir = newfile; + workDirParents[workDirDepth++] = workDir; #if ENABLED(SDCARD_SORT_ALPHA) presort(); #endif @@ -632,8 +627,8 @@ void CardReader::chdir(const char * relpath) { } void CardReader::updir() { - if (workDirDepth > 0) { - workDir = workDirParents[--workDirDepth]; + if (workDirDepth > 0) { // At least 1 dir has been saved + workDir = --workDirDepth ? workDirParents[workDirDepth] : root; // Use parent, or root if none #if ENABLED(SDCARD_SORT_ALPHA) presort(); #endif @@ -696,7 +691,7 @@ void CardReader::updir() { sortnames = new char*[fileCnt]; #endif #elif ENABLED(SDSORT_USES_STACK) - char sortnames[fileCnt][LONG_FILENAME_LENGTH]; + char sortnames[fileCnt][SORTED_LONGNAME_MAXLEN]; #endif // Folder sorting needs 1 bit per entry for flags. @@ -735,7 +730,12 @@ void CardReader::updir() { #endif #else // Copy filenames into the static array - strcpy(sortnames[i], LONGEST_FILENAME); + #if SORTED_LONGNAME_MAXLEN != LONG_FILENAME_LENGTH + strncpy(sortnames[i], LONGEST_FILENAME, SORTED_LONGNAME_MAXLEN); + sortnames[i][SORTED_LONGNAME_MAXLEN - 1] = '\0'; + #else + strncpy(sortnames[i], LONGEST_FILENAME, SORTED_LONGNAME_MAXLEN); + #endif #if ENABLED(SDSORT_CACHE_NAMES) strcpy(sortshort[i], filename); #endif @@ -826,12 +826,21 @@ void CardReader::updir() { #if ENABLED(SDSORT_DYNAMIC_RAM) sortnames = new char*[1]; sortnames[0] = strdup(LONGEST_FILENAME); // malloc - sortshort = new char*[1]; - sortshort[0] = strdup(filename); // malloc + #if ENABLED(SDSORT_CACHE_NAMES) + sortshort = new char*[1]; + sortshort[0] = strdup(filename); // malloc + #endif isDir = new uint8_t[1]; #else - strcpy(sortnames[0], LONGEST_FILENAME); - strcpy(sortshort[0], filename); + #if SORTED_LONGNAME_MAXLEN != LONG_FILENAME_LENGTH + strncpy(sortnames[0], LONGEST_FILENAME, SORTED_LONGNAME_MAXLEN); + sortnames[0][SORTED_LONGNAME_MAXLEN - 1] = '\0'; + #else + strncpy(sortnames[0], LONGEST_FILENAME, SORTED_LONGNAME_MAXLEN); + #endif + #if ENABLED(SDSORT_CACHE_NAMES) + strcpy(sortshort[0], filename); + #endif #endif isDir[0] = filenameIsDir ? 0x01 : 0x00; #endif @@ -860,6 +869,16 @@ void CardReader::updir() { #endif // SDCARD_SORT_ALPHA +uint16_t CardReader::get_num_Files() { + return + #if ENABLED(SDCARD_SORT_ALPHA) && SDSORT_USES_RAM && SDSORT_CACHE_NAMES + nrFiles // no need to access the SD card for filenames + #else + getnrfilenames() + #endif + ; +} + void CardReader::printingHasFinished() { stepper.synchronize(); file.close(); diff --git a/Marlin/cardreader.h b/Marlin/cardreader.h index 60930c442..5bdfa7f47 100644 --- a/Marlin/cardreader.h +++ b/Marlin/cardreader.h @@ -20,8 +20,8 @@ * */ -#ifndef CARDREADER_H -#define CARDREADER_H +#ifndef _CARDREADER_H_ +#define _CARDREADER_H_ #include "MarlinConfig.h" @@ -30,7 +30,6 @@ #define MAX_DIR_DEPTH 10 // Maximum folder depth #include "SdFile.h" - #include "types.h" #include "enum.h" @@ -40,13 +39,15 @@ public: void initsd(); void write_command(char *buf); - //files auto[0-9].g on the sd card are performed in a row - //this is to delay autostart and hence the initialisaiton of the sd card to some seconds after the normal init, so the device is available quick after a reset + // Files auto[0-9].g on the sd card are performed in sequence. + // This is to delay autostart and hence the initialisation of + // the sd card to some seconds after the normal init, so the + // device is available soon after a reset. void checkautostart(bool x); - void openFile(char* name, bool read, bool push_current=false); + void openFile(char* name, const bool read, const bool subcall=false); void openLogFile(char* name); - void removeFile(char* name); + void removeFile(const char * const name); void closefile(bool store_location=false); void release(); void openAndPrintFile(const char *name); @@ -69,6 +70,8 @@ public: void updir(); void setroot(); + uint16_t get_num_Files(); + #if ENABLED(SDCARD_SORT_ALPHA) void presort(); void getfilename_sorted(const uint16_t nr); @@ -111,6 +114,12 @@ private: uint8_t sort_order[SDSORT_LIMIT]; #endif + #if ENABLED(SDSORT_USES_RAM) && ENABLED(SDSORT_CACHE_NAMES) && DISABLED(SDSORT_DYNAMIC_RAM) + #define SORTED_LONGNAME_MAXLEN ((SDSORT_CACHE_VFATS) * (FILENAME_LENGTH) + 1) + #else + #define SORTED_LONGNAME_MAXLEN LONG_FILENAME_LENGTH + #endif + // Cache filenames to speed up SD menus. #if ENABLED(SDSORT_USES_RAM) @@ -120,10 +129,10 @@ private: char **sortshort, **sortnames; #else char sortshort[SDSORT_LIMIT][FILENAME_LENGTH]; - char sortnames[SDSORT_LIMIT][SDSORT_CACHE_VFATS * FILENAME_LENGTH + 1]; + char sortnames[SDSORT_LIMIT][SORTED_LONGNAME_MAXLEN]; #endif #elif DISABLED(SDSORT_USES_STACK) - char sortnames[SDSORT_LIMIT][LONG_FILENAME_LENGTH]; + char sortnames[SDSORT_LIMIT][SORTED_LONGNAME_MAXLEN]; #endif // Folder sorting uses an isDir array when caching items. @@ -148,8 +157,7 @@ private: uint8_t file_subcall_ctr; uint32_t filespos[SD_PROCEDURE_DEPTH]; char proc_filenames[SD_PROCEDURE_DEPTH][MAXPATHNAMELENGTH]; - uint32_t filesize; - uint32_t sdpos; + uint32_t filesize, sdpos; millis_t next_autostart_ms; bool autostart_stilltocheck; //the sd start is delayed, because otherwise the serial cannot answer fast enought to make contact with the hostsoftware. @@ -164,27 +172,27 @@ private: #endif }; -extern CardReader card; - -#define IS_SD_PRINTING (card.sdprinting) -#define IS_SD_FILE_OPEN (card.isFileOpen()) - #if PIN_EXISTS(SD_DETECT) #if ENABLED(SD_DETECT_INVERTED) - #define IS_SD_INSERTED (READ(SD_DETECT_PIN) != 0) + #define IS_SD_INSERTED (READ(SD_DETECT_PIN) == HIGH) #else - #define IS_SD_INSERTED (READ(SD_DETECT_PIN) == 0) + #define IS_SD_INSERTED (READ(SD_DETECT_PIN) == LOW) #endif #else - //No card detect line? Assume the card is inserted. + // No card detect line? Assume the card is inserted. #define IS_SD_INSERTED true #endif -#else - -#define IS_SD_PRINTING (false) -#define IS_SD_FILE_OPEN (false) +extern CardReader card; #endif // SDSUPPORT -#endif // __CARDREADER_H +#if ENABLED(SDSUPPORT) + #define IS_SD_PRINTING (card.sdprinting) + #define IS_SD_FILE_OPEN (card.isFileOpen()) +#else + #define IS_SD_PRINTING (false) + #define IS_SD_FILE_OPEN (false) +#endif + +#endif // _CARDREADER_H_ diff --git a/Marlin/ultralcd.cpp b/Marlin/ultralcd.cpp index 9533916de..3b4082cd2 100644 --- a/Marlin/ultralcd.cpp +++ b/Marlin/ultralcd.cpp @@ -3799,7 +3799,8 @@ void kill_screen(const char* lcd_msg) { void lcd_sdcard_menu() { ENCODER_DIRECTION_MENUS(); - const uint16_t fileCnt = card.getnrfilenames(); + const uint16_t fileCnt = card.get_num_Files(); + START_MENU(); MENU_BACK(MSG_MAIN); card.getWorkDirName();