Merge pull request #8432 from thinkyhead/bf1_fix_M32_subroutines

[1.1] Fix 'M32 P' subroutines
master
Scott Lahteine 7 years ago committed by GitHub
commit d2613ce077
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1276,8 +1276,13 @@ inline void get_serial_commands() {
|| ((sd_char == '#' || sd_char == ':') && !sd_comment_mode) || ((sd_char == '#' || sd_char == ':') && !sd_comment_mode)
) { ) {
if (card_eof) { if (card_eof) {
SERIAL_PROTOCOLLNPGM(MSG_FILE_PRINTED);
card.printingHasFinished(); card.printingHasFinished();
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) #if ENABLED(PRINTER_EVENT_LEDS)
LCD_MESSAGEPGM(MSG_INFO_COMPLETED_PRINTS); LCD_MESSAGEPGM(MSG_INFO_COMPLETED_PRINTS);
set_led_color(0, 255, 0); // Green set_led_color(0, 255, 0); // Green
@ -1290,6 +1295,7 @@ inline void get_serial_commands() {
#endif #endif
card.checkautostart(true); card.checkautostart(true);
} }
}
else if (n == -1) { else if (n == -1) {
SERIAL_ERROR_START(); SERIAL_ERROR_START();
SERIAL_ECHOLNPGM(MSG_SD_ERR_READ); SERIAL_ECHOLNPGM(MSG_SD_ERR_READ);
@ -6897,19 +6903,23 @@ inline void gcode_M31() {
/** /**
* M32: Select file and start SD Print * 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() { inline void gcode_M32() {
if (card.sdprinting) if (card.sdprinting) stepper.synchronize();
stepper.synchronize();
char* namestartpos = parser.string_arg; if (card.cardOK) {
const bool call_procedure = parser.boolval('P'); const bool call_procedure = parser.boolval('P');
if (card.cardOK) { card.openFile(parser.string_arg, true, call_procedure);
card.openFile(namestartpos, true, call_procedure);
if (parser.seenval('S')) if (parser.seenval('S')) card.setIndex(parser.value_long());
card.setIndex(parser.value_long());
card.startFileprint(); card.startFileprint();

@ -26,19 +26,19 @@
* *
* This file is part of the Arduino Sd2Card Library * This file is part of the Arduino Sd2Card Library
*/ */
#include "Marlin.h" #include "MarlinConfig.h"
#if ENABLED(SDSUPPORT) #if ENABLED(SDSUPPORT)
#include "Sd2Card.h" #include "Sd2Card.h"
#if ENABLED(USE_WATCHDOG) #if ENABLED(USE_WATCHDOG)
#include "watchdog.h" #include "watchdog.h"
#endif #endif
//------------------------------------------------------------------------------
#if DISABLED(SOFTWARE_SPI) #if DISABLED(SOFTWARE_SPI)
// functions for hardware SPI // functions for hardware SPI
//------------------------------------------------------------------------------
// make sure SPCR rate is in expected bits // make sure SPCR rate is in expected bits
#if (SPR0 != 0 || SPR1 != 1) #if (SPR0 != 0 || SPR1 != 1)
#error "unexpected SPCR bits" #error "unexpected SPCR bits"
@ -52,14 +52,14 @@
SPCR = _BV(SPE) | _BV(MSTR) | (spiRate >> 1); SPCR = _BV(SPE) | _BV(MSTR) | (spiRate >> 1);
SPSR = spiRate & 1 || spiRate == 6 ? 0 : _BV(SPI2X); SPSR = spiRate & 1 || spiRate == 6 ? 0 : _BV(SPI2X);
} }
//------------------------------------------------------------------------------
/** SPI receive a byte */ /** SPI receive a byte */
static uint8_t spiRec() { static uint8_t spiRec() {
SPDR = 0xFF; SPDR = 0xFF;
while (!TEST(SPSR, SPIF)) { /* Intentionally left empty */ } while (!TEST(SPSR, SPIF)) { /* Intentionally left empty */ }
return SPDR; return SPDR;
} }
//------------------------------------------------------------------------------
/** SPI read data - only one call so force inline */ /** SPI read data - only one call so force inline */
static inline __attribute__((always_inline)) static inline __attribute__((always_inline))
void spiRead(uint8_t* buf, uint16_t nbyte) { void spiRead(uint8_t* buf, uint16_t nbyte) {
@ -73,13 +73,13 @@
while (!TEST(SPSR, SPIF)) { /* Intentionally left empty */ } while (!TEST(SPSR, SPIF)) { /* Intentionally left empty */ }
buf[nbyte] = SPDR; buf[nbyte] = SPDR;
} }
//------------------------------------------------------------------------------
/** SPI send a byte */ /** SPI send a byte */
static void spiSend(uint8_t b) { static void spiSend(uint8_t b) {
SPDR = b; SPDR = b;
while (!TEST(SPSR, SPIF)) { /* Intentionally left empty */ } while (!TEST(SPSR, SPIF)) { /* Intentionally left empty */ }
} }
//------------------------------------------------------------------------------
/** SPI send block - only one call so force inline */ /** SPI send block - only one call so force inline */
static inline __attribute__((always_inline)) static inline __attribute__((always_inline))
void spiSendBlock(uint8_t token, const uint8_t* buf) { void spiSendBlock(uint8_t token, const uint8_t* buf) {
@ -95,9 +95,10 @@
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#else // SOFTWARE_SPI #else // SOFTWARE_SPI
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/** nop to tune soft SPI timing */ /** nop to tune soft SPI timing */
#define nop asm volatile ("nop\n\t") #define nop asm volatile ("nop\n\t")
//------------------------------------------------------------------------------
/** Soft SPI receive byte */ /** Soft SPI receive byte */
static uint8_t spiRec() { static uint8_t spiRec() {
uint8_t data = 0; uint8_t data = 0;
@ -123,13 +124,13 @@
sei(); sei();
return data; return data;
} }
//------------------------------------------------------------------------------
/** Soft SPI read data */ /** Soft SPI read data */
static void spiRead(uint8_t* buf, uint16_t nbyte) { static void spiRead(uint8_t* buf, uint16_t nbyte) {
for (uint16_t i = 0; i < nbyte; i++) for (uint16_t i = 0; i < nbyte; i++)
buf[i] = spiRec(); buf[i] = spiRec();
} }
//------------------------------------------------------------------------------
/** Soft SPI send byte */ /** Soft SPI send byte */
static void spiSend(uint8_t data) { static void spiSend(uint8_t data) {
// no interrupts during byte send - about 8 us // no interrupts during byte send - about 8 us
@ -153,7 +154,7 @@
// enable interrupts // enable interrupts
sei(); sei();
} }
//------------------------------------------------------------------------------
/** Soft SPI send block */ /** Soft SPI send block */
void spiSendBlock(uint8_t token, const uint8_t* buf) { void spiSendBlock(uint8_t token, const uint8_t* buf) {
spiSend(token); spiSend(token);
@ -161,7 +162,7 @@
spiSend(buf[i]); spiSend(buf[i]);
} }
#endif // SOFTWARE_SPI #endif // SOFTWARE_SPI
//------------------------------------------------------------------------------
// send command and return error code. Return zero for OK // send command and return error code. Return zero for OK
uint8_t Sd2Card::cardCommand(uint8_t cmd, uint32_t arg) { uint8_t Sd2Card::cardCommand(uint8_t cmd, uint32_t arg) {
// select card // 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 */ } for (uint8_t i = 0; ((status_ = spiRec()) & 0x80) && i != 0xFF; i++) { /* Intentionally left empty */ }
return status_; return status_;
} }
//------------------------------------------------------------------------------
/** /**
* Determine the size of an SD flash memory card. * Determine the size of an SD flash memory card.
* *
@ -217,19 +218,20 @@ uint32_t Sd2Card::cardSize() {
return 0; return 0;
} }
} }
//------------------------------------------------------------------------------
void Sd2Card::chipSelectHigh() { void Sd2Card::chipSelectHigh() {
digitalWrite(chipSelectPin_, HIGH); digitalWrite(chipSelectPin_, HIGH);
} }
//------------------------------------------------------------------------------
void Sd2Card::chipSelectLow() { void Sd2Card::chipSelectLow() {
#if DISABLED(SOFTWARE_SPI) #if DISABLED(SOFTWARE_SPI)
spiInit(spiRate_); spiInit(spiRate_);
#endif // SOFTWARE_SPI #endif // SOFTWARE_SPI
digitalWrite(chipSelectPin_, LOW); 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] firstBlock The address of the first block in the range.
* \param[in] lastBlock The address of the last 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 * either 0 or 1, depends on the card vendor. The card must support
* single block erase. * single block erase.
* *
* \return The value one, true, is returned for success and * \return true for success, false for failure.
* the value zero, false, is returned for failure.
*/ */
bool Sd2Card::erase(uint32_t firstBlock, uint32_t lastBlock) { bool Sd2Card::erase(uint32_t firstBlock, uint32_t lastBlock) {
csd_t csd; csd_t csd;
@ -275,26 +276,26 @@ bool Sd2Card::erase(uint32_t firstBlock, uint32_t lastBlock) {
chipSelectHigh(); chipSelectHigh();
return false; 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. * \return true if single block erase is supported.
* The value zero, false, is returned if single block erase is not supported. * false if single block erase is not supported.
*/ */
bool Sd2Card::eraseSingleBlockEnable() { bool Sd2Card::eraseSingleBlockEnable() {
csd_t csd; csd_t csd;
return readCSD(&csd) ? csd.v1.erase_blk_en : false; return readCSD(&csd) ? csd.v1.erase_blk_en : false;
} }
//------------------------------------------------------------------------------
/** /**
* Initialize an SD flash memory card. * Initialize an SD flash memory card.
* *
* \param[in] sckRateID SPI clock rate selector. See setSckRate(). * \param[in] sckRateID SPI clock rate selector. See setSckRate().
* \param[in] chipSelectPin SD chip select pin number. * \param[in] chipSelectPin SD chip select pin number.
* *
* \return The value one, true, is returned for success and * \return true for success, false for failure.
* the value zero, false, is returned for failure. The reason for failure * The reason for failure can be determined by calling errorCode() and errorData().
* can be determined by calling errorCode() and errorData().
*/ */
bool Sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin) { bool Sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin) {
errorCode_ = type_ = 0; errorCode_ = type_ = 0;
@ -384,14 +385,13 @@ bool Sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin) {
chipSelectHigh(); chipSelectHigh();
return false; return false;
} }
//------------------------------------------------------------------------------
/** /**
* Read a 512 byte block from an SD card. * Read a 512 byte block from an SD card.
* *
* \param[in] blockNumber Logical block to be read. * \param[in] blockNumber Logical block to be read.
* \param[out] dst Pointer to the location that will receive the data. * \param[out] dst Pointer to the location that will receive the data.
* \return The value one, true, is returned for success and * \return true for success, false for failure.
* the value zero, false, is returned for failure.
*/ */
bool Sd2Card::readBlock(uint32_t blockNumber, uint8_t* dst) { bool Sd2Card::readBlock(uint32_t blockNumber, uint8_t* dst) {
// use address if not SDHC card // 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) #if ENABLED(SD_CHECK_AND_RETRY)
uint8_t retryCnt = 3; uint8_t retryCnt = 3;
do { for(;;) {
if (!cardCommand(CMD17, blockNumber)) { if (cardCommand(CMD17, blockNumber))
if (readData(dst, 512)) return true;
}
else
error(SD_CARD_ERROR_CMD17); error(SD_CARD_ERROR_CMD17);
else if (readData(dst, 512))
return true;
if (!--retryCnt) break; if (!--retryCnt) break;
chipSelectHigh(); chipSelectHigh();
cardCommand(CMD12, 0); // Try sending a stop command, ignore the result. cardCommand(CMD12, 0); // Try sending a stop command, ignore the result.
errorCode_ = 0; errorCode_ = 0;
} while (true); }
#else #else
if (cardCommand(CMD17, blockNumber)) if (cardCommand(CMD17, blockNumber))
error(SD_CARD_ERROR_CMD17); error(SD_CARD_ERROR_CMD17);
@ -422,13 +421,13 @@ bool Sd2Card::readBlock(uint32_t blockNumber, uint8_t* dst) {
chipSelectHigh(); chipSelectHigh();
return false; 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. * \param[in] dst Pointer to the location for the data to be read.
* *
* \return The value one, true, is returned for success and * \return true for success, false for failure.
* the value zero, false, is returned for failure.
*/ */
bool Sd2Card::readData(uint8_t* dst) { bool Sd2Card::readData(uint8_t* dst) {
chipSelectLow(); chipSelectLow();
@ -477,9 +476,8 @@ static uint16_t CRC_CCITT(const uint8_t* data, size_t n) {
} }
return crc; return crc;
} }
#endif #endif // SD_CHECK_AND_RETRY
//------------------------------------------------------------------------------
bool Sd2Card::readData(uint8_t* dst, uint16_t count) { bool Sd2Card::readData(uint8_t* dst, uint16_t count) {
// wait for start block token // wait for start block token
uint16_t t0 = millis(); uint16_t t0 = millis();
@ -521,61 +519,55 @@ bool Sd2Card::readData(uint8_t* dst, uint16_t count) {
spiSend(0XFF); spiSend(0XFF);
return false; return false;
} }
//------------------------------------------------------------------------------
/** read CID or CSR register */ /** read CID or CSR register */
bool Sd2Card::readRegister(uint8_t cmd, void* buf) { bool Sd2Card::readRegister(uint8_t cmd, void* buf) {
uint8_t* dst = reinterpret_cast<uint8_t*>(buf); uint8_t* dst = reinterpret_cast<uint8_t*>(buf);
if (cardCommand(cmd, 0)) { if (cardCommand(cmd, 0)) {
error(SD_CARD_ERROR_READ_REG); error(SD_CARD_ERROR_READ_REG);
goto FAIL;
}
return readData(dst, 16);
FAIL:
chipSelectHigh(); chipSelectHigh();
return false; return false;
} }
//------------------------------------------------------------------------------ return readData(dst, 16);
/** Start a read multiple blocks sequence. }
/**
* Start a read multiple blocks sequence.
* *
* \param[in] blockNumber Address of first block in sequence. * \param[in] blockNumber Address of first block in sequence.
* *
* \note This function is used with readData() and readStop() for optimized * \note This function is used with readData() and readStop() for optimized
* multiple block reads. SPI chipSelect must be low for the entire sequence. * multiple block reads. SPI chipSelect must be low for the entire sequence.
* *
* \return The value one, true, is returned for success and * \return true for success, false for failure.
* the value zero, false, is returned for failure.
*/ */
bool Sd2Card::readStart(uint32_t blockNumber) { bool Sd2Card::readStart(uint32_t blockNumber) {
if (type() != SD_CARD_TYPE_SDHC) blockNumber <<= 9; if (type() != SD_CARD_TYPE_SDHC) blockNumber <<= 9;
if (cardCommand(CMD18, blockNumber)) { if (cardCommand(CMD18, blockNumber)) {
error(SD_CARD_ERROR_CMD18); error(SD_CARD_ERROR_CMD18);
goto FAIL; chipSelectHigh();
return false;
} }
chipSelectHigh(); chipSelectHigh();
return true; 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 * \return true for success, false for failure.
* the value zero, false, is returned for failure.
*/ */
bool Sd2Card::readStop() { bool Sd2Card::readStop() {
chipSelectLow(); chipSelectLow();
if (cardCommand(CMD12, 0)) { if (cardCommand(CMD12, 0)) {
error(SD_CARD_ERROR_CMD12); error(SD_CARD_ERROR_CMD12);
goto FAIL; chipSelectHigh();
return false;
} }
chipSelectHigh(); chipSelectHigh();
return true; return true;
FAIL:
chipSelectHigh();
return false;
} }
//------------------------------------------------------------------------------
/** /**
* Set the SPI clock rate. * Set the SPI clock rate.
* *
@ -596,25 +588,22 @@ bool Sd2Card::setSckRate(uint8_t sckRateID) {
spiRate_ = sckRateID; spiRate_ = sckRateID;
return true; return true;
} }
//------------------------------------------------------------------------------
// wait for card to go not busy // wait for card to go not busy
bool Sd2Card::waitNotBusy(uint16_t timeoutMillis) { bool Sd2Card::waitNotBusy(uint16_t timeoutMillis) {
uint16_t t0 = millis(); uint16_t t0 = millis();
while (spiRec() != 0XFF) { while (spiRec() != 0XFF)
if (((uint16_t)millis() - t0) >= timeoutMillis) goto FAIL; if (((uint16_t)millis() - t0) >= timeoutMillis) return false;
}
return true; return true;
FAIL:
return false;
} }
//------------------------------------------------------------------------------
/** /**
* Writes a 512 byte block to an SD card. * Writes a 512 byte block to an SD card.
* *
* \param[in] blockNumber Logical block to be written. * \param[in] blockNumber Logical block to be written.
* \param[in] src Pointer to the location of the data 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 * \return true for success, false for failure.
* the value zero, false, is returned for failure.
*/ */
bool Sd2Card::writeBlock(uint32_t blockNumber, const uint8_t* src) { bool Sd2Card::writeBlock(uint32_t blockNumber, const uint8_t* src) {
// use address if not SDHC card // use address if not SDHC card
@ -641,25 +630,24 @@ bool Sd2Card::writeBlock(uint32_t blockNumber, const uint8_t* src) {
chipSelectHigh(); chipSelectHigh();
return false; 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. * \param[in] src Pointer to the location of the data to be written.
* \return The value one, true, is returned for success and * \return true for success, false for failure.
* the value zero, false, is returned for failure.
*/ */
bool Sd2Card::writeData(const uint8_t* src) { bool Sd2Card::writeData(const uint8_t* src) {
chipSelectLow(); chipSelectLow();
// wait for previous write to finish // wait for previous write to finish
if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto FAIL; if (!waitNotBusy(SD_WRITE_TIMEOUT) || !writeData(WRITE_MULTIPLE_TOKEN, src)) {
if (!writeData(WRITE_MULTIPLE_TOKEN, src)) goto FAIL;
chipSelectHigh();
return true;
FAIL:
error(SD_CARD_ERROR_WRITE_MULTIPLE); error(SD_CARD_ERROR_WRITE_MULTIPLE);
chipSelectHigh(); chipSelectHigh();
return false; return false;
} }
//------------------------------------------------------------------------------ chipSelectHigh();
return true;
}
// send one block of data for write block or write multiple blocks // send one block of data for write block or write multiple blocks
bool Sd2Card::writeData(uint8_t token, const uint8_t* src) { bool Sd2Card::writeData(uint8_t token, const uint8_t* src) {
spiSendBlock(token, src); spiSendBlock(token, src);
@ -670,15 +658,14 @@ bool Sd2Card::writeData(uint8_t token, const uint8_t* src) {
status_ = spiRec(); status_ = spiRec();
if ((status_ & DATA_RES_MASK) != DATA_RES_ACCEPTED) { if ((status_ & DATA_RES_MASK) != DATA_RES_ACCEPTED) {
error(SD_CARD_ERROR_WRITE); error(SD_CARD_ERROR_WRITE);
goto FAIL;
}
return true;
FAIL:
chipSelectHigh(); chipSelectHigh();
return false; return false;
} }
//------------------------------------------------------------------------------ return true;
/** Start a write multiple blocks sequence. }
/**
* Start a write multiple blocks sequence.
* *
* \param[in] blockNumber Address of first block in sequence. * \param[in] blockNumber Address of first block in sequence.
* \param[in] eraseCount The number of blocks to be pre-erased. * \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() * \note This function is used with writeData() and writeStop()
* for optimized multiple block writes. * for optimized multiple block writes.
* *
* \return The value one, true, is returned for success and * \return true for success, false for failure.
* the value zero, false, is returned for failure.
*/ */
bool Sd2Card::writeStart(uint32_t blockNumber, uint32_t eraseCount) { bool Sd2Card::writeStart(uint32_t blockNumber, uint32_t eraseCount) {
// send pre-erase count // send pre-erase count
@ -707,11 +693,11 @@ bool Sd2Card::writeStart(uint32_t blockNumber, uint32_t eraseCount) {
chipSelectHigh(); chipSelectHigh();
return false; return false;
} }
//------------------------------------------------------------------------------
/** End a write multiple blocks sequence. /**
* End a write multiple blocks sequence.
* *
* \return The value one, true, is returned for success and * \return true for success, false for failure.
* the value zero, false, is returned for failure.
*/ */
bool Sd2Card::writeStop() { bool Sd2Card::writeStop() {
chipSelectLow(); chipSelectLow();
@ -726,4 +712,4 @@ bool Sd2Card::writeStop() {
return false; return false;
} }
#endif #endif // SDSUPPORT

@ -20,165 +20,119 @@
* *
*/ */
/**
* \file
* \brief Sd2Card class for V2 SD/SDHC cards
*/
/** /**
* Arduino Sd2Card Library * Arduino Sd2Card Library
* Copyright (C) 2009 by William Greiman * Copyright (C) 2009 by William Greiman
* *
* This file is part of the Arduino Sd2Card Library * 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 "SdFatConfig.h"
#include "SdInfo.h" #include "SdInfo.h"
//------------------------------------------------------------------------------
// SPI speed is F_CPU/2^(1 + index), 0 <= index <= 6 // 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 to max rate of F_CPU/2. See Sd2Card::setSckRate().
uint8_t const SPI_FULL_SPEED = 0; SPI_HALF_SPEED = 1, // Set SCK rate to F_CPU/4. See Sd2Card::setSckRate().
/** Set SCK rate to F_CPU/4. See Sd2Card::setSckRate(). */ SPI_QUARTER_SPEED = 2, // Set SCK rate to F_CPU/8. See Sd2Card::setSckRate().
uint8_t const SPI_HALF_SPEED = 1; SPI_EIGHTH_SPEED = 3, // Set SCK rate to F_CPU/16. See Sd2Card::setSckRate().
/** Set SCK rate to F_CPU/8. See Sd2Card::setSckRate(). */ SPI_SIXTEENTH_SPEED = 4; // Set SCK rate to F_CPU/32. See Sd2Card::setSckRate().
uint8_t const SPI_QUARTER_SPEED = 2;
/** Set SCK rate to F_CPU/16. See Sd2Card::setSckRate(). */ uint16_t const SD_INIT_TIMEOUT = 2000, // init timeout ms
uint8_t const SPI_EIGHTH_SPEED = 3; SD_ERASE_TIMEOUT = 10000, // erase timeout ms
/** Set SCK rate to F_CPU/32. See Sd2Card::setSckRate(). */ SD_READ_TIMEOUT = 300, // read timeout ms
uint8_t const SPI_SIXTEENTH_SPEED = 4; SD_WRITE_TIMEOUT = 600; // write time out ms
//------------------------------------------------------------------------------
/** 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;
//------------------------------------------------------------------------------
// SD card errors // SD card errors
/** timeout error for command CMD0 (initialize card in SPI mode) */ uint8_t const SD_CARD_ERROR_CMD0 = 0X1, // timeout error for command CMD0 (initialize card in SPI mode)
uint8_t const SD_CARD_ERROR_CMD0 = 0X1; SD_CARD_ERROR_CMD8 = 0X2, // CMD8 was not accepted - not a valid SD card
/** CMD8 was not accepted - not a valid SD card*/ SD_CARD_ERROR_CMD12 = 0X3, // card returned an error response for CMD12 (write stop)
uint8_t const SD_CARD_ERROR_CMD8 = 0X2; SD_CARD_ERROR_CMD17 = 0X4, // card returned an error response for CMD17 (read block)
/** card returned an error response for CMD12 (write stop) */ SD_CARD_ERROR_CMD18 = 0X5, // card returned an error response for CMD18 (read multiple block)
uint8_t const SD_CARD_ERROR_CMD12 = 0X3; SD_CARD_ERROR_CMD24 = 0X6, // card returned an error response for CMD24 (write block)
/** card returned an error response for CMD17 (read block) */ SD_CARD_ERROR_CMD25 = 0X7, // WRITE_MULTIPLE_BLOCKS command failed
uint8_t const SD_CARD_ERROR_CMD17 = 0X4; SD_CARD_ERROR_CMD58 = 0X8, // card returned an error response for CMD58 (read OCR)
/** card returned an error response for CMD18 (read multiple block) */ SD_CARD_ERROR_ACMD23 = 0X9, // SET_WR_BLK_ERASE_COUNT failed
uint8_t const SD_CARD_ERROR_CMD18 = 0X5; SD_CARD_ERROR_ACMD41 = 0XA, // ACMD41 initialization process timeout
/** card returned an error response for CMD24 (write block) */ SD_CARD_ERROR_BAD_CSD = 0XB, // card returned a bad CSR version field
uint8_t const SD_CARD_ERROR_CMD24 = 0X6; SD_CARD_ERROR_ERASE = 0XC, // erase block group command failed
/** WRITE_MULTIPLE_BLOCKS command failed */ SD_CARD_ERROR_ERASE_SINGLE_BLOCK = 0XD, // card not capable of single block erase
uint8_t const SD_CARD_ERROR_CMD25 = 0X7; SD_CARD_ERROR_ERASE_TIMEOUT = 0XE, // Erase sequence timed out
/** card returned an error response for CMD58 (read OCR) */ SD_CARD_ERROR_READ = 0XF, // card returned an error token instead of read data
uint8_t const SD_CARD_ERROR_CMD58 = 0X8; SD_CARD_ERROR_READ_REG = 0x10, // read CID or CSD failed
/** SET_WR_BLK_ERASE_COUNT failed */ SD_CARD_ERROR_READ_TIMEOUT = 0x11, // timeout while waiting for start of read data
uint8_t const SD_CARD_ERROR_ACMD23 = 0X9; SD_CARD_ERROR_STOP_TRAN = 0x12, // card did not accept STOP_TRAN_TOKEN
/** ACMD41 initialization process timeout */ SD_CARD_ERROR_WRITE = 0x13, // card returned an error token as a response to a write operation
uint8_t const SD_CARD_ERROR_ACMD41 = 0XA; SD_CARD_ERROR_WRITE_BLOCK_ZERO = 0x14, // REMOVE - not used ... attempt to write protected block zero
/** card returned a bad CSR version field */ SD_CARD_ERROR_WRITE_MULTIPLE = 0x15, // card did not go ready for a multiple block write
uint8_t const SD_CARD_ERROR_BAD_CSD = 0XB; SD_CARD_ERROR_WRITE_PROGRAMMING = 0x16, // card returned an error to a CMD13 status check after a write
/** erase block group command failed */ SD_CARD_ERROR_WRITE_TIMEOUT = 0x17, // timeout occurred during write programming
uint8_t const SD_CARD_ERROR_ERASE = 0XC; SD_CARD_ERROR_SCK_RATE = 0x18, // incorrect rate selected
/** card not capable of single block erase */ SD_CARD_ERROR_INIT_NOT_CALLED = 0x19, // init() not called
uint8_t const SD_CARD_ERROR_ERASE_SINGLE_BLOCK = 0XD; SD_CARD_ERROR_CRC = 0x20; // crc check error
/** 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;
//------------------------------------------------------------------------------
// card types // card types
/** Standard capacity V1 SD card */ uint8_t const SD_CARD_TYPE_SD1 = 1, // Standard capacity V1 SD card
uint8_t const SD_CARD_TYPE_SD1 = 1; SD_CARD_TYPE_SD2 = 2, // Standard capacity V2 SD card
/** Standard capacity V2 SD card */ SD_CARD_TYPE_SDHC = 3; // High Capacity SD card
uint8_t const SD_CARD_TYPE_SD2 = 2;
/** High Capacity SD card */
uint8_t const SD_CARD_TYPE_SDHC = 3;
/** /**
* define SOFTWARE_SPI to use bit-bang SPI * define SOFTWARE_SPI to use bit-bang SPI
*/ */
//------------------------------------------------------------------------------
#if MEGA_SOFT_SPI #if MEGA_SOFT_SPI
#define SOFTWARE_SPI #define SOFTWARE_SPI
#elif USE_SOFTWARE_SPI #elif USE_SOFTWARE_SPI
#define SOFTWARE_SPI #define SOFTWARE_SPI
#endif // MEGA_SOFT_SPI #endif
//------------------------------------------------------------------------------
// SPI pin definitions - do not edit here - change in SdFatConfig.h // SPI pin definitions - do not edit here - change in SdFatConfig.h
//
#if DISABLED(SOFTWARE_SPI) #if DISABLED(SOFTWARE_SPI)
// hardware pin defs // hardware pin defs
/** The default chip select pin for the SD card is SS. */ #define SD_CHIP_SELECT_PIN SS_PIN // The default chip select pin for the SD card is SS.
#define SD_CHIP_SELECT_PIN SS_PIN
// The following three pins must not be redefined for hardware SPI. // 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 Out Slave In pin
#define SPI_MOSI_PIN MOSI_PIN #define SPI_MISO_PIN MISO_PIN // SPI Master In Slave Out pin
/** SPI Master In Slave Out pin */ #define SPI_SCK_PIN SCK_PIN // SPI Clock pin
#define SPI_MISO_PIN MISO_PIN
/** SPI Clock pin */
#define SPI_SCK_PIN SCK_PIN
#else // SOFTWARE_SPI #else // SOFTWARE_SPI
#define SD_CHIP_SELECT_PIN SOFT_SPI_CS_PIN // SPI chip select pin
/** SPI chip select pin */ #define SPI_MOSI_PIN SOFT_SPI_MOSI_PIN // SPI Master Out Slave In pin
#define SD_CHIP_SELECT_PIN SOFT_SPI_CS_PIN #define SPI_MISO_PIN SOFT_SPI_MISO_PIN // SPI Master In Slave Out pin
/** SPI Master Out Slave In pin */ #define SPI_SCK_PIN SOFT_SPI_SCK_PIN // SPI Clock 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
#endif // SOFTWARE_SPI #endif // SOFTWARE_SPI
//------------------------------------------------------------------------------
/** /**
* \class Sd2Card * \class Sd2Card
* \brief Raw access to SD and SDHC flash memory cards. * \brief Raw access to SD and SDHC flash memory cards.
*/ */
class Sd2Card { class Sd2Card {
public: public:
/** Construct an instance of Sd2Card. */
Sd2Card() : errorCode_(SD_CARD_ERROR_INIT_NOT_CALLED), type_(0) {} Sd2Card() : errorCode_(SD_CARD_ERROR_INIT_NOT_CALLED), type_(0) {}
uint32_t cardSize(); uint32_t cardSize();
bool erase(uint32_t firstBlock, uint32_t lastBlock); bool erase(uint32_t firstBlock, uint32_t lastBlock);
bool eraseSingleBlockEnable(); bool eraseSingleBlockEnable();
/** /**
* Set SD error code. * Set SD error code.
* \param[in] code value for error code. * \param[in] code value for error code.
*/ */
void error(uint8_t code) {errorCode_ = code;} void error(uint8_t code) {errorCode_ = code;}
/** /**
* \return error code for last error. See Sd2Card.h for a list of error codes. * \return error code for last error. See Sd2Card.h for a list of error codes.
*/ */
int errorCode() const {return errorCode_;} int errorCode() const {return errorCode_;}
/** \return error data for last error. */ /** \return error data for last error. */
int errorData() const {return status_;} int errorData() const {return status_;}
/** /**
* Initialize an SD flash memory card with default clock rate and chip * Initialize an SD flash memory card with default clock rate and chip
* select pin. See sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin). * 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, bool init(uint8_t sckRateID = SPI_FULL_SPEED,
uint8_t chipSelectPin = SD_CHIP_SELECT_PIN); uint8_t chipSelectPin = SD_CHIP_SELECT_PIN);
bool readBlock(uint32_t block, uint8_t* dst); bool readBlock(uint32_t block, uint8_t* dst);
/** /**
* Read a card's CID register. The CID contains card identification * Read a card's CID register. The CID contains card identification
* information such as Manufacturer ID, Product name, Product serial * information such as Manufacturer ID, Product name, Product serial
@ -197,9 +152,8 @@ class Sd2Card {
* *
* \return true for success or false for failure. * \return true for success or false for failure.
*/ */
bool readCID(cid_t* cid) { bool readCID(cid_t* cid) { return readRegister(CMD10, cid); }
return readRegister(CMD10, cid);
}
/** /**
* Read a card's CSD register. The CSD contains Card-Specific Data that * Read a card's CSD register. The CSD contains Card-Specific Data that
* provides information regarding access to the card's contents. * provides information regarding access to the card's contents.
@ -208,14 +162,14 @@ class Sd2Card {
* *
* \return true for success or false for failure. * \return true for success or false for failure.
*/ */
bool readCSD(csd_t* csd) { bool readCSD(csd_t* csd) { return readRegister(CMD9, csd); }
return readRegister(CMD9, csd);
}
bool readData(uint8_t* dst); bool readData(uint8_t* dst);
bool readStart(uint32_t blockNumber); bool readStart(uint32_t blockNumber);
bool readStop(); bool readStop();
bool setSckRate(uint8_t sckRateID); 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. * \return 0 - SD V1, 1 - SD V2, or 3 - SDHC.
*/ */
int type() const {return type_;} int type() const {return type_;}
@ -223,13 +177,14 @@ class Sd2Card {
bool writeData(const uint8_t* src); bool writeData(const uint8_t* src);
bool writeStart(uint32_t blockNumber, uint32_t eraseCount); bool writeStart(uint32_t blockNumber, uint32_t eraseCount);
bool writeStop(); bool writeStop();
private: private:
//---------------------------------------------------------------------------- uint8_t chipSelectPin_,
uint8_t chipSelectPin_; errorCode_,
uint8_t errorCode_; spiRate_,
uint8_t spiRate_; status_,
uint8_t status_; type_;
uint8_t type_;
// private functions // private functions
uint8_t cardAcmd(uint8_t cmd, uint32_t arg) { uint8_t cardAcmd(uint8_t cmd, uint32_t arg) {
cardCommand(CMD55, 0); cardCommand(CMD55, 0);
@ -245,7 +200,5 @@ class Sd2Card {
bool waitNotBusy(uint16_t timeoutMillis); bool waitNotBusy(uint16_t timeoutMillis);
bool writeData(uint8_t token, const uint8_t* src); bool writeData(uint8_t token, const uint8_t* src);
}; };
#endif // Sd2Card_h
#endif // _SD2CARD_H_
#endif

@ -27,19 +27,21 @@
* This file is part of the Arduino Sd2Card Library * This file is part of the Arduino Sd2Card Library
*/ */
#include "Marlin.h" #include "MarlinConfig.h"
#if ENABLED(SDSUPPORT) #if ENABLED(SDSUPPORT)
#include "SdBaseFile.h" #include "SdBaseFile.h"
//------------------------------------------------------------------------------ #include "Marlin.h"
// pointer to cwd directory
SdBaseFile* SdBaseFile::cwd_ = 0; SdBaseFile* SdBaseFile::cwd_ = 0; // Pointer to Current Working Directory
// callback function for date/time // callback function for date/time
void (*SdBaseFile::dateTime_)(uint16_t* date, uint16_t* time) = 0; void (*SdBaseFile::dateTime_)(uint16_t* date, uint16_t* time) = 0;
//------------------------------------------------------------------------------
// add a cluster to a file // add a cluster to a file
bool SdBaseFile::addCluster() { 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 first cluster of file link to directory entry
if (firstCluster_ == 0) { if (firstCluster_ == 0) {
@ -47,20 +49,17 @@ bool SdBaseFile::addCluster() {
flags_ |= F_FILE_DIR_DIRTY; flags_ |= F_FILE_DIR_DIRTY;
} }
return true; return true;
FAIL:
return false;
} }
//------------------------------------------------------------------------------
// Add a cluster to a directory file and zero the cluster. // Add a cluster to a directory file and zero the cluster.
// return with first block of cluster in the cache // return with first block of cluster in the cache
bool SdBaseFile::addDirCluster() { bool SdBaseFile::addDirCluster() {
uint32_t block; uint32_t block;
// max folder size // max folder size
if (fileSize_ / sizeof(dir_t) >= 0xFFFF) goto FAIL; if (fileSize_ / sizeof(dir_t) >= 0xFFFF) return false;
if (!addCluster()) goto FAIL; if (!addCluster()) return false;
if (!vol_->cacheFlush()) goto FAIL; if (!vol_->cacheFlush()) return false;
block = vol_->clusterStartBlock(curCluster_); block = vol_->clusterStartBlock(curCluster_);
@ -72,29 +71,25 @@ bool SdBaseFile::addDirCluster() {
// zero rest of cluster // zero rest of cluster
for (uint8_t i = 1; i < vol_->blocksPerCluster_; i++) { 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 // Increase directory file size by cluster size
fileSize_ += 512UL << vol_->clusterSizeShift_; fileSize_ += 512UL << vol_->clusterSizeShift_;
return true; return true;
FAIL:
return false;
} }
//------------------------------------------------------------------------------
// cache a file's directory entry // cache a file's directory entry
// return pointer to cached entry or null for failure // return pointer to cached entry or null for failure
dir_t* SdBaseFile::cacheDirEntry(uint8_t action) { 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_; 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. * to be written to the storage device.
* *
* \return The value one, true, is returned for success and * \return true for success, false for failure.
* the value zero, false, is returned for failure.
* Reasons for failure include no file is open or an I/O error. * Reasons for failure include no file is open or an I/O error.
*/ */
bool SdBaseFile::close() { bool SdBaseFile::close() {
@ -102,41 +97,40 @@ bool SdBaseFile::close() {
type_ = FAT_FILE_TYPE_CLOSED; type_ = FAT_FILE_TYPE_CLOSED;
return rtn; 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] bgnBlock the first block address for the file.
* \param[out] endBlock the last 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 * \return true for success, false for failure.
* the value zero, false, is returned for failure.
* Reasons for failure include file is not contiguous, file has zero length * Reasons for failure include file is not contiguous, file has zero length
* or an I/O error occurred. * or an I/O error occurred.
*/ */
bool SdBaseFile::contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock) { bool SdBaseFile::contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock) {
// error if no blocks // error if no blocks
if (firstCluster_ == 0) goto FAIL; if (firstCluster_ == 0) return false;
for (uint32_t c = firstCluster_; ; c++) { for (uint32_t c = firstCluster_; ; c++) {
uint32_t next; uint32_t next;
if (!vol_->fatGet(c, &next)) goto FAIL; if (!vol_->fatGet(c, &next)) return false;
// check for contiguous // check for contiguous
if (next != (c + 1)) { if (next != (c + 1)) {
// error if not end of chain // error if not end of chain
if (!vol_->isEOC(next)) goto FAIL; if (!vol_->isEOC(next)) return false;
*bgnBlock = vol_->clusterStartBlock(firstCluster_); *bgnBlock = vol_->clusterStartBlock(firstCluster_);
*endBlock = vol_->clusterStartBlock(c) *endBlock = vol_->clusterStartBlock(c)
+ vol_->blocksPerCluster_ - 1; + vol_->blocksPerCluster_ - 1;
return true; return true;
} }
} }
FAIL:
return false; 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. * \note This function only supports short DOS 8.3 names.
* See open() for more information. * 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] path A path with a valid DOS 8.3 file name.
* \param[in] size The desired file size. * \param[in] size The desired file size.
* *
* \return The value one, true, is returned for success and * \return true for success, false for failure.
* the value zero, false, is returned for failure.
* Reasons for failure include \a path contains * Reasons for failure include \a path contains
* an invalid DOS 8.3 file name, the FAT volume has not been initialized, * 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 * a file is already open, the file already exists, the root
* directory is full or an I/O error. * directory is full or an I/O error.
* *
*/ */
bool SdBaseFile::createContiguous(SdBaseFile* dirFile, bool SdBaseFile::createContiguous(SdBaseFile* dirFile, const char* path, uint32_t size) {
const char* path, uint32_t size) {
uint32_t count; uint32_t count;
// don't allow zero length file // don't allow zero length file
if (size == 0) goto FAIL; if (size == 0) return false;
if (!open(dirFile, path, O_CREAT | O_EXCL | O_RDWR)) goto FAIL; if (!open(dirFile, path, O_CREAT | O_EXCL | O_RDWR)) return false;
// calculate number of clusters needed // calculate number of clusters needed
count = ((size - 1) >> (vol_->clusterSizeShift_ + 9)) + 1; count = ((size - 1) >> (vol_->clusterSizeShift_ + 9)) + 1;
@ -166,7 +158,7 @@ bool SdBaseFile::createContiguous(SdBaseFile* dirFile,
// allocate clusters // allocate clusters
if (!vol_->allocContiguous(count, &firstCluster_)) { if (!vol_->allocContiguous(count, &firstCluster_)) {
remove(); remove();
goto FAIL; return false;
} }
fileSize_ = size; fileSize_ = size;
@ -174,34 +166,31 @@ bool SdBaseFile::createContiguous(SdBaseFile* dirFile,
flags_ |= F_FILE_DIR_DIRTY; flags_ |= F_FILE_DIR_DIRTY;
return sync(); 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. * \param[out] dir Location for return of the file's directory entry.
* *
* \return The value one, true, is returned for success and * \return true for success, false for failure.
* the value zero, false, is returned for failure.
*/ */
bool SdBaseFile::dirEntry(dir_t* dir) { bool SdBaseFile::dirEntry(dir_t* dir) {
dir_t* p; dir_t* p;
// make sure fields on SD are correct // make sure fields on SD are correct
if (!sync()) goto FAIL; if (!sync()) return false;
// read entry // read entry
p = cacheDirEntry(SdVolume::CACHE_FOR_READ); p = cacheDirEntry(SdVolume::CACHE_FOR_READ);
if (!p) goto FAIL; if (!p) return false;
// copy to caller's struct // copy to caller's struct
memcpy(dir, p, sizeof(dir_t)); memcpy(dir, p, sizeof(dir_t));
return true; 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. * \a name in standard 8.3 short name format.
* *
* \param[in] dir The directory structure containing the name. * \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; 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. * \param[in] name Name of the file to be tested for.
* *
@ -232,7 +222,7 @@ bool SdBaseFile::exists(const char* name) {
SdBaseFile file; SdBaseFile file;
return file.open(this, name, O_READ); return file.open(this, name, O_READ);
} }
//------------------------------------------------------------------------------
/** /**
* Get a string from a file. * Get a string from a file.
* *
@ -275,15 +265,15 @@ int16_t SdBaseFile::fgets(char* str, int16_t num, char* delim) {
str[n] = '\0'; str[n] = '\0';
return n; 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. * \param[out] name An array of 13 characters for the file's name.
* *
* \return The value one, true, is returned for success and * \return true for success, false for failure.
* the value zero, false, is returned for failure.
*/ */
bool SdBaseFile::getFilename(char* name) { bool SdBaseFile::getFilename(char * const name) {
if (!isOpen()) return false; if (!isOpen()) return false;
if (isRoot()) { if (isRoot()) {
@ -299,14 +289,14 @@ bool SdBaseFile::getFilename(char* name) {
dirName(*p, name); dirName(*p, name);
return true; return true;
} }
//------------------------------------------------------------------------------
void SdBaseFile::getpos(filepos_t* pos) { void SdBaseFile::getpos(filepos_t* pos) {
pos->position = curPosition_; pos->position = curPosition_;
pos->cluster = curCluster_; pos->cluster = curCluster_;
} }
//------------------------------------------------------------------------------ /**
/** List directory contents. * List directory contents.
* *
* \param[in] pr Print stream for list. * \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 // saves 32 bytes on stack for ls recursion
// return 0 - EOF, 1 - normal file, or 2 - directory // return 0 - EOF, 1 - normal file, or 2 - directory
int8_t SdBaseFile::lsPrintNext(uint8_t flags, uint8_t indent) { 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(); MYSERIAL.println();
return DIR_IS_FILE(&dir) ? 1 : 2; 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) { bool SdBaseFile::make83Name(const char* str, uint8_t* name, const char** ptr) {
uint8_t c; uint8_t n = 7, // Max index until a dot is found
uint8_t n = 7; // max index for part before dot i = 11;
uint8_t i = 0; while (i) name[--i] = ' '; // Set whole FILENAME.EXT to spaces
// blank fill name and extension while (*str && *str != '/') { // For each character, until nul or '/'
while (i < 11) name[i++] = ' '; uint8_t c = *str++; // Get char and advance
i = 0; if (c == '.') { // For a dot...
while (*str != '\0' && *str != '/') { if (n == 10) return false; // Already moved the max index? fail!
c = *str++; n = 10; // Move the max index for full 8.3 name
if (c == '.') { i = 8; // Move up to the extension place
if (n == 10) goto FAIL; // only one dot allowed
n = 10; // max index for full 8.3 name
i = 8; // place for extension
} }
else { else {
// illegal FAT characters // Fail for illegal characters
PGM_P p = PSTR("|<>^+=?/[];,*\"\\"); PGM_P p = PSTR("|<>^+=?/[];,*\"\\");
uint8_t b; while (uint8_t b = pgm_read_byte(p++)) if (b == c) return false;
while ((b = pgm_read_byte(p++))) if (b == c) goto FAIL; if (i > n || c < 0x21 || c == 0x7F) return false; // Check size, non-printable characters
// check size and only allow ASCII printable characters name[i++] = (c < 'a' || c > 'z') ? (c) : (c + ('A' - 'a')); // Uppercase required for 8.3 name
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'));
} }
} }
*ptr = str; *ptr = str; // Set passed pointer to the end
// must have a file name, extension is optional return name[0] != ' '; // Return true if any name was set
return name[0] != ' ';
FAIL:
return false;
} }
//------------------------------------------------------------------------------
/** Make a new directory. /**
* Make a new directory.
* *
* \param[in] parent An open SdFat instance for the directory that will contain * \param[in] parent An open SdFat instance for the directory that will contain
* the new directory. * 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. * \param[in] pFlag Create missing parent directories if true.
* *
* \return The value one, true, is returned for success and * \return true for success, false for failure.
* the value zero, false, is returned for failure.
* Reasons for failure include this file is already open, \a parent is not a * 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. * 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* sub = &dir1;
SdBaseFile* start = parent; SdBaseFile* start = parent;
if (!parent || isOpen()) goto FAIL; if (!parent || isOpen()) return false;
if (*path == '/') { if (*path == '/') {
while (*path == '/') path++; while (*path == '/') path++;
if (!parent->isRoot()) { if (!parent->isRoot()) {
if (!dir2.openRoot(parent->vol_)) goto FAIL; if (!dir2.openRoot(parent->vol_)) return false;
parent = &dir2; parent = &dir2;
} }
} }
while (1) { while (1) {
if (!make83Name(path, dname, &path)) goto FAIL; if (!make83Name(path, dname, &path)) return false;
while (*path == '/') path++; while (*path == '/') path++;
if (!*path) break; if (!*path) break;
if (!sub->open(parent, dname, O_READ)) { if (!sub->open(parent, dname, O_READ)) {
if (!pFlag || !sub->mkdir(parent, dname)) { if (!pFlag || !sub->mkdir(parent, dname))
goto FAIL; return false;
}
} }
if (parent != start) parent->close(); if (parent != start) parent->close();
parent = sub; parent = sub;
sub = parent != &dir1 ? &dir1 : &dir2; sub = parent != &dir1 ? &dir1 : &dir2;
} }
return mkdir(parent, dname); return mkdir(parent, dname);
FAIL:
return false;
} }
//------------------------------------------------------------------------------
bool SdBaseFile::mkdir(SdBaseFile* parent, const uint8_t dname[11]) { bool SdBaseFile::mkdir(SdBaseFile* parent, const uint8_t dname[11]) {
uint32_t block; uint32_t block;
dir_t d; dir_t d;
dir_t* p; dir_t* p;
if (!parent->isDir()) goto FAIL; if (!parent->isDir()) return false;
// create a normal file // 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 // convert file to directory
flags_ = O_READ; flags_ = O_READ;
type_ = FAT_FILE_TYPE_SUBDIR; type_ = FAT_FILE_TYPE_SUBDIR;
// allocate and zero first cluster // allocate and zero first cluster
if (!addDirCluster())goto FAIL; if (!addDirCluster()) return false;
// force entry to SD // force entry to SD
if (!sync()) goto FAIL; if (!sync()) return false;
// cache entry - should already be in cache due to sync() call // cache entry - should already be in cache due to sync() call
p = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); p = cacheDirEntry(SdVolume::CACHE_FOR_WRITE);
if (!p) goto FAIL; if (!p) return false;
// change directory entry attribute // change directory entry attribute
p->attributes = DIR_ATT_DIRECTORY; p->attributes = DIR_ATT_DIRECTORY;
@ -498,7 +476,7 @@ bool SdBaseFile::mkdir(SdBaseFile* parent, const uint8_t dname[11]) {
// cache block for '.' and '..' // cache block for '.' and '..'
block = vol_->clusterStartBlock(firstCluster_); 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 // copy '.' to block
memcpy(&vol_->cache()->dir[0], &d, sizeof(d)); 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 // write first block
return vol_->cacheFlush(); 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] 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 * \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). * OR of open flags. see SdBaseFile::open(SdBaseFile*, const char*, uint8_t).
* *
* \return The value one, true, is returned for success and * \return true for success, false for failure.
* the value zero, false, is returned for failure.
*/ */
bool SdBaseFile::open(const char* path, uint8_t oflag) { bool SdBaseFile::open(const char* path, uint8_t oflag) {
return open(cwd_, path, 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 * \param[in] dirFile An open SdFat instance for the directory containing the
* file to be opened. * 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 * \note Directory files must be opened read only. Write and truncation is
* not allowed for directory files. * not allowed for directory files.
* *
* \return The value one, true, is returned for success and * \return true for success, false for failure.
* the value zero, false, is returned for failure.
* Reasons for failure include this file is already open, \a dirFile is not * Reasons for failure include this file is already open, \a dirFile is not
* a directory, \a path is invalid, the file does not exist * a directory, \a path is invalid, the file does not exist
* or can't be opened in the access mode specified by oflag. * 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) { bool SdBaseFile::open(SdBaseFile* dirFile, const char* path, uint8_t oflag) {
uint8_t dname[11]; uint8_t dname[11];
SdBaseFile dir1, dir2; SdBaseFile dir1, dir2;
SdBaseFile* parent = dirFile; SdBaseFile *parent = dirFile, *sub = &dir1;
SdBaseFile* sub = &dir1;
if (!dirFile) goto FAIL; if (!dirFile || isOpen()) return false;
// error if already open if (*path == '/') { // Path starts with '/'
if (isOpen()) goto FAIL; if (!dirFile->isRoot()) { // Is the passed dirFile the root?
if (!dir2.openRoot(dirFile->vol_)) return false; // Get the root in dir2, if possible
if (*path == '/') { parent = &dir2; // Change 'parent' to point at the root dir
while (*path == '/') path++;
if (!dirFile->isRoot()) {
if (!dir2.openRoot(dirFile->vol_)) goto FAIL;
parent = &dir2;
} }
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++; while (*path == '/') path++;
if (!*path) break; 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(); if (parent != dirFile) parent->close();
parent = sub; parent = sub;
sub = parent != &dir1 ? &dir1 : &dir2; sub = parent != &dir1 ? &dir1 : &dir2;
} }
return open(parent, dname, oflag); return open(parent, dname, oflag);
FAIL:
return false;
} }
//------------------------------------------------------------------------------
// open with filename in dname // open with filename in dname
bool SdBaseFile::open(SdBaseFile* dirFile, bool SdBaseFile::open(SdBaseFile* dirFile, const uint8_t dname[11], uint8_t oflag) {
const uint8_t dname[11], uint8_t oflag) { bool emptyFound = false, fileFound = false;
bool emptyFound = false;
bool fileFound = false;
uint8_t index; uint8_t index;
dir_t* p; dir_t* p;
@ -634,7 +603,7 @@ bool SdBaseFile::open(SdBaseFile* dirFile,
while (dirFile->curPosition_ < dirFile->fileSize_) { while (dirFile->curPosition_ < dirFile->fileSize_) {
index = 0XF & (dirFile->curPosition_ >> 5); index = 0XF & (dirFile->curPosition_ >> 5);
p = dirFile->readDirCache(); p = dirFile->readDirCache();
if (!p) goto FAIL; if (!p) return false;
if (p->name[0] == DIR_NAME_FREE || p->name[0] == DIR_NAME_DELETED) { if (p->name[0] == DIR_NAME_FREE || p->name[0] == DIR_NAME_DELETED) {
// remember first empty slot // remember first empty slot
@ -653,21 +622,21 @@ bool SdBaseFile::open(SdBaseFile* dirFile,
} }
if (fileFound) { if (fileFound) {
// don't open existing file if O_EXCL // don't open existing file if O_EXCL
if (oflag & O_EXCL) goto FAIL; if (oflag & O_EXCL) return false;
} }
else { else {
// don't create unless O_CREAT and O_WRITE // 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) { if (emptyFound) {
index = dirIndex_; index = dirIndex_;
p = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); p = cacheDirEntry(SdVolume::CACHE_FOR_WRITE);
if (!p) goto FAIL; if (!p) return false;
} }
else { 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 // 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 // use first entry in cluster
p = dirFile->vol_->cache()->dir; p = dirFile->vol_->cache()->dir;
@ -692,15 +661,14 @@ bool SdBaseFile::open(SdBaseFile* dirFile,
p->lastWriteTime = p->creationTime; p->lastWriteTime = p->creationTime;
// write entry to SD // write entry to SD
if (!dirFile->vol_->cacheFlush()) goto FAIL; if (!dirFile->vol_->cacheFlush()) return false;
} }
// open entry in cache // open entry in cache
return openCachedEntry(index, oflag); 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. * \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_; vol_ = dirFile->vol_;
// error if already open // 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 // 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 // seek to location of entry
if (!dirFile->seekSet(32 * index)) goto FAIL; if (!dirFile->seekSet(32 * index)) return false;
// read entry into cache // read entry into cache
p = dirFile->readDirCache(); p = dirFile->readDirCache();
if (!p) goto FAIL; if (!p) return false;
// error if empty slot or '.' or '..' // error if empty slot or '.' or '..'
if (p->name[0] == DIR_NAME_FREE || if (p->name[0] == DIR_NAME_FREE ||
p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') { p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') {
goto FAIL; return false;
} }
// open cached entry // open cached entry
return openCachedEntry(index & 0XF, oflag); return openCachedEntry(index & 0XF, oflag);
FAIL:
return false;
} }
//------------------------------------------------------------------------------
// open a cached directory entry. Assumes vol_ is initialized // open a cached directory entry. Assumes vol_ is initialized
bool SdBaseFile::openCachedEntry(uint8_t dirIndex, uint8_t oflag) { bool SdBaseFile::openCachedEntry(uint8_t dirIndex, uint8_t oflag) {
// location of entry in cache // 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; if (!vol_->chainSize(firstCluster_, &fileSize_)) goto FAIL;
type_ = FAT_FILE_TYPE_SUBDIR; type_ = FAT_FILE_TYPE_SUBDIR;
} }
else { else
goto FAIL; goto FAIL;
}
// save open flags for read/write // save open flags for read/write
flags_ = oflag & F_OFLAG; flags_ = oflag & F_OFLAG;
@ -779,12 +745,14 @@ bool SdBaseFile::openCachedEntry(uint8_t dirIndex, uint8_t oflag) {
curPosition_ = 0; curPosition_ = 0;
if ((oflag & O_TRUNC) && !truncate(0)) return false; if ((oflag & O_TRUNC) && !truncate(0)) return false;
return oflag & O_AT_END ? seekEnd(0) : true; return oflag & O_AT_END ? seekEnd(0) : true;
FAIL: FAIL:
type_ = FAT_FILE_TYPE_CLOSED; type_ = FAT_FILE_TYPE_CLOSED;
return false; 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 * \param[in] dirFile An open SdFat instance for the directory containing the
* file to be opened. * file to be opened.
@ -799,10 +767,10 @@ bool SdBaseFile::openNext(SdBaseFile* dirFile, uint8_t oflag) {
dir_t* p; dir_t* p;
uint8_t index; uint8_t index;
if (!dirFile) goto FAIL; if (!dirFile) return false;
// error if already open // error if already open
if (isOpen()) goto FAIL; if (isOpen()) return false;
vol_ = dirFile->vol_; vol_ = dirFile->vol_;
@ -811,10 +779,10 @@ bool SdBaseFile::openNext(SdBaseFile* dirFile, uint8_t oflag) {
// read entry into cache // read entry into cache
p = dirFile->readDirCache(); p = dirFile->readDirCache();
if (!p) goto FAIL; if (!p) return false;
// done if last entry // 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 '..' // skip empty slot or '.' or '..'
if (p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') { 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); return openCachedEntry(index, oflag);
} }
} }
FAIL:
return false; 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. * \param[in] dir Parent of this directory will be opened. Must not be root.
* *
* \return The value one, true, is returned for success and * \return true for success, false for failure.
* the value zero, false, is returned for failure.
*/ */
bool SdBaseFile::openParent(SdBaseFile* dir) { bool SdBaseFile::openParent(SdBaseFile* dir) {
dir_t entry; dir_t entry;
@ -844,14 +812,14 @@ bool SdBaseFile::openParent(SdBaseFile* dir) {
uint32_t cluster; uint32_t cluster;
uint32_t lbn; uint32_t lbn;
// error if already open or dir is root or dir is not a directory // 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_; vol_ = dir->vol_;
// position to '..' // position to '..'
if (!dir->seekSet(32)) goto FAIL; if (!dir->seekSet(32)) return false;
// read '..' entry // read '..' entry
if (dir->read(&entry, sizeof(entry)) != 32) goto FAIL; if (dir->read(&entry, sizeof(entry)) != 32) return false;
// verify it is '..' // verify it is '..'
if (entry.name[0] != '.' || entry.name[1] != '.') goto FAIL; if (entry.name[0] != '.' || entry.name[1] != '.') return false;
// start cluster for '..' // start cluster for '..'
cluster = entry.firstClusterLow; cluster = entry.firstClusterLow;
cluster |= (uint32_t)entry.firstClusterHigh << 16; cluster |= (uint32_t)entry.firstClusterHigh << 16;
@ -859,43 +827,42 @@ bool SdBaseFile::openParent(SdBaseFile* dir) {
// start block for '..' // start block for '..'
lbn = vol_->clusterStartBlock(cluster); lbn = vol_->clusterStartBlock(cluster);
// first block of parent dir // first block of parent dir
if (!vol_->cacheRawBlock(lbn, SdVolume::CACHE_FOR_READ)) { if (!vol_->cacheRawBlock(lbn, SdVolume::CACHE_FOR_READ)) return false;
goto FAIL;
}
p = &vol_->cacheBuffer_.dir[1]; p = &vol_->cacheBuffer_.dir[1];
// verify name for '../..' // 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 // '..' is pointer to first cluster of parent. open '../..' to find parent
if (p->firstClusterHigh == 0 && p->firstClusterLow == 0) { if (p->firstClusterHigh == 0 && p->firstClusterLow == 0) {
if (!file.openRoot(dir->volume())) goto FAIL; if (!file.openRoot(dir->volume())) return false;
}
else if (!file.openCachedEntry(1, O_READ)) {
goto FAIL;
} }
else if (!file.openCachedEntry(1, O_READ))
return false;
// search for parent in '../..' // search for parent in '../..'
do { do {
if (file.readDir(&entry, NULL) != 32) goto FAIL; if (file.readDir(&entry, NULL) != 32) return false;
c = entry.firstClusterLow; c = entry.firstClusterLow;
c |= (uint32_t)entry.firstClusterHigh << 16; c |= (uint32_t)entry.firstClusterHigh << 16;
} while (c != cluster); } while (c != cluster);
// open parent // open parent
return open(&file, file.curPosition() / 32 - 1, O_READ); return open(&file, file.curPosition() / 32 - 1, O_READ);
FAIL:
return false;
} }
//------------------------------------------------------------------------------ #endif
/** Open a volume's root directory.
/**
* Open a volume's root directory.
* *
* \param[in] vol The FAT volume containing the root directory to be opened. * \param[in] vol The FAT volume containing the root directory to be opened.
* *
* \return The value one, true, is returned for success and * \return true for success, false for failure.
* the value zero, false, is returned for failure.
* Reasons for failure include the file is already open, the FAT volume has * Reasons for failure include the file is already open, the FAT volume has
* not been initialized or it a FAT12 volume. * not been initialized or it a FAT12 volume.
*/ */
bool SdBaseFile::openRoot(SdVolume* vol) { bool SdBaseFile::openRoot(SdVolume* vol) {
// error if file is already open // error if file is already open
if (isOpen()) goto FAIL; if (isOpen()) return false;
if (vol->fatType() == 16 || (FAT12_SUPPORT && vol->fatType() == 12)) { if (vol->fatType() == 16 || (FAT12_SUPPORT && vol->fatType() == 12)) {
type_ = FAT_FILE_TYPE_ROOT_FIXED; type_ = FAT_FILE_TYPE_ROOT_FIXED;
@ -905,29 +872,25 @@ bool SdBaseFile::openRoot(SdVolume* vol) {
else if (vol->fatType() == 32) { else if (vol->fatType() == 32) {
type_ = FAT_FILE_TYPE_ROOT32; type_ = FAT_FILE_TYPE_ROOT32;
firstCluster_ = vol->rootDirStart(); firstCluster_ = vol->rootDirStart();
if (!vol->chainSize(firstCluster_, &fileSize_)) goto FAIL; if (!vol->chainSize(firstCluster_, &fileSize_)) return false;
} }
else { else // volume is not initialized, invalid, or FAT12 without support
// volume is not initialized, invalid, or FAT12 without support
return false; return false;
}
vol_ = vol; vol_ = vol;
// read only // read only
flags_ = O_READ; flags_ = O_READ;
// set to start of file // set to start of file
curCluster_ = 0; curCluster_ = curPosition_ = 0;
curPosition_ = 0;
// root has no directory entry // root has no directory entry
dirBlock_ = 0; dirBlock_ = dirIndex_ = 0;
dirIndex_ = 0;
return true; 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; * \return The byte if no error and not at eof else -1;
*/ */
@ -939,50 +902,24 @@ int SdBaseFile::peek() {
return c; 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 // print uint8_t with width 2
static void print2u(uint8_t v) { static void print2u(uint8_t v) {
if (v < 10) MYSERIAL.write('0'); if (v < 10) MYSERIAL.write('0');
MYSERIAL.print(v, DEC); MYSERIAL.print(v, DEC);
} }
//------------------------------------------------------------------------------
/** %Print a directory date field to Serial. /**
* %Print a directory date field to Serial.
* *
* Format is yyyy-mm-dd. * Format is yyyy-mm-dd.
* *
* \param[in] fatDate The date field from a directory entry. * \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. * Format is yyyy-mm-dd.
* *
@ -997,8 +934,9 @@ void SdBaseFile::printFatDate(uint16_t fatDate) {
print2u(FAT_DAY(fatDate)); print2u(FAT_DAY(fatDate));
} }
//------------------------------------------------------------------------------
/** %Print a directory time field. /**
* %Print a directory time field.
* *
* Format is hh:mm:ss. * Format is hh:mm:ss.
* *
@ -1012,11 +950,11 @@ void SdBaseFile::printFatTime(uint16_t fatTime) {
MYSERIAL.write(':'); MYSERIAL.write(':');
print2u(FAT_SECOND(fatTime)); 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 * \return true for success, false for failure.
* the value zero, false, is returned for failure.
*/ */
bool SdBaseFile::printName() { bool SdBaseFile::printName() {
char name[FILENAME_LENGTH]; char name[FILENAME_LENGTH];
@ -1024,8 +962,9 @@ bool SdBaseFile::printName() {
MYSERIAL.print(name); MYSERIAL.print(name);
return true; 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. * \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. * If an error occurs or end of file is reached -1 is returned.
@ -1034,8 +973,9 @@ int16_t SdBaseFile::read() {
uint8_t b; uint8_t b;
return read(&b, 1) == 1 ? b : -1; 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. * \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) { int16_t SdBaseFile::read(void* buf, uint16_t nbyte) {
uint8_t* dst = reinterpret_cast<uint8_t*>(buf); uint8_t* dst = reinterpret_cast<uint8_t*>(buf);
uint16_t offset; uint16_t offset, toRead;
uint16_t toRead;
uint32_t block; // raw device block number uint32_t block; // raw device block number
// error if not open or write only // 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 // max bytes left in file
NOMORE(nbyte, fileSize_ - curPosition_); NOMORE(nbyte, fileSize_ - curPosition_);
@ -1071,14 +1010,10 @@ int16_t SdBaseFile::read(void* buf, uint16_t nbyte) {
uint8_t blockOfCluster = vol_->blockOfCluster(curPosition_); uint8_t blockOfCluster = vol_->blockOfCluster(curPosition_);
if (offset == 0 && blockOfCluster == 0) { if (offset == 0 && blockOfCluster == 0) {
// start of new cluster // start of new cluster
if (curPosition_ == 0) { if (curPosition_ == 0)
// use first cluster in file curCluster_ = firstCluster_; // use first cluster in file
curCluster_ = firstCluster_; else if (!vol_->fatGet(curCluster_, &curCluster_)) // get next cluster from FAT
} return -1;
else {
// get next cluster from FAT
if (!vol_->fatGet(curCluster_, &curCluster_)) goto FAIL;
}
} }
block = vol_->clusterStartBlock(curCluster_) + blockOfCluster; block = vol_->clusterStartBlock(curCluster_) + blockOfCluster;
} }
@ -1089,11 +1024,11 @@ int16_t SdBaseFile::read(void* buf, uint16_t nbyte) {
// no buffering needed if n == 512 // no buffering needed if n == 512
if (n == 512 && block != vol_->cacheBlockNumber()) { if (n == 512 && block != vol_->cacheBlockNumber()) {
if (!vol_->readBlock(block, dst)) goto FAIL; if (!vol_->readBlock(block, dst)) return -1;
} }
else { else {
// read block to cache and copy data to caller // 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; uint8_t* src = vol_->cache()->data + offset;
memcpy(dst, src, n); memcpy(dst, src, n);
} }
@ -1102,8 +1037,6 @@ int16_t SdBaseFile::read(void* buf, uint16_t nbyte) {
toRead -= n; toRead -= n;
} }
return nbyte; 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 // Read next directory entry into the cache
// Assumes file is correctly positioned // Assumes file is correctly positioned
dir_t* SdBaseFile::readDirCache() { dir_t* SdBaseFile::readDirCache() {
uint8_t i; uint8_t i;
// error if not directory // error if not directory
if (!isDir()) goto FAIL; if (!isDir()) return 0;
// index of entry in cache // index of entry in cache
i = (curPosition_ >> 5) & 0XF; i = (curPosition_ >> 5) & 0XF;
// use read to locate and cache block // use read to locate and cache block
if (read() < 0) goto FAIL; if (read() < 0) return 0;
// advance to next entry // advance to next entry
curPosition_ += 31; curPosition_ += 31;
// return pointer to entry // return pointer to entry
return vol_->cache()->dir + i; 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. * 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 * 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". * "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 * \return true for success, false for failure.
* the value zero, false, is returned for failure.
* Reasons for failure include the file read-only, is a directory, * Reasons for failure include the file read-only, is a directory,
* or an I/O error occurred. * or an I/O error occurred.
*/ */
bool SdBaseFile::remove() { bool SdBaseFile::remove() {
dir_t* d; dir_t* d;
// free any clusters - will fail if read-only or directory // free any clusters - will fail if read-only or directory
if (!truncate(0)) goto FAIL; if (!truncate(0)) return false;
// cache directory entry // cache directory entry
d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE);
if (!d) goto FAIL; if (!d) return false;
// mark entry deleted // mark entry deleted
d->name[0] = DIR_NAME_DELETED; d->name[0] = DIR_NAME_DELETED;
@ -1209,11 +1140,10 @@ bool SdBaseFile::remove() {
// write entry to SD // write entry to SD
return vol_->cacheFlush(); return vol_->cacheFlush();
return true; return true;
FAIL:
return false;
} }
//------------------------------------------------------------------------------
/** Remove a file. /**
* Remove a file.
* *
* The directory entry and all data for the file are deleted. * 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 * 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". * "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 * \return true for success, false for failure.
* the value zero, false, is returned for failure.
* Reasons for failure include the file is a directory, is read only, * Reasons for failure include the file is a directory, is read only,
* \a dirFile is not a directory, \a path is not found * \a dirFile is not a directory, \a path is not found
* or an I/O error occurred. * or an I/O error occurred.
*/ */
bool SdBaseFile::remove(SdBaseFile* dirFile, const char* path) { bool SdBaseFile::remove(SdBaseFile* dirFile, const char* path) {
SdBaseFile file; SdBaseFile file;
if (!file.open(dirFile, path, O_WRITE)) goto FAIL; return file.open(dirFile, path, O_WRITE) ? file.remove() : false;
return file.remove();
FAIL:
// can't set iostate - static function
return false;
} }
//------------------------------------------------------------------------------
/** Rename a file or subdirectory. /**
* Rename a file or subdirectory.
* *
* \param[in] dirFile Directory for the new path. * \param[in] dirFile Directory for the new path.
* \param[in] newPath New path name for the file/directory. * \param[in] newPath New path name for the file/directory.
* *
* \return The value one, true, is returned for success and * \return true for success, false for failure.
* the value zero, false, is returned for failure.
* Reasons for failure include \a dirFile is not open or is not a directory * 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. * 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; dir_t* d;
// must be an open file or subdirectory // must be an open file or subdirectory
if (!(isFile() || isSubDir())) goto FAIL; if (!(isFile() || isSubDir())) return false;
// can't move file // can't move file
if (vol_ != dirFile->vol_) goto FAIL; if (vol_ != dirFile->vol_) return false;
// sync() and cache directory entry // sync() and cache directory entry
sync(); sync();
d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE);
if (!d) goto FAIL; if (!d) return false;
// save directory entry // save directory entry
memcpy(&entry, d, sizeof(entry)); memcpy(&entry, d, sizeof(entry));
@ -1295,7 +1220,7 @@ bool SdBaseFile::rename(SdBaseFile* dirFile, const char* newPath) {
// cache new directory entry // cache new directory entry
d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE);
if (!d) goto FAIL; if (!d) return false;
// copy all but name field to new directory entry // copy all but name field to new directory entry
memcpy(&d->attributes, &entry.attributes, sizeof(entry) - sizeof(d->name)); memcpy(&d->attributes, &entry.attributes, sizeof(entry) - sizeof(d->name));
@ -1304,31 +1229,30 @@ bool SdBaseFile::rename(SdBaseFile* dirFile, const char* newPath) {
if (dirCluster) { if (dirCluster) {
// get new dot dot // get new dot dot
uint32_t block = vol_->clusterStartBlock(dirCluster); 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)); memcpy(&entry, &vol_->cache()->dir[1], sizeof(entry));
// free unused cluster // free unused cluster
if (!vol_->freeChain(dirCluster)) goto FAIL; if (!vol_->freeChain(dirCluster)) return false;
// store new dot dot // store new dot dot
block = vol_->clusterStartBlock(firstCluster_); 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)); memcpy(&vol_->cache()->dir[1], &entry, sizeof(entry));
} }
return vol_->cacheFlush(); return vol_->cacheFlush();
restore: restore:
d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); if ((d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE))) {
if (!d) goto FAIL;
// restore entry // restore entry
d->name[0] = entry.name[0]; d->name[0] = entry.name[0];
vol_->cacheFlush(); vol_->cacheFlush();
}
FAIL:
return false; 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 * 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 * 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 * 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". * long name "New folder" you should not delete the 8.3 name "NEWFOL~1".
* *
* \return The value one, true, is returned for success and * \return true for success, false for failure.
* the value zero, false, is returned for failure.
* Reasons for failure include the file is not a directory, is the root * Reasons for failure include the file is not a directory, is the root
* directory, is not empty, or an I/O error occurred. * directory, is not empty, or an I/O error occurred.
*/ */
bool SdBaseFile::rmdir() { bool SdBaseFile::rmdir() {
// must be open subdirectory // must be open subdirectory
if (!isSubDir()) goto FAIL; if (!isSubDir()) return false;
rewind(); rewind();
// make sure directory is empty // make sure directory is empty
while (curPosition_ < fileSize_) { while (curPosition_ < fileSize_) {
dir_t* p = readDirCache(); dir_t* p = readDirCache();
if (!p) goto FAIL; if (!p) return false;
// done if past last used entry // done if past last used entry
if (p->name[0] == DIR_NAME_FREE) break; if (p->name[0] == DIR_NAME_FREE) break;
// skip empty slot, '.' or '..' // skip empty slot, '.' or '..'
if (p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') continue; if (p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') continue;
// error not empty // 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 // convert empty directory to normal file for remove
type_ = FAT_FILE_TYPE_NORMAL; type_ = FAT_FILE_TYPE_NORMAL;
flags_ |= O_WRITE; flags_ |= O_WRITE;
return remove(); 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 * This is like the Unix/Linux 'rm -rf *' if called with the root directory
* hence the name. * hence the name.
@ -1380,8 +1302,7 @@ bool SdBaseFile::rmdir() {
* \note This function should not be used to delete the 8.3 version of * \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(). * a directory that has a long name. See remove() and rmdir().
* *
* \return The value one, true, is returned for success and * \return true for success, false for failure.
* the value zero, false, is returned for failure.
*/ */
bool SdBaseFile::rmRfStar() { bool SdBaseFile::rmRfStar() {
uint32_t index; uint32_t index;
@ -1392,7 +1313,7 @@ bool SdBaseFile::rmRfStar() {
index = curPosition_ / 32; index = curPosition_ / 32;
dir_t* p = readDirCache(); dir_t* p = readDirCache();
if (!p) goto FAIL; if (!p) return false;
// done if past last entry // done if past last entry
if (p->name[0] == DIR_NAME_FREE) break; 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 // skip if part of long file name or volume label in root
if (!DIR_IS_FILE_OR_SUBDIR(p)) continue; if (!DIR_IS_FILE_OR_SUBDIR(p)) continue;
if (!f.open(this, index, O_READ)) goto FAIL; if (!f.open(this, index, O_READ)) return false;
if (f.isSubDir()) { if (f.isSubDir()) {
// recursively delete // recursively delete
if (!f.rmRfStar()) goto FAIL; if (!f.rmRfStar()) return false;
} }
else { else {
// ignore read-only // ignore read-only
f.flags_ |= O_WRITE; f.flags_ |= O_WRITE;
if (!f.remove()) goto FAIL; if (!f.remove()) return false;
} }
// position to next entry if required // position to next entry if required
if (curPosition_ != (32 * (index + 1))) { 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 // don't try to delete root
if (!isRoot()) { if (!isRoot()) {
if (!rmdir()) goto FAIL; if (!rmdir()) return false;
} }
return true; 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. * \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; writeError = false;
open(path, oflag); 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. * \param[in] pos The new position in bytes from the beginning of the file.
* *
* \return The value one, true, is returned for success and * \return true for success, false for failure.
* the value zero, false, is returned for failure.
*/ */
bool SdBaseFile::seekSet(uint32_t pos) { bool SdBaseFile::seekSet(const uint32_t pos) {
uint32_t nCur; uint32_t nCur, nNew;
uint32_t nNew;
// error if file not open or seek past end of file // 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) { if (type_ == FAT_FILE_TYPE_ROOT_FIXED) {
curPosition_ = pos; curPosition_ = pos;
goto done; return true;
} }
if (pos == 0) { if (pos == 0) {
// set position to start of file curCluster_ = curPosition_ = 0; // set position to start of file
curCluster_ = 0; return true;
curPosition_ = 0;
goto done;
} }
// calculate cluster index for cur and new position // calculate cluster index for cur and new position
nCur = (curPosition_ - 1) >> (vol_->clusterSizeShift_ + 9); nCur = (curPosition_ - 1) >> (vol_->clusterSizeShift_ + 9);
nNew = (pos - 1) >> (vol_->clusterSizeShift_ + 9); nNew = (pos - 1) >> (vol_->clusterSizeShift_ + 9);
if (nNew < nCur || curPosition_ == 0) { if (nNew < nCur || curPosition_ == 0)
// must follow chain from first cluster curCluster_ = firstCluster_; // must follow chain from first cluster
curCluster_ = firstCluster_; else
} nNew -= nCur; // advance from curPosition
else {
// advance from curPosition
nNew -= nCur;
}
while (nNew--) {
if (!vol_->fatGet(curCluster_, &curCluster_)) goto FAIL;
}
curPosition_ = pos;
done: while (nNew--)
return true; if (!vol_->fatGet(curCluster_, &curCluster_)) return false;
FAIL: curPosition_ = pos;
return false; return true;
} }
//------------------------------------------------------------------------------
void SdBaseFile::setpos(filepos_t* pos) { void SdBaseFile::setpos(filepos_t* pos) {
curPosition_ = pos->position; curPosition_ = pos->position;
curCluster_ = pos->cluster; 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. * to be written to the storage device.
* *
* \return The value one, true, is returned for success and * \return true for success, false for failure.
* the value zero, false, is returned for failure.
* Reasons for failure include a call to sync() before a file has been * Reasons for failure include a call to sync() before a file has been
* opened or an I/O error. * opened or an I/O error.
*/ */
@ -1530,8 +1440,9 @@ bool SdBaseFile::sync() {
writeError = true; writeError = true;
return false; return false;
} }
//------------------------------------------------------------------------------
/** Copy a file's timestamps /**
* Copy a file's timestamps
* *
* \param[in] file File to copy timestamps from. * \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 * Modify and access timestamps may be overwritten if a date time callback
* function has been set by dateTimeCallback(). * function has been set by dateTimeCallback().
* *
* \return The value one, true, is returned for success and * \return true for success, false for failure.
* the value zero, false, is returned for failure.
*/ */
bool SdBaseFile::timestamp(SdBaseFile* file) { bool SdBaseFile::timestamp(SdBaseFile* file) {
dir_t* d; dir_t* d;
dir_t dir; dir_t dir;
// get timestamps // get timestamps
if (!file->dirEntry(&dir)) goto FAIL; if (!file->dirEntry(&dir)) return false;
// update directory fields // update directory fields
if (!sync()) goto FAIL; if (!sync()) return false;
d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE);
if (!d) goto FAIL; if (!d) return false;
// copy timestamps // copy timestamps
d->lastAccessDate = dir.lastAccessDate; d->lastAccessDate = dir.lastAccessDate;
@ -1565,12 +1475,10 @@ bool SdBaseFile::timestamp(SdBaseFile* file) {
// write back entry // write back entry
return vol_->cacheFlush(); 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 * \param[in] flags Values for \a flags are constructed by a bitwise-inclusive
* OR of flags from the following list * 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 * Modify and access timestamps may be overwritten if a date time callback
* function has been set by dateTimeCallback(). * function has been set by dateTimeCallback().
* *
* \return The value one, true, is returned for success and * \return true for success, false for failure.
* the value zero, false, is returned for failure.
*/ */
bool SdBaseFile::timestamp(uint8_t flags, uint16_t year, uint8_t month, bool SdBaseFile::timestamp(uint8_t flags, uint16_t year, uint8_t month,
uint8_t day, uint8_t hour, uint8_t minute, uint8_t second) { uint8_t day, uint8_t hour, uint8_t minute, uint8_t second) {
uint16_t dirDate; uint16_t dirDate, dirTime;
uint16_t dirTime;
dir_t* d; dir_t* d;
if (!isOpen() if (!isOpen()
@ -1619,13 +1525,13 @@ bool SdBaseFile::timestamp(uint8_t flags, uint16_t year, uint8_t month,
|| hour > 23 || hour > 23
|| minute > 59 || minute > 59
|| second > 59) { || second > 59) {
goto FAIL; return false;
} }
// update directory entry // update directory entry
if (!sync()) goto FAIL; if (!sync()) return false;
d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE); d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE);
if (!d) goto FAIL; if (!d) return false;
dirDate = FAT_DATE(year, month, day); dirDate = FAT_DATE(year, month, day);
dirTime = FAT_TIME(hour, minute, second); 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; d->lastWriteTime = dirTime;
} }
return vol_->cacheFlush(); 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 * will be maintained if it is less than or equal to \a length otherwise
* it will be set to end of file. * it will be set to end of file.
* *
* \param[in] length The desired length for the file. * \param[in] length The desired length for the file.
* *
* \return The value one, true, is returned for success and * \return true for success, false for failure.
* the value zero, false, is returned for failure.
* Reasons for failure include file is read only, file is a directory, * 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. * \a length is greater than the current file size or an I/O error occurs.
*/ */
bool SdBaseFile::truncate(uint32_t length) { bool SdBaseFile::truncate(uint32_t length) {
uint32_t newPos; uint32_t newPos;
// error if not a normal file or read-only // 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 // 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 // fileSize and length are zero - nothing to do
if (fileSize_ == 0) return true; if (fileSize_ == 0) return true;
@ -1673,23 +1577,23 @@ bool SdBaseFile::truncate(uint32_t length) {
newPos = curPosition_ > length ? length : curPosition_; newPos = curPosition_ > length ? length : curPosition_;
// position to last cluster in truncated file // position to last cluster in truncated file
if (!seekSet(length)) goto FAIL; if (!seekSet(length)) return false;
if (length == 0) { if (length == 0) {
// free all clusters // free all clusters
if (!vol_->freeChain(firstCluster_)) goto FAIL; if (!vol_->freeChain(firstCluster_)) return false;
firstCluster_ = 0; firstCluster_ = 0;
} }
else { else {
uint32_t toFree; uint32_t toFree;
if (!vol_->fatGet(curCluster_, &toFree)) goto FAIL; if (!vol_->fatGet(curCluster_, &toFree)) return false;
if (!vol_->isEOC(toFree)) { if (!vol_->isEOC(toFree)) {
// free extra clusters // free extra clusters
if (!vol_->freeChain(toFree)) goto FAIL; if (!vol_->freeChain(toFree)) return false;
// current cluster is end of chain // current cluster is end of chain
if (!vol_->fatPutEOC(curCluster_)) goto FAIL; if (!vol_->fatPutEOC(curCluster_)) return false;
} }
} }
fileSize_ = length; fileSize_ = length;
@ -1697,16 +1601,14 @@ bool SdBaseFile::truncate(uint32_t length) {
// need to update directory entry // need to update directory entry
flags_ |= F_FILE_DIR_DIRTY; flags_ |= F_FILE_DIR_DIRTY;
if (!sync()) goto FAIL; if (!sync()) return false;
// set file to correct position // set file to correct position
return seekSet(newPos); 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 * \note Data is moved to the cache but may not be written to the
* storage device until sync() is called. * storage device until sync() is called.
@ -1816,11 +1718,9 @@ int16_t SdBaseFile::write(const void* buf, uint16_t nbyte) {
writeError = true; writeError = true;
return -1; 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
#endif // SDSUPPORT

@ -20,208 +20,196 @@
* *
*/ */
/**
* \file
* \brief SdBaseFile class
*/
/** /**
* Arduino SdFat Library * Arduino SdFat Library
* Copyright (C) 2009 by William Greiman * Copyright (C) 2009 by William Greiman
* *
* This file is part of the Arduino Sd2Card Library * This file is part of the Arduino Sd2Card Library
*/ */
#include "Marlin.h" #ifndef _SDBASEFILE_H_
#if ENABLED(SDSUPPORT) #define _SDBASEFILE_H_
#ifndef SdBaseFile_h
#define SdBaseFile_h
/**
* \file
* \brief SdBaseFile class
*/
#include "Marlin.h"
#include "SdFatConfig.h" #include "SdFatConfig.h"
#include "SdVolume.h" #include "SdVolume.h"
//------------------------------------------------------------------------------
/** /**
* \struct filepos_t * \struct filepos_t
* \brief internal type for istream * \brief internal type for istream
* do not use in user apps * do not use in user apps
*/ */
struct filepos_t { struct filepos_t {
/** stream position */ uint32_t position; // stream byte position
uint32_t position; uint32_t cluster; // cluster of position
/** cluster for position */
uint32_t cluster;
filepos_t() : position(0), cluster(0) {} filepos_t() : position(0), cluster(0) {}
}; };
// use the gnu style oflag in open() // use the gnu style oflag in open()
/** open() oflag for reading */ uint8_t const O_READ = 0x01, // open() oflag for reading
uint8_t const O_READ = 0x01; O_RDONLY = O_READ, // open() oflag - same as O_IN
/** open() oflag - same as O_IN */ O_WRITE = 0x02, // open() oflag for write
uint8_t const O_RDONLY = O_READ; O_WRONLY = O_WRITE, // open() oflag - same as O_WRITE
/** open() oflag for write */ O_RDWR = (O_READ | O_WRITE), // open() oflag for reading and writing
uint8_t const O_WRITE = 0x02; O_ACCMODE = (O_READ | O_WRITE), // open() oflag mask for access modes
/** open() oflag - same as O_WRITE */ O_APPEND = 0x04, // The file offset shall be set to the end of the file prior to each write.
uint8_t const O_WRONLY = O_WRITE; O_SYNC = 0x08, // Synchronous writes - call sync() after each write
/** open() oflag for reading and writing */ O_TRUNC = 0x10, // Truncate the file to zero length
uint8_t const O_RDWR = (O_READ | O_WRITE); O_AT_END = 0x20, // Set the initial position at the end of the file
/** open() oflag mask for access modes */ O_CREAT = 0x40, // Create the file if nonexistent
uint8_t const O_ACCMODE = (O_READ | O_WRITE); O_EXCL = 0x80; // If O_CREAT and O_EXCL are set, open() shall fail if the file exists
/** 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;
// SdBaseFile class static and const definitions // SdBaseFile class static and const definitions
// flags for ls() // flags for ls()
/** ls() flag to print modify date */ uint8_t const LS_DATE = 1, // ls() flag to print modify date
uint8_t const LS_DATE = 1; LS_SIZE = 2, // ls() flag to print file size
/** ls() flag to print file size */ LS_R = 4; // ls() flag for recursive list of subdirectories
uint8_t const LS_SIZE = 2;
/** ls() flag for recursive list of subdirectories */
uint8_t const LS_R = 4;
// flags for timestamp // flags for timestamp
/** set the file's last access date */ uint8_t const T_ACCESS = 1, // Set the file's last access date
uint8_t const T_ACCESS = 1; T_CREATE = 2, // Set the file's creation date and time
/** set the file's creation date and time */ T_WRITE = 4; // Set the file's write date and time
uint8_t const T_CREATE = 2;
/** Set the file's write date and time */
uint8_t const T_WRITE = 4;
// values for type_ // values for type_
/** This file has not been opened. */ uint8_t const FAT_FILE_TYPE_CLOSED = 0, // This file has not been opened.
uint8_t const FAT_FILE_TYPE_CLOSED = 0; FAT_FILE_TYPE_NORMAL = 1, // A normal file
/** A normal file */ FAT_FILE_TYPE_ROOT_FIXED = 2, // A FAT12 or FAT16 root directory
uint8_t const FAT_FILE_TYPE_NORMAL = 1; FAT_FILE_TYPE_ROOT32 = 3, // A FAT32 root directory
/** A FAT12 or FAT16 root directory */ FAT_FILE_TYPE_SUBDIR = 4, // A subdirectory file
uint8_t const FAT_FILE_TYPE_ROOT_FIXED = 2; FAT_FILE_TYPE_MIN_DIR = FAT_FILE_TYPE_ROOT_FIXED; // Test value for directory type
/** A FAT32 root directory */
uint8_t const FAT_FILE_TYPE_ROOT32 = 3; /**
/** A subdirectory file*/ * date field for FAT directory entry
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
* \param[in] year [1980,2107] * \param[in] year [1980,2107]
* \param[in] month [1,12] * \param[in] month [1,12]
* \param[in] day [1,31] * \param[in] day [1,31]
* *
* \return Packed date for dir_t entry. * \return Packed date for dir_t entry.
*/ */
static inline uint16_t FAT_DATE(uint16_t year, uint8_t month, uint8_t day) { static inline uint16_t FAT_DATE(uint16_t year, uint8_t month, uint8_t day) { return (year - 1980) << 9 | month << 5 | day; }
return (year - 1980) << 9 | month << 5 | day;
} /**
/** year part of FAT directory date field * year part of FAT directory date field
* \param[in] fatDate Date in packed dir format. * \param[in] fatDate Date in packed dir format.
* *
* \return Extracted year [1980,2107] * \return Extracted year [1980,2107]
*/ */
static inline uint16_t FAT_YEAR(uint16_t fatDate) { static inline uint16_t FAT_YEAR(uint16_t fatDate) { return 1980 + (fatDate >> 9); }
return 1980 + (fatDate >> 9);
} /**
/** month part of FAT directory date field * month part of FAT directory date field
* \param[in] fatDate Date in packed dir format. * \param[in] fatDate Date in packed dir format.
* *
* \return Extracted month [1,12] * \return Extracted month [1,12]
*/ */
static inline uint8_t FAT_MONTH(uint16_t fatDate) { static inline uint8_t FAT_MONTH(uint16_t fatDate) { return (fatDate >> 5) & 0XF; }
return (fatDate >> 5) & 0XF;
} /**
/** day part of FAT directory date field * day part of FAT directory date field
* \param[in] fatDate Date in packed dir format. * \param[in] fatDate Date in packed dir format.
* *
* \return Extracted day [1,31] * \return Extracted day [1,31]
*/ */
static inline uint8_t FAT_DAY(uint16_t fatDate) { static inline uint8_t FAT_DAY(uint16_t fatDate) { return fatDate & 0x1F; }
return fatDate & 0x1F;
} /**
/** time field for FAT directory entry * time field for FAT directory entry
* \param[in] hour [0,23] * \param[in] hour [0,23]
* \param[in] minute [0,59] * \param[in] minute [0,59]
* \param[in] second [0,59] * \param[in] second [0,59]
* *
* \return Packed time for dir_t entry. * \return Packed time for dir_t entry.
*/ */
static inline uint16_t FAT_TIME(uint8_t hour, uint8_t minute, uint8_t second) { static inline uint16_t FAT_TIME(uint8_t hour, uint8_t minute, uint8_t second) { return hour << 11 | minute << 5 | second >> 1; }
return hour << 11 | minute << 5 | second >> 1;
} /**
/** hour part of FAT directory time field * hour part of FAT directory time field
* \param[in] fatTime Time in packed dir format. * \param[in] fatTime Time in packed dir format.
* *
* \return Extracted hour [0,23] * \return Extracted hour [0,23]
*/ */
static inline uint8_t FAT_HOUR(uint16_t fatTime) { static inline uint8_t FAT_HOUR(uint16_t fatTime) { return fatTime >> 11; }
return fatTime >> 11;
} /**
/** minute part of FAT directory time field * minute part of FAT directory time field
* \param[in] fatTime Time in packed dir format. * \param[in] fatTime Time in packed dir format.
* *
* \return Extracted minute [0,59] * \return Extracted minute [0,59]
*/ */
static inline uint8_t FAT_MINUTE(uint16_t fatTime) { static inline uint8_t FAT_MINUTE(uint16_t fatTime) { return (fatTime >> 5) & 0x3F; }
return (fatTime >> 5) & 0x3F;
} /**
/** second part of FAT directory time field * second part of FAT directory time field
* Note second/2 is stored in packed time. * Note second/2 is stored in packed time.
* *
* \param[in] fatTime Time in packed dir format. * \param[in] fatTime Time in packed dir format.
* *
* \return Extracted second [0,58] * \return Extracted second [0,58]
*/ */
static inline uint8_t FAT_SECOND(uint16_t fatTime) { static inline uint8_t FAT_SECOND(uint16_t fatTime) { return 2 * (fatTime & 0x1F); }
return 2 * (fatTime & 0x1F);
} // Default date for file timestamps is 1 Jan 2000
/** Default date for file timestamps is 1 Jan 2000 */
uint16_t const FAT_DEFAULT_DATE = ((2000 - 1980) << 9) | (1 << 5) | 1; 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); uint16_t const FAT_DEFAULT_TIME = (1 << 11);
//------------------------------------------------------------------------------
/** /**
* \class SdBaseFile * \class SdBaseFile
* \brief Base class for SdFile with Print and C++ streams. * \brief Base class for SdFile with Print and C++ streams.
*/ */
class SdBaseFile { class SdBaseFile {
public: public:
/** Create an instance. */
SdBaseFile() : writeError(false), type_(FAT_FILE_TYPE_CLOSED) {} SdBaseFile() : writeError(false), type_(FAT_FILE_TYPE_CLOSED) {}
SdBaseFile(const char* path, uint8_t oflag); 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(). * writeError is set to true if an error occurs during a write().
* Set writeError to false before calling print() and/or write() and check * Set writeError to false before calling print() and/or write() and check
* for true after calls to print() and/or write(). * for true after calls to print() and/or write().
*/ */
bool writeError; bool writeError;
//----------------------------------------------------------------------------
// helpers for stream classes // helpers for stream classes
/** get position for streams
/**
* get position for streams
* \param[out] pos struct to receive position * \param[out] pos struct to receive position
*/ */
void getpos(filepos_t* pos); void getpos(filepos_t* pos);
/** set position for streams
/**
* set position for streams
* \param[out] pos struct with value for new position * \param[out] pos struct with value for new position
*/ */
void setpos(filepos_t* pos); void setpos(filepos_t* pos);
//----------------------------------------------------------------------------
bool close(); bool close();
bool contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock); bool contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock);
bool createContiguous(SdBaseFile* dirFile, bool createContiguous(SdBaseFile* dirFile,
const char* path, uint32_t size); const char* path, uint32_t size);
/** \return The current cluster number for a file or directory. */ /**
* \return The current cluster number for a file or directory.
*/
uint32_t curCluster() const { return curCluster_; } uint32_t curCluster() const { return curCluster_; }
/** \return The current position for a file or directory. */
/**
* \return The current position for a file or directory.
*/
uint32_t curPosition() const { return curPosition_; } uint32_t curPosition() const { return curPosition_; }
/** \return Current working directory */
/**
* \return Current working directory
*/
static SdBaseFile* cwd() { return cwd_; } static SdBaseFile* cwd() { return cwd_; }
/** Set the date/time callback function
/**
* Set the date/time callback function
* *
* \param[in] dateTime The user's call back function. The callback * \param[in] dateTime The user's call back function. The callback
* function is of the form: * function is of the form:
@ -252,35 +240,55 @@ class SdBaseFile {
void (*dateTime)(uint16_t* date, uint16_t* time)) { void (*dateTime)(uint16_t* date, uint16_t* time)) {
dateTime_ = dateTime; dateTime_ = dateTime;
} }
/** Cancel the date/time callback function. */
/**
* Cancel the date/time callback function.
*/
static void dateTimeCallbackCancel() { dateTime_ = 0; } static void dateTimeCallbackCancel() { dateTime_ = 0; }
bool dirEntry(dir_t* dir); bool dirEntry(dir_t* dir);
static void dirName(const dir_t& dir, char* name); static void dirName(const dir_t& dir, char* name);
bool exists(const char* name); bool exists(const char* name);
int16_t fgets(char* str, int16_t num, char* delim = 0); int16_t fgets(char* str, int16_t num, char* delim = 0);
/** \return The total number of bytes in a file or directory. */
/**
* \return The total number of bytes in a file or directory.
*/
uint32_t fileSize() const { return fileSize_; } uint32_t fileSize() const { return fileSize_; }
/** \return The first cluster number for a file or directory. */
/**
* \return The first cluster number for a file or directory.
*/
uint32_t firstCluster() const { return firstCluster_; } uint32_t firstCluster() const { return firstCluster_; }
bool getFilename(char* name);
/** \return True if this is a directory else false. */ /**
* \return True if this is a directory else false.
*/
bool isDir() const { return type_ >= FAT_FILE_TYPE_MIN_DIR; } bool isDir() const { return type_ >= FAT_FILE_TYPE_MIN_DIR; }
/** \return True if this is a normal file else false. */
/**
* \return True if this is a normal file else false.
*/
bool isFile() const { return type_ == FAT_FILE_TYPE_NORMAL; } bool isFile() const { return type_ == FAT_FILE_TYPE_NORMAL; }
/** \return True if this is an open file/directory else false. */
/**
* \return True if this is an open file/directory else false.
*/
bool isOpen() const { return type_ != FAT_FILE_TYPE_CLOSED; } bool isOpen() const { return type_ != FAT_FILE_TYPE_CLOSED; }
/** \return True if this is a subdirectory else false. */
/**
* \return True if this is a subdirectory else false.
*/
bool isSubDir() const { return type_ == FAT_FILE_TYPE_SUBDIR; } 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 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); void ls(uint8_t flags = 0, uint8_t indent = 0);
bool mkdir(SdBaseFile* dir, const char* path, bool pFlag = true); 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, uint16_t index, uint8_t oflag);
bool open(SdBaseFile* dirFile, const char* path, uint8_t oflag); bool open(SdBaseFile* dirFile, const char* path, uint8_t oflag);
bool open(const char* path, uint8_t oflag = O_READ); bool open(const char* path, uint8_t oflag = O_READ);
@ -295,53 +303,58 @@ class SdBaseFile {
int8_t readDir(dir_t* dir, char* longFilename); int8_t readDir(dir_t* dir, char* longFilename);
static bool remove(SdBaseFile* dirFile, const char* path); static bool remove(SdBaseFile* dirFile, const char* path);
bool remove(); bool remove();
/** Set the file's current position to zero. */
/**
* Set the file's current position to zero.
*/
void rewind() { seekSet(0); } void rewind() { seekSet(0); }
bool rename(SdBaseFile* dirFile, const char* newPath); bool rename(SdBaseFile* dirFile, const char* newPath);
bool rmdir(); bool rmdir();
// for backward compatibility
bool rmDir() {return rmdir();}
bool rmRfStar(); 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. * \param[in] offset The new position in bytes from the current position.
* \return true for success or false for failure. * \return true for success or false for failure.
*/ */
bool seekCur(int32_t offset) { bool seekCur(const int32_t offset) { return seekSet(curPosition_ + offset); }
return seekSet(curPosition_ + offset);
} /**
/** Set the files position to end-of-file + \a offset. See seekSet(). * Set the files position to end-of-file + \a offset. See seekSet().
* \param[in] offset The new position in bytes from end-of-file. * \param[in] offset The new position in bytes from end-of-file.
* \return true for success or false for failure. * \return true for success or false for failure.
*/ */
bool seekEnd(int32_t offset = 0) {return seekSet(fileSize_ + offset);} bool seekEnd(const int32_t offset = 0) { return seekSet(fileSize_ + offset); }
bool seekSet(uint32_t pos); bool seekSet(const uint32_t pos);
bool sync(); bool sync();
bool timestamp(SdBaseFile* file); bool timestamp(SdBaseFile* file);
bool timestamp(uint8_t flag, uint16_t year, uint8_t month, uint8_t day, bool timestamp(uint8_t flag, uint16_t year, uint8_t month, uint8_t day,
uint8_t hour, uint8_t minute, uint8_t second); 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. * \return The file or directory type.
*/ */
uint8_t type() const { return type_; } uint8_t type() const { return type_; }
bool truncate(uint32_t size); bool truncate(uint32_t size);
/** \return SdVolume that contains this file. */
/**
* \return SdVolume that contains this file.
*/
SdVolume* volume() const { return vol_; } SdVolume* volume() const { return vol_; }
int16_t write(const void* buf, uint16_t nbyte); int16_t write(const void* buf, uint16_t nbyte);
//------------------------------------------------------------------------------
private: private:
// allow SdFat to set cwd_ friend class SdFat; // allow SdFat to set cwd_
friend class SdFat; static SdBaseFile* cwd_; // global pointer to cwd dir
// global pointer to cwd dir
static SdBaseFile* cwd_;
// data time callback function // data time callback function
static void (*dateTime_)(uint16_t* date, uint16_t* time); static void (*dateTime_)(uint16_t* date, uint16_t* time);
// bits defined in flags_ // bits defined in flags_
// should be 0x0F static uint8_t const F_OFLAG = (O_ACCMODE | O_APPEND | O_SYNC), // should be 0x0F
static uint8_t const F_OFLAG = (O_ACCMODE | O_APPEND | O_SYNC); F_FILE_DIR_DIRTY = 0x80; // sync of directory entry required
// sync of directory entry required
static uint8_t const F_FILE_DIR_DIRTY = 0x80;
// private data // private data
uint8_t flags_; // See above for definition of flags_ bits uint8_t flags_; // See above for definition of flags_ bits
@ -355,8 +368,11 @@ class SdBaseFile {
uint32_t firstCluster_; // first cluster of file uint32_t firstCluster_; // first cluster of file
SdVolume* vol_; // volume where file is located 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 // private functions
bool addCluster(); bool addCluster();
bool addDirCluster(); bool addDirCluster();
@ -367,61 +383,48 @@ class SdBaseFile {
bool open(SdBaseFile* dirFile, const uint8_t dname[11], uint8_t oflag); bool open(SdBaseFile* dirFile, const uint8_t dname[11], uint8_t oflag);
bool openCachedEntry(uint8_t cacheIndex, uint8_t oflags); bool openCachedEntry(uint8_t cacheIndex, uint8_t oflags);
dir_t* readDirCache(); dir_t* readDirCache();
//------------------------------------------------------------------------------
// to be deleted // Deprecated functions
static void printDirName(const dir_t& dir, #if ALLOW_DEPRECATED_FUNCTIONS
uint8_t width, bool printSlash);
//------------------------------------------------------------------------------
// Deprecated functions - suppress cpplint warnings with NOLINT comment
#if ALLOW_DEPRECATED_FUNCTIONS && !defined(DOXYGEN)
public: public:
/** \deprecated Use:
/**
* \deprecated Use:
* bool contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock); * bool contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock);
* \param[out] bgnBlock the first block address for the file. * \param[out] bgnBlock the first block address for the file.
* \param[out] endBlock the last block address for the file. * \param[out] endBlock the last block address for the file.
* \return true for success or false for failure. * \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); return contiguousRange(&bgnBlock, &endBlock);
} }
/** \deprecated Use:
* bool createContiguous(SdBaseFile* dirFile, /**
* 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] dirFile The directory where the file will be created.
* \param[in] path A path with a valid DOS 8.3 file name. * \param[in] path A path with a valid DOS 8.3 file name.
* \param[in] size The desired file size. * \param[in] size The desired file size.
* \return true for success or false for failure. * \return true for success or false for failure.
*/ */
bool createContiguous(SdBaseFile& dirFile, // NOLINT bool createContiguous(SdBaseFile& dirFile, const char* path, uint32_t size) {
const char* path, uint32_t size) {
return createContiguous(&dirFile, path, size); return createContiguous(&dirFile, path, size);
} }
/** \deprecated Use:
/**
* \deprecated Use:
* static void dateTimeCallback( * static void dateTimeCallback(
* void (*dateTime)(uint16_t* date, uint16_t* time)); * void (*dateTime)(uint16_t* date, uint16_t* time));
* \param[in] dateTime The user's call back function. * \param[in] dateTime The user's call back function.
*/ */
static void dateTimeCallback( static void dateTimeCallback(
void (*dateTime)(uint16_t &date, uint16_t &time)) { // NOLINT void (*dateTime)(uint16_t &date, uint16_t &time)) {
oldDateTime_ = dateTime; oldDateTime_ = dateTime;
dateTime_ = dateTime ? oldToNew : 0; 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. * \deprecated Use:
*/
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:
* bool open(SdBaseFile* dirFile, const char* path, uint8_t oflag); * bool open(SdBaseFile* dirFile, const char* path, uint8_t oflag);
* \param[in] dirFile An open SdFat instance for the directory containing the * \param[in] dirFile An open SdFat instance for the directory containing the
* file to be opened. * file to be opened.
@ -430,20 +433,23 @@ class SdBaseFile {
* OR of flags O_READ, O_WRITE, O_TRUNC, and O_SYNC. * OR of flags O_READ, O_WRITE, O_TRUNC, and O_SYNC.
* \return true for success or false for failure. * \return true for success or false for failure.
*/ */
bool open(SdBaseFile& dirFile, // NOLINT bool open(SdBaseFile& dirFile, const char* path, uint8_t oflag) {
const char* path, uint8_t oflag) {
return open(&dirFile, path, 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 * \param[in] dirFile An open SdFat instance for the directory containing the
* file to be opened. * file to be opened.
* \param[in] path A path with a valid 8.3 DOS name for a 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. * \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); return open(dirFile, path, O_RDWR);
} }
/** \deprecated Use:
/**
* \deprecated Use:
* bool open(SdBaseFile* dirFile, uint16_t index, uint8_t oflag); * bool open(SdBaseFile* dirFile, uint16_t index, uint8_t oflag);
* \param[in] dirFile An open SdFat instance for the directory. * \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 * \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. * OR of flags O_READ, O_WRITE, O_TRUNC, and O_SYNC.
* \return true for success or false for failure. * \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); 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. * \param[in] vol The FAT volume containing the root directory to be opened.
* \return true for success or false for failure. * \return true for success or false for failure.
*/ */
bool openRoot(SdVolume& vol) {return openRoot(&vol);} // NOLINT bool openRoot(SdVolume& vol) { return openRoot(&vol); }
/** \deprecated Use: int8_t readDir(dir_t* dir);
/**
* \deprecated Use: int8_t readDir(dir_t* dir);
* \param[out] dir The dir_t struct that will receive the data. * \param[out] dir The dir_t struct that will receive the data.
* \return bytes read for success zero for eof or -1 for failure. * \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 int8_t readDir(dir_t& dir, char* longFilename) {
/** \deprecated Use: return readDir(&dir, longFilename);
}
/**
* \deprecated Use:
* static uint8_t remove(SdBaseFile* dirFile, const char* path); * static uint8_t remove(SdBaseFile* dirFile, const char* path);
* \param[in] dirFile The directory that contains the file. * \param[in] dirFile The directory that contains the file.
* \param[in] path The name of the file to be removed. * \param[in] path The name of the file to be removed.
* \return true for success or false for failure. * \return true for success or false for failure.
*/ */
static bool remove(SdBaseFile& dirFile, const char* path) { // NOLINT static bool remove(SdBaseFile& dirFile, const char* path) { return remove(&dirFile, path); }
return remove(&dirFile, path);
}
//------------------------------------------------------------------------------
// rest are private
private: private:
static void (*oldDateTime_)(uint16_t &date, uint16_t &time); // NOLINT static void (*oldDateTime_)(uint16_t &date, uint16_t &time);
static void oldToNew(uint16_t* date, uint16_t* time) { static void oldToNew(uint16_t * const date, uint16_t * const time) {
uint16_t d; uint16_t d, t;
uint16_t t;
oldDateTime_(d, t); oldDateTime_(d, t);
*date = d; *date = d;
*time = t; *time = t;
@ -488,5 +498,4 @@ class SdBaseFile {
#endif // ALLOW_DEPRECATED_FUNCTIONS #endif // ALLOW_DEPRECATED_FUNCTIONS
}; };
#endif // SdBaseFile_h #endif // _SDBASEFILE_H_
#endif

@ -33,8 +33,6 @@
#include "MarlinConfig.h" #include "MarlinConfig.h"
//------------------------------------------------------------------------------
/** /**
* To use multiple SD cards set USE_MULTIPLE_CARDS nonzero. * To use multiple SD cards set USE_MULTIPLE_CARDS nonzero.
* *
@ -44,8 +42,6 @@
*/ */
#define USE_MULTIPLE_CARDS 0 #define USE_MULTIPLE_CARDS 0
//------------------------------------------------------------------------------
/** /**
* Call flush for endl if ENDL_CALLS_FLUSH is nonzero * Call flush for endl if ENDL_CALLS_FLUSH is nonzero
* *
@ -65,39 +61,29 @@
*/ */
#define ENDL_CALLS_FLUSH 0 #define ENDL_CALLS_FLUSH 0
//------------------------------------------------------------------------------
/** /**
* Allow use of deprecated functions if ALLOW_DEPRECATED_FUNCTIONS is nonzero * Allow use of deprecated functions if ALLOW_DEPRECATED_FUNCTIONS is nonzero
*/ */
#define ALLOW_DEPRECATED_FUNCTIONS 1 #define ALLOW_DEPRECATED_FUNCTIONS 1
//------------------------------------------------------------------------------
/** /**
* Allow FAT12 volumes if FAT12_SUPPORT is nonzero. * Allow FAT12 volumes if FAT12_SUPPORT is nonzero.
* FAT12 has not been well tested. * FAT12 has not been well tested.
*/ */
#define FAT12_SUPPORT 0 #define FAT12_SUPPORT 0
//------------------------------------------------------------------------------
/** /**
* SPI init rate for SD initialization commands. Must be 5 (F_CPU/64) * SPI init rate for SD initialization commands. Must be 5 (F_CPU/64)
* or 6 (F_CPU/128). * or 6 (F_CPU/128).
*/ */
#define SPI_SD_INIT_RATE 5 #define SPI_SD_INIT_RATE 5
//------------------------------------------------------------------------------
/** /**
* Set the SS pin high for hardware SPI. If SS is chip select for another SPI * 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. * device this will disable that device during the SD init phase.
*/ */
#define SET_SPI_SS_HIGH 1 #define SET_SPI_SS_HIGH 1
//------------------------------------------------------------------------------
/** /**
* Define MEGA_SOFT_SPI nonzero to use software SPI on Mega Arduinos. * Define MEGA_SOFT_SPI nonzero to use software SPI on Mega Arduinos.
* Pins used are SS 10, MOSI 11, MISO 12, and SCK 13. * Pins used are SS 10, MOSI 11, MISO 12, and SCK 13.
@ -108,8 +94,6 @@
*/ */
#define MEGA_SOFT_SPI 0 #define MEGA_SOFT_SPI 0
//------------------------------------------------------------------------------
// Set USE_SOFTWARE_SPI nonzero to ALWAYS use Software SPI. // Set USE_SOFTWARE_SPI nonzero to ALWAYS use Software SPI.
#define USE_SOFTWARE_SPI 0 #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_MISO_PIN 12 // Software SPI Master In Slave Out pin
#define SOFT_SPI_SCK_PIN 13 // Software SPI Clock pin #define SOFT_SPI_SCK_PIN 13 // Software SPI Clock pin
//------------------------------------------------------------------------------
/** /**
* The __cxa_pure_virtual function is an error handler that is invoked when * The __cxa_pure_virtual function is an error handler that is invoked when
* a pure virtual function is called. * a pure virtual function is called.

@ -20,35 +20,31 @@
* *
*/ */
/**
* \file
* \brief FAT file structures
*/
/** /**
* Arduino SdFat Library * Arduino SdFat Library
* Copyright (C) 2009 by William Greiman * Copyright (C) 2009 by William Greiman
* *
* This file is part of the Arduino Sd2Card Library * This file is part of the Arduino Sd2Card Library
*/ */
#include "Marlin.h" #ifndef SDFATSTRUCTS_H
#if ENABLED(SDSUPPORT) #define SDFATSTRUCTS_H
#ifndef SdFatStructs_h
#define SdFatStructs_h
#define PACKED __attribute__((__packed__)) #define PACKED __attribute__((__packed__))
/**
* \file
* \brief FAT file structures
*/
/** /**
* mostly from Microsoft document fatgen103.doc * mostly from Microsoft document fatgen103.doc
* http://www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx * 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 510 of boot block or MBR
uint8_t const BOOTSIG0 = 0x55; BOOTSIG1 = 0xAA, // Value for byte 511 of boot block or MBR
/** Value for byte 511 of boot block or MBR */ EXTENDED_BOOT_SIG = 0x29; // Value for bootSignature field int FAT/FAT32 boot sector
uint8_t const BOOTSIG1 = 0xAA;
/** Value for bootSignature field int FAT/FAT32 boot sector */
uint8_t const EXTENDED_BOOT_SIG = 0x29;
//------------------------------------------------------------------------------
/** /**
* \struct partitionTable * \struct partitionTable
* \brief MBR partition table entry * \brief MBR partition table entry
@ -102,14 +98,13 @@ struct partitionTable {
* are 0-1023. Only used in old PC BIOS. * are 0-1023. Only used in old PC BIOS.
*/ */
uint8_t endCylinderLow; uint8_t endCylinderLow;
/** Logical block address of the first block in the partition. */
uint32_t firstSector; uint32_t firstSector; // Logical block address of the first block in the partition.
/** Length of the partition, in blocks. */ uint32_t totalSectors; // Length of the partition, in blocks.
uint32_t totalSectors;
} PACKED; } PACKED;
/** Type name for partitionTable */
typedef struct partitionTable part_t; typedef struct partitionTable part_t; // Type name for partitionTable
//------------------------------------------------------------------------------
/** /**
* \struct masterBootRecord * \struct masterBootRecord
* *
@ -118,22 +113,16 @@ typedef struct partitionTable part_t;
* The first block of a storage device that is formatted with a MBR. * The first block of a storage device that is formatted with a MBR.
*/ */
struct masterBootRecord { struct masterBootRecord {
/** Code Area for master boot program. */ uint8_t codeArea[440]; // Code Area for master boot program.
uint8_t codeArea[440]; uint32_t diskSignature; // Optional Windows NT disk signature. May contain boot code.
/** Optional Windows NT disk signature. May contain boot code. */ uint16_t usuallyZero; // Usually zero but may be more boot code.
uint32_t diskSignature; part_t part[4]; // Partition tables.
/** Usually zero but may be more boot code. */ uint8_t mbrSig0; // First MBR signature byte. Must be 0x55
uint16_t usuallyZero; uint8_t mbrSig1; // Second MBR signature byte. Must be 0xAA
/** 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;
} PACKED; } PACKED;
/** Type name for masterBootRecord */ /** Type name for masterBootRecord */
typedef struct masterBootRecord mbr_t; typedef struct masterBootRecord mbr_t;
//------------------------------------------------------------------------------
/** /**
* \struct fat_boot * \struct fat_boot
* *
@ -206,10 +195,10 @@ struct fat_boot {
* contains the FAT size count. * contains the FAT size count.
*/ */
uint16_t sectorsPerFat16; uint16_t sectorsPerFat16;
/** Sectors per track for interrupt 0x13. Not used otherwise. */
uint16_t sectorsPerTrack; uint16_t sectorsPerTrack; // Sectors per track for interrupt 0x13. Not used otherwise.
/** Number of heads for interrupt 0x13. Not used otherwise. */ uint16_t headCount; // Number of heads for interrupt 0x13. Not used otherwise.
uint16_t headCount;
/** /**
* Count of hidden sectors preceding the partition that contains this * Count of hidden sectors preceding the partition that contains this
* FAT volume. This field is generally only relevant for media * FAT volume. This field is generally only relevant for media
@ -232,10 +221,10 @@ struct fat_boot {
* relevant if the device is a boot device. * relevant if the device is a boot device.
*/ */
uint8_t driveNumber; uint8_t driveNumber;
/** used by Windows NT - should be zero for FAT */
uint8_t reserved1; uint8_t reserved1; // used by Windows NT - should be zero for FAT
/** 0x29 if next three fields are valid */ uint8_t bootSignature; // 0x29 if next three fields are valid
uint8_t bootSignature;
/** /**
* A random serial number created when formatting a disk, * A random serial number created when formatting a disk,
* which helps to distinguish between disks. * which helps to distinguish between disks.
@ -252,21 +241,18 @@ struct fat_boot {
* depending on the disk format. * depending on the disk format.
*/ */
char fileSystemType[8]; char fileSystemType[8];
/** X86 boot code */
uint8_t bootCode[448]; uint8_t bootCode[448]; // X86 boot code
/** must be 0x55 */ uint8_t bootSectorSig0; // must be 0x55
uint8_t bootSectorSig0; uint8_t bootSectorSig1; // must be 0xAA
/** must be 0xAA */
uint8_t bootSectorSig1;
} PACKED; } 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 * \struct fat32_boot
* *
* \brief Boot sector for a FAT32 volume. * \brief Boot sector for a FAT32 volume.
*
*/ */
struct fat32_boot { struct fat32_boot {
/** /**
@ -322,10 +308,10 @@ struct fat32_boot {
* contains the FAT size count. * contains the FAT size count.
*/ */
uint16_t sectorsPerFat16; uint16_t sectorsPerFat16;
/** Sectors per track for interrupt 0x13. Not used otherwise. */
uint16_t sectorsPerTrack; uint16_t sectorsPerTrack; // Sectors per track for interrupt 0x13. Not used otherwise.
/** Number of heads for interrupt 0x13. Not used otherwise. */ uint16_t headCount; // Number of heads for interrupt 0x13. Not used otherwise.
uint16_t headCount;
/** /**
* Count of hidden sectors preceding the partition that contains this * Count of hidden sectors preceding the partition that contains this
* FAT volume. This field is generally only relevant for media * FAT volume. This field is generally only relevant for media
@ -387,10 +373,10 @@ struct fat32_boot {
* relevant if the device is a boot device. * relevant if the device is a boot device.
*/ */
uint8_t driveNumber; uint8_t driveNumber;
/** used by Windows NT - should be zero for FAT */
uint8_t reserved1; uint8_t reserved1; // Used by Windows NT - should be zero for FAT
/** 0x29 if next three fields are valid */ uint8_t bootSignature; // 0x29 if next three fields are valid
uint8_t bootSignature;
/** /**
* A random serial number created when formatting a disk, * A random serial number created when formatting a disk,
* which helps to distinguish between disks. * which helps to distinguish between disks.
@ -406,20 +392,18 @@ struct fat32_boot {
* A text field with a value of FAT32. * A text field with a value of FAT32.
*/ */
char fileSystemType[8]; char fileSystemType[8];
/** X86 boot code */
uint8_t bootCode[420]; uint8_t bootCode[420]; // X86 boot code
/** must be 0x55 */ uint8_t bootSectorSig0; // must be 0x55
uint8_t bootSectorSig0; uint8_t bootSectorSig1; // must be 0xAA
/** must be 0xAA */
uint8_t bootSectorSig1;
} PACKED; } PACKED;
/** Type name for FAT32 Boot Sector */
typedef struct fat32_boot fat32_boot_t; typedef struct fat32_boot fat32_boot_t; // Type name for FAT32 Boot Sector
//------------------------------------------------------------------------------
/** Lead signature for a FSINFO sector */ uint32_t const FSINFO_LEAD_SIG = 0x41615252, // 'AaRR' Lead signature for a FSINFO sector
uint32_t const FSINFO_LEAD_SIG = 0x41615252; FSINFO_STRUCT_SIG = 0x61417272; // 'aArr' Struct signature for a FSINFO sector
/** Struct signature for a FSINFO sector */
uint32_t const FSINFO_STRUCT_SIG = 0x61417272;
/** /**
* \struct fat32_fsinfo * \struct fat32_fsinfo
* *
@ -427,12 +411,9 @@ uint32_t const FSINFO_STRUCT_SIG = 0x61417272;
* *
*/ */
struct fat32_fsinfo { struct fat32_fsinfo {
/** must be 0x52, 0x52, 0x61, 0x41 */ uint32_t leadSignature; // must be 0x52, 0x52, 0x61, 0x41 'RRaA'
uint32_t leadSignature; uint8_t reserved1[480]; // must be zero
/** must be zero */ uint32_t structSignature; // must be 0x72, 0x72, 0x41, 0x61 'rrAa'
uint8_t reserved1[480];
/** must be 0x72, 0x72, 0x41, 0x61 */
uint32_t structSignature;
/** /**
* Contains the last known free cluster count on the volume. * Contains the last known free cluster count on the volume.
* If the value is 0xFFFFFFFF, then the free count is unknown * If the value is 0xFFFFFFFF, then the free count is unknown
@ -448,30 +429,22 @@ struct fat32_fsinfo {
* should start looking at cluster 2. * should start looking at cluster 2.
*/ */
uint32_t nextFree; uint32_t nextFree;
/** must be zero */
uint8_t reserved2[12]; uint8_t reserved2[12]; // must be zero
/** must be 0x00, 0x00, 0x55, 0xAA */ uint8_t tailSignature[4]; // must be 0x00, 0x00, 0x55, 0xAA
uint8_t tailSignature[4];
} PACKED; } 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 // End Of Chain values for FAT entries
/** FAT12 end of chain value used by Microsoft. */ uint16_t const FAT12EOC = 0xFFF, // FAT12 end of chain value used by Microsoft.
uint16_t const FAT12EOC = 0xFFF; FAT12EOC_MIN = 0xFF8, // Minimum value for FAT12 EOC. Use to test for EOC.
/** Minimum value for FAT12 EOC. Use to test for EOC. */ FAT16EOC = 0xFFFF, // FAT16 end of chain value used by Microsoft.
uint16_t const FAT12EOC_MIN = 0xFF8; FAT16EOC_MIN = 0xFFF8; // Minimum value for FAT16 EOC. Use to test for EOC.
/** FAT16 end of chain value used by Microsoft. */ uint32_t const FAT32EOC = 0x0FFFFFFF, // FAT32 end of chain value used by Microsoft.
uint16_t const FAT16EOC = 0xFFFF; FAT32EOC_MIN = 0x0FFFFFF8, // Minimum value for FAT32 EOC. Use to test for EOC.
/** Minimum value for FAT16 EOC. Use to test for EOC. */ FAT32MASK = 0x0FFFFFFF; // Mask a for FAT32 entry. Entries are 28 bits.
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;
//------------------------------------------------------------------------------
/** /**
* \struct directoryEntry * \struct directoryEntry
* \brief FAT short directory entry * \brief FAT short directory entry
@ -503,13 +476,15 @@ uint32_t const FAT32MASK = 0x0FFFFFFF;
* The valid time range is from Midnight 00:00:00 to 23:59:58. * The valid time range is from Midnight 00:00:00 to 23:59:58.
*/ */
struct directoryEntry { struct directoryEntry {
/** Short 8.3 name. /**
* Short 8.3 name.
* *
* The first eight bytes contain the file name with blank fill. * The first eight bytes contain the file name with blank fill.
* The last three bytes contain the file extension with blank fill. * The last three bytes contain the file extension with blank fill.
*/ */
uint8_t name[11]; uint8_t name[11];
/** Entry attributes. /**
* Entry attributes.
* *
* The upper two bits of the attribute byte are reserved and should * 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 * always be set to 0 when a file is created and never modified or
@ -527,10 +502,10 @@ struct directoryEntry {
* value range is 0-199 inclusive. (WHG note - seems to be hundredths) * value range is 0-199 inclusive. (WHG note - seems to be hundredths)
*/ */
uint8_t creationTimeTenths; uint8_t creationTimeTenths;
/** Time file was created. */
uint16_t creationTime; uint16_t creationTime; // Time file was created.
/** Date file was created. */ uint16_t creationDate; // Date file was created.
uint16_t creationDate;
/** /**
* Last access date. Note that there is no last access time, only * 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 date. This is the date of last read or write. In the case of
@ -542,15 +517,13 @@ struct directoryEntry {
* FAT12 or FAT16 volume). * FAT12 or FAT16 volume).
*/ */
uint16_t firstClusterHigh; uint16_t firstClusterHigh;
/** Time of last write. File creation is considered a write. */
uint16_t lastWriteTime; uint16_t lastWriteTime; // Time of last write. File creation is considered a write.
/** Date of last write. File creation is considered a write. */ uint16_t lastWriteDate; // Date of last write. File creation is considered a write.
uint16_t lastWriteDate; uint16_t firstClusterLow; // Low word of this entry's first cluster number.
/** Low word of this entry's first cluster number. */ uint32_t fileSize; // 32-bit unsigned holding this file's size in bytes.
uint16_t firstClusterLow;
/** 32-bit unsigned holding this file's size in bytes. */
uint32_t fileSize;
} PACKED; } PACKED;
/** /**
* \struct directoryVFATEntry * \struct directoryVFATEntry
* \brief VFAT long filename directory entry * \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) * bit 0-4: the position of this long filename block (first block is 1)
*/ */
uint8_t sequenceNumber; uint8_t sequenceNumber;
/** First set of UTF-16 characters */
uint16_t name1[5];//UTF-16 uint16_t name1[5]; // First set of UTF-16 characters
/** attributes (at the same location as in directoryEntry), always 0x0F */ uint8_t attributes; // attributes (at the same location as in directoryEntry), always 0x0F
uint8_t attributes; uint8_t reservedNT; // Reserved for use by Windows NT. Always 0.
/** 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.
uint8_t reservedNT; uint16_t name2[6]; // Second set of UTF-16 characters
/** 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 firstClusterLow; // firstClusterLow is always zero for longFilenames
uint8_t checksum; uint16_t name3[2]; // Third set of UTF-16 characters
/** 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
} PACKED; } PACKED;
//------------------------------------------------------------------------------
// Definitions for directory entries // Definitions for directory entries
// //
/** Type name for directoryEntry */ typedef struct directoryEntry dir_t; // Type name for directoryEntry
typedef struct directoryEntry dir_t; typedef struct directoryVFATEntry vfat_t; // Type name for directoryVFATEntry
/** Type name for directoryVFATEntry */
typedef struct directoryVFATEntry vfat_t; uint8_t const DIR_NAME_0xE5 = 0x05, // escape for name[0] = 0xE5
/** escape for name[0] = 0xE5 */ DIR_NAME_DELETED = 0xE5, // name[0] value for entry that is free after being "deleted"
uint8_t const DIR_NAME_0xE5 = 0x05; DIR_NAME_FREE = 0x00, // name[0] value for entry that is free and no allocated entries follow
/** name[0] value for entry that is free after being "deleted" */ DIR_ATT_READ_ONLY = 0x01, // file is read-only
uint8_t const DIR_NAME_DELETED = 0xE5; DIR_ATT_HIDDEN = 0x02, // File should hidden in directory listings
/** name[0] value for entry that is free and no allocated entries follow */ DIR_ATT_SYSTEM = 0x04, // Entry is for a system file
uint8_t const DIR_NAME_FREE = 0x00; DIR_ATT_VOLUME_ID = 0x08, // Directory entry contains the volume label
/** file is read-only */ DIR_ATT_DIRECTORY = 0x10, // Entry is for a directory
uint8_t const DIR_ATT_READ_ONLY = 0x01; DIR_ATT_ARCHIVE = 0x20, // Old DOS archive bit for backup support
/** File should hidden in directory listings */ DIR_ATT_LONG_NAME = 0x0F, // Test value for long name entry. Test is (d->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME.
uint8_t const DIR_ATT_HIDDEN = 0x02; DIR_ATT_LONG_NAME_MASK = 0x3F, // Test mask for long name entry
/** Entry is for a system file */ DIR_ATT_DEFINED_BITS = 0x3F; // defined attribute bits
uint8_t const DIR_ATT_SYSTEM = 0x04;
/** Directory entry contains the volume label */ /**
uint8_t const DIR_ATT_VOLUME_ID = 0x08; * Directory entry is part of a long name
/** 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
* \param[in] dir Pointer to a directory entry. * \param[in] dir Pointer to a directory entry.
* *
* \return true if the entry is for part of a long name else false. * \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) { static inline uint8_t DIR_IS_LONG_NAME(const dir_t* dir) {
return (dir->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME; return (dir->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME;
} }
/** Mask for file/subdirectory tests */ /** Mask for file/subdirectory tests */
uint8_t const DIR_ATT_FILE_TYPE_MASK = (DIR_ATT_VOLUME_ID | DIR_ATT_DIRECTORY); 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. * \param[in] dir Pointer to a directory entry.
* *
* \return true if the entry is for a normal file else false. * \return true if the entry is 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) { static inline uint8_t DIR_IS_FILE(const dir_t* dir) {
return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == 0; 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. * \param[in] dir Pointer to a directory entry.
* *
* \return true if the entry is for a subdirectory else false. * \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) { static inline uint8_t DIR_IS_SUBDIR(const dir_t* dir) {
return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == DIR_ATT_DIRECTORY; 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. * \param[in] dir Pointer to a directory entry.
* *
* \return true if the entry is for a normal file or subdirectory else false. * \return true if the entry is 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) { static inline uint8_t DIR_IS_FILE_OR_SUBDIR(const dir_t* dir) {
return (dir->attributes & DIR_ATT_VOLUME_ID) == 0; return (dir->attributes & DIR_ATT_VOLUME_ID) == 0;
} }
#endif // SdFatStructs_h
#endif #endif // SDFATSTRUCTS_H

@ -26,13 +26,15 @@
* *
* This file is part of the Arduino Sd2Card Library * This file is part of the Arduino Sd2Card Library
*/ */
#include "Marlin.h" #include "MarlinConfig.h"
#if ENABLED(SDSUPPORT) #if ENABLED(SDSUPPORT)
#include "SdFatUtil.h" #include "SdFatUtil.h"
#include "serial.h"
//------------------------------------------------------------------------------ /**
/** Amount of free RAM * Amount of free RAM
* \return The number of free bytes. * \return The number of free bytes.
*/ */
#ifdef __arm__ #ifdef __arm__
@ -44,7 +46,8 @@ int SdFatUtil::FreeRam() {
#else // __arm__ #else // __arm__
extern char* __brkval; extern char* __brkval;
extern char __bss_end; extern char __bss_end;
/** Amount of free RAM /**
* Amount of free RAM
* \return The number of free bytes. * \return The number of free bytes.
*/ */
int SdFatUtil::FreeRam() { int SdFatUtil::FreeRam() {
@ -53,8 +56,8 @@ int SdFatUtil::FreeRam() {
} }
#endif // __arm #endif // __arm
//------------------------------------------------------------------------------ /**
/** %Print a string in flash memory. * %Print a string in flash memory.
* *
* \param[in] pr Print object for output. * \param[in] pr Print object for output.
* \param[in] str Pointer to string stored in flash memory. * \param[in] str Pointer to string stored in flash memory.
@ -62,30 +65,27 @@ int SdFatUtil::FreeRam() {
void SdFatUtil::print_P(PGM_P str) { void SdFatUtil::print_P(PGM_P str) {
for (uint8_t c; (c = pgm_read_byte(str)); str++) MYSERIAL.write(c); 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] pr Print object for output.
* \param[in] str Pointer to string stored in flash memory. * \param[in] str Pointer to string stored in flash memory.
*/ */
void SdFatUtil::println_P(PGM_P str) { void SdFatUtil::println_P(PGM_P str) { print_P(str); MYSERIAL.println(); }
print_P(str);
MYSERIAL.println(); /**
} * %Print a string in flash memory to Serial.
//------------------------------------------------------------------------------
/** %Print a string in flash memory to Serial.
* *
* \param[in] str Pointer to string stored in flash memory. * \param[in] str Pointer to string stored in flash memory.
*/ */
void SdFatUtil::SerialPrint_P(PGM_P str) { void SdFatUtil::SerialPrint_P(PGM_P str) { print_P(str); }
print_P(str);
} /**
//------------------------------------------------------------------------------ * %Print a string in flash memory to Serial followed by a CR/LF.
/** %Print a string in flash memory to Serial followed by a CR/LF.
* *
* \param[in] str Pointer to string stored in flash memory. * \param[in] str Pointer to string stored in flash memory.
*/ */
void SdFatUtil::SerialPrintln_P(PGM_P str) { void SdFatUtil::SerialPrintln_P(PGM_P str) { println_P(str); }
println_P(str);
} #endif // SDSUPPORT
#endif

@ -26,11 +26,8 @@
* *
* This file is part of the Arduino Sd2Card Library * This file is part of the Arduino Sd2Card Library
*/ */
#ifndef SdFatUtil_h #ifndef _SDFATUTIL_H_
#define SdFatUtil_h #define _SDFATUTIL_H_
#include "Marlin.h"
#if ENABLED(SDSUPPORT)
/** /**
* \file * \file
@ -51,6 +48,4 @@ namespace SdFatUtil {
using namespace SdFatUtil; // NOLINT using namespace SdFatUtil; // NOLINT
#endif // SDSUPPORT #endif // _SDFATUTIL_H_
#endif // SdFatUtil_h

@ -26,21 +26,24 @@
* *
* This file is part of the Arduino Sd2Card Library * This file is part of the Arduino Sd2Card Library
*/ */
#include "Marlin.h" #include "MarlinConfig.h"
#if ENABLED(SDSUPPORT) #if ENABLED(SDSUPPORT)
#include "SdFile.h" #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] 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 * \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). * OR of open flags. see SdBaseFile::open(SdBaseFile*, const char*, uint8_t).
*/ */
SdFile::SdFile(const char* path, uint8_t oflag) : SdBaseFile(path, oflag) { SdFile::SdFile(const char* path, uint8_t oflag) : SdBaseFile(path, oflag) { }
}
//------------------------------------------------------------------------------ /**
/** 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 * \note Data is moved to the cache but may not be written to the
* storage device until sync() is called. * 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. * 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) { int16_t SdFile::write(const void* buf, uint16_t nbyte) { return SdBaseFile::write(buf, nbyte); }
return SdBaseFile::write(buf, nbyte);
} /**
//------------------------------------------------------------------------------ * Write a byte to a file. Required by the Arduino Print class.
/** Write a byte to a file. Required by the Arduino Print class.
* \param[in] b the byte to be written. * \param[in] b the byte to be written.
* Use writeError to check for errors. * Use writeError to check for errors.
*/ */
#if ARDUINO >= 100 #if ARDUINO >= 100
size_t SdFile::write(uint8_t b) { size_t SdFile::write(uint8_t b) { return SdBaseFile::write(&b, 1); }
return SdBaseFile::write(&b, 1);
}
#else #else
void SdFile::write(uint8_t b) { void SdFile::write(uint8_t b) { SdBaseFile::write(&b, 1); }
SdBaseFile::write(&b, 1);
}
#endif #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. * \param[in] str Pointer to the string.
* Use writeError to check for errors. * Use writeError to check for errors.
*/ */
void SdFile::write(const char* str) { void SdFile::write(const char* str) { SdBaseFile::write(str, strlen(str)); }
SdBaseFile::write(str, strlen(str));
} /**
//------------------------------------------------------------------------------ * Write a PROGMEM string to a file.
/** Write a PROGMEM string to a file.
* \param[in] str Pointer to the PROGMEM string. * \param[in] str Pointer to the PROGMEM string.
* Use writeError to check for errors. * Use writeError to check for errors.
*/ */
void SdFile::write_P(PGM_P str) { void SdFile::write_P(PGM_P str) {
for (uint8_t c; (c = pgm_read_byte(str)); str++) write(c); for (uint8_t c; (c = pgm_read_byte(str)); str++) write(c);
} }
//------------------------------------------------------------------------------
/** Write a PROGMEM string followed by CR/LF to a file. /**
* Write a PROGMEM string followed by CR/LF to a file.
* \param[in] str Pointer to the PROGMEM string. * \param[in] str Pointer to the PROGMEM string.
* Use writeError to check for errors. * Use writeError to check for errors.
*/ */
@ -98,5 +97,4 @@ void SdFile::writeln_P(PGM_P str) {
write_P(PSTR("\r\n")); write_P(PSTR("\r\n"));
} }
#endif // SDSUPPORT
#endif

@ -20,24 +20,23 @@
* *
*/ */
/**
* \file
* \brief SdFile class
*/
/** /**
* Arduino SdFat Library * Arduino SdFat Library
* Copyright (C) 2009 by William Greiman * Copyright (C) 2009 by William Greiman
* *
* This file is part of the Arduino Sd2Card Library * This file is part of the Arduino Sd2Card Library
*/ */
/** #ifndef _SDFILE_H_
* \file #define _SDFILE_H_
* \brief SdFile class
*/
#include "Marlin.h"
#if ENABLED(SDSUPPORT)
#include "SdBaseFile.h" #include "SdBaseFile.h"
#include <Print.h> #include <Print.h>
#ifndef SdFile_h
#define SdFile_h
//------------------------------------------------------------------------------
/** /**
* \class SdFile * \class SdFile
* \brief SdBaseFile with Print. * \brief SdBaseFile with Print.
@ -57,7 +56,5 @@ class SdFile : public SdBaseFile, public Print {
void write_P(PGM_P str); void write_P(PGM_P str);
void writeln_P(PGM_P str); void writeln_P(PGM_P str);
}; };
#endif // SdFile_h
#endif #endif // _SDFILE_H_

@ -26,12 +26,11 @@
* *
* This file is part of the Arduino Sd2Card Library * This file is part of the Arduino Sd2Card Library
*/ */
#include "Marlin.h" #ifndef _SDINFO_H_
#if ENABLED(SDSUPPORT) #define _SDINFO_H_
#ifndef SdInfo_h
#define SdInfo_h
#include <stdint.h> #include <stdint.h>
// Based on the document: // Based on the document:
// //
// SD Specifications // SD Specifications
@ -42,46 +41,26 @@
// May 18, 2010 // May 18, 2010
// //
// http://www.sdcard.org/developers/tech/sdcard/pls/simplified_specs // http://www.sdcard.org/developers/tech/sdcard/pls/simplified_specs
//------------------------------------------------------------------------------
// SD card commands // SD card commands
/** GO_IDLE_STATE - init card in spi mode if CS low */ uint8_t const CMD0 = 0x00, // GO_IDLE_STATE - init card in spi mode if CS low
uint8_t const CMD0 = 0x00; CMD8 = 0x08, // SEND_IF_COND - verify SD Memory Card interface operating condition
/** SEND_IF_COND - verify SD Memory Card interface operating condition.*/ CMD9 = 0x09, // SEND_CSD - read the Card Specific Data (CSD register)
uint8_t const CMD8 = 0x08; CMD10 = 0x0A, // SEND_CID - read the card identification information (CID register)
/** SEND_CSD - read the Card Specific Data (CSD register) */ CMD12 = 0x0C, // STOP_TRANSMISSION - end multiple block read sequence
uint8_t const CMD9 = 0x09; CMD13 = 0x0D, // SEND_STATUS - read the card status register
/** SEND_CID - read the card identification information (CID register) */ CMD17 = 0x11, // READ_SINGLE_BLOCK - read a single data block from the card
uint8_t const CMD10 = 0x0A; CMD18 = 0x12, // READ_MULTIPLE_BLOCK - read a multiple data blocks from the card
/** STOP_TRANSMISSION - end multiple block read sequence */ CMD24 = 0x18, // WRITE_BLOCK - write a single data block to the card
uint8_t const CMD12 = 0x0C; CMD25 = 0x19, // WRITE_MULTIPLE_BLOCK - write blocks of data until a STOP_TRANSMISSION
/** SEND_STATUS - read the card status register */ CMD32 = 0x20, // ERASE_WR_BLK_START - sets the address of the first block to be erased
uint8_t const CMD13 = 0x0D; CMD33 = 0x21, // ERASE_WR_BLK_END - sets the address of the last block of the continuous range to be erased*/
/** READ_SINGLE_BLOCK - read a single data block from the card */ CMD38 = 0x26, // ERASE - erase all previously selected blocks */
uint8_t const CMD17 = 0x11; CMD55 = 0x37, // APP_CMD - escape for application specific command */
/** READ_MULTIPLE_BLOCK - read a multiple data blocks from the card */ CMD58 = 0x3A, // READ_OCR - read the OCR register of a card */
uint8_t const CMD18 = 0x12; ACMD23 = 0x17, // SET_WR_BLK_ERASE_COUNT - Set the number of write blocks to be pre-erased before writing */
/** WRITE_BLOCK - write a single data block to the card */ ACMD41 = 0x29; // SD_SEND_OP_COMD - Sends host capacity support information and activates the card's initialization process */
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;
//------------------------------------------------------------------------------
/** status for card in the ready state */ /** status for card in the ready state */
uint8_t const R1_READY_STATE = 0x00; uint8_t const R1_READY_STATE = 0x00;
/** status for card in the idle state */ /** 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; uint8_t const DATA_RES_MASK = 0x1F;
/** write data accepted token */ /** write data accepted token */
uint8_t const DATA_RES_ACCEPTED = 0x05; uint8_t const DATA_RES_ACCEPTED = 0x05;
//------------------------------------------------------------------------------
/** Card IDentification (CID) register */ /** Card IDentification (CID) register */
typedef struct CID { typedef struct CID {
// byte 0 // byte 0
@ -134,7 +113,7 @@ typedef struct CID {
/** CRC7 checksum */ /** CRC7 checksum */
unsigned char crc : 7; unsigned char crc : 7;
} cid_t; } cid_t;
//------------------------------------------------------------------------------
/** CSD for version 1.00 cards */ /** CSD for version 1.00 cards */
typedef struct CSDV1 { typedef struct CSDV1 {
// byte 0 // byte 0
@ -196,7 +175,7 @@ typedef struct CSDV1 {
unsigned char always1 : 1; unsigned char always1 : 1;
unsigned char crc : 7; unsigned char crc : 7;
} csd1_t; } csd1_t;
//------------------------------------------------------------------------------
/** CSD for version 2.00 cards */ /** CSD for version 2.00 cards */
typedef struct CSDV2 { typedef struct CSDV2 {
// byte 0 // byte 0
@ -278,12 +257,11 @@ typedef struct CSDV2 {
/** checksum */ /** checksum */
unsigned char crc : 7; unsigned char crc : 7;
} csd2_t; } csd2_t;
//------------------------------------------------------------------------------
/** union of old and new style CSD register */ /** union of old and new style CSD register */
union csd_t { union csd_t {
csd1_t v1; csd1_t v1;
csd2_t v2; csd2_t v2;
}; };
#endif // SdInfo_h
#endif #endif // _SDINFO_H_

@ -26,11 +26,12 @@
* *
* This file is part of the Arduino Sd2Card Library * This file is part of the Arduino Sd2Card Library
*/ */
#include "Marlin.h" #include "MarlinConfig.h"
#if ENABLED(SDSUPPORT) #if ENABLED(SDSUPPORT)
#include "SdVolume.h" #include "SdVolume.h"
//------------------------------------------------------------------------------
#if !USE_MULTIPLE_CARDS #if !USE_MULTIPLE_CARDS
// raw block cache // raw block cache
uint32_t SdVolume::cacheBlockNumber_; // current block number uint32_t SdVolume::cacheBlockNumber_; // current block number
@ -39,7 +40,7 @@
bool SdVolume::cacheDirty_; // cacheFlush() will write block if true bool SdVolume::cacheDirty_; // cacheFlush() will write block if true
uint32_t SdVolume::cacheMirrorBlock_; // mirror block for second FAT uint32_t SdVolume::cacheMirrorBlock_; // mirror block for second FAT
#endif // USE_MULTIPLE_CARDS #endif // USE_MULTIPLE_CARDS
//------------------------------------------------------------------------------
// find a contiguous group of clusters // find a contiguous group of clusters
bool SdVolume::allocContiguous(uint32_t count, uint32_t* curCluster) { bool SdVolume::allocContiguous(uint32_t count, uint32_t* curCluster) {
// start of group // start of group
@ -73,14 +74,14 @@ bool SdVolume::allocContiguous(uint32_t count, uint32_t* curCluster) {
// search the FAT for free clusters // search the FAT for free clusters
for (uint32_t n = 0;; n++, endCluster++) { for (uint32_t n = 0;; n++, endCluster++) {
// can't find space checked all clusters // can't find space checked all clusters
if (n >= clusterCount_) goto FAIL; if (n >= clusterCount_) return false;
// past end - start from beginning of FAT // past end - start from beginning of FAT
if (endCluster > fatEnd) { if (endCluster > fatEnd) {
bgnCluster = endCluster = 2; bgnCluster = endCluster = 2;
} }
uint32_t f; uint32_t f;
if (!fatGet(endCluster, &f)) goto FAIL; if (!fatGet(endCluster, &f)) return false;
if (f != 0) { if (f != 0) {
// cluster in use try next cluster as bgnCluster // 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 // mark end of chain
if (!fatPutEOC(endCluster)) goto FAIL; if (!fatPutEOC(endCluster)) return false;
// link clusters // link clusters
while (endCluster > bgnCluster) { while (endCluster > bgnCluster) {
if (!fatPut(endCluster - 1, endCluster)) goto FAIL; if (!fatPut(endCluster - 1, endCluster)) return false;
endCluster--; endCluster--;
} }
if (*curCluster != 0) { if (*curCluster != 0) {
// connect chains // connect chains
if (!fatPut(*curCluster, bgnCluster)) goto FAIL; if (!fatPut(*curCluster, bgnCluster)) return false;
} }
// return first cluster number to caller // return first cluster number to caller
*curCluster = bgnCluster; *curCluster = bgnCluster;
@ -110,111 +111,94 @@ bool SdVolume::allocContiguous(uint32_t count, uint32_t* curCluster) {
if (setStart) allocSearchStart_ = bgnCluster + 1; if (setStart) allocSearchStart_ = bgnCluster + 1;
return true; return true;
FAIL:
return false;
} }
//------------------------------------------------------------------------------
bool SdVolume::cacheFlush() { bool SdVolume::cacheFlush() {
if (cacheDirty_) { if (cacheDirty_) {
if (!sdCard_->writeBlock(cacheBlockNumber_, cacheBuffer_.data)) { if (!sdCard_->writeBlock(cacheBlockNumber_, cacheBuffer_.data))
goto FAIL; return false;
}
// mirror FAT tables // mirror FAT tables
if (cacheMirrorBlock_) { if (cacheMirrorBlock_) {
if (!sdCard_->writeBlock(cacheMirrorBlock_, cacheBuffer_.data)) { if (!sdCard_->writeBlock(cacheMirrorBlock_, cacheBuffer_.data))
goto FAIL; return false;
}
cacheMirrorBlock_ = 0; cacheMirrorBlock_ = 0;
} }
cacheDirty_ = 0; cacheDirty_ = 0;
} }
return true; return true;
FAIL:
return false;
} }
//------------------------------------------------------------------------------
bool SdVolume::cacheRawBlock(uint32_t blockNumber, bool dirty) { bool SdVolume::cacheRawBlock(uint32_t blockNumber, bool dirty) {
if (cacheBlockNumber_ != blockNumber) { if (cacheBlockNumber_ != blockNumber) {
if (!cacheFlush()) goto FAIL; if (!cacheFlush()) return false;
if (!sdCard_->readBlock(blockNumber, cacheBuffer_.data)) goto FAIL; if (!sdCard_->readBlock(blockNumber, cacheBuffer_.data)) return false;
cacheBlockNumber_ = blockNumber; cacheBlockNumber_ = blockNumber;
} }
if (dirty) cacheDirty_ = true; if (dirty) cacheDirty_ = true;
return true; return true;
FAIL:
return false;
} }
//------------------------------------------------------------------------------
// return the size in bytes of a cluster chain // return the size in bytes of a cluster chain
bool SdVolume::chainSize(uint32_t cluster, uint32_t* size) { bool SdVolume::chainSize(uint32_t cluster, uint32_t* size) {
uint32_t s = 0; uint32_t s = 0;
do { do {
if (!fatGet(cluster, &cluster)) goto FAIL; if (!fatGet(cluster, &cluster)) return false;
s += 512UL << clusterSizeShift_; s += 512UL << clusterSizeShift_;
} while (!isEOC(cluster)); } while (!isEOC(cluster));
*size = s; *size = s;
return true; return true;
FAIL:
return false;
} }
//------------------------------------------------------------------------------
// Fetch a FAT entry // Fetch a FAT entry
bool SdVolume::fatGet(uint32_t cluster, uint32_t* value) { bool SdVolume::fatGet(uint32_t cluster, uint32_t* value) {
uint32_t lba; uint32_t lba;
if (cluster > (clusterCount_ + 1)) goto FAIL; if (cluster > (clusterCount_ + 1)) return false;
if (FAT12_SUPPORT && fatType_ == 12) { if (FAT12_SUPPORT && fatType_ == 12) {
uint16_t index = cluster; uint16_t index = cluster;
index += index >> 1; index += index >> 1;
lba = fatStartBlock_ + (index >> 9); lba = fatStartBlock_ + (index >> 9);
if (!cacheRawBlock(lba, CACHE_FOR_READ)) goto FAIL; if (!cacheRawBlock(lba, CACHE_FOR_READ)) return false;
index &= 0x1FF; index &= 0x1FF;
uint16_t tmp = cacheBuffer_.data[index]; uint16_t tmp = cacheBuffer_.data[index];
index++; index++;
if (index == 512) { if (index == 512) {
if (!cacheRawBlock(lba + 1, CACHE_FOR_READ)) goto FAIL; if (!cacheRawBlock(lba + 1, CACHE_FOR_READ)) return false;
index = 0; index = 0;
} }
tmp |= cacheBuffer_.data[index] << 8; tmp |= cacheBuffer_.data[index] << 8;
*value = cluster & 1 ? tmp >> 4 : tmp & 0xFFF; *value = cluster & 1 ? tmp >> 4 : tmp & 0xFFF;
return true; return true;
} }
if (fatType_ == 16) {
if (fatType_ == 16)
lba = fatStartBlock_ + (cluster >> 8); lba = fatStartBlock_ + (cluster >> 8);
} else if (fatType_ == 32)
else if (fatType_ == 32) {
lba = fatStartBlock_ + (cluster >> 7); lba = fatStartBlock_ + (cluster >> 7);
} else
else { return false;
goto FAIL;
} if (lba != cacheBlockNumber_ && !cacheRawBlock(lba, CACHE_FOR_READ))
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;
}
return true;
FAIL:
return false; return false;
*value = (fatType_ == 16) ? cacheBuffer_.fat16[cluster & 0xFF] : (cacheBuffer_.fat32[cluster & 0x7F] & FAT32MASK);
return true;
} }
//------------------------------------------------------------------------------
// Store a FAT entry // Store a FAT entry
bool SdVolume::fatPut(uint32_t cluster, uint32_t value) { bool SdVolume::fatPut(uint32_t cluster, uint32_t value) {
uint32_t lba; uint32_t lba;
// error if reserved cluster // error if reserved cluster
if (cluster < 2) goto FAIL; if (cluster < 2) return false;
// error if not in FAT // error if not in FAT
if (cluster > (clusterCount_ + 1)) goto FAIL; if (cluster > (clusterCount_ + 1)) return false;
if (FAT12_SUPPORT && fatType_ == 12) { if (FAT12_SUPPORT && fatType_ == 12) {
uint16_t index = cluster; uint16_t index = cluster;
index += index >> 1; index += index >> 1;
lba = fatStartBlock_ + (index >> 9); lba = fatStartBlock_ + (index >> 9);
if (!cacheRawBlock(lba, CACHE_FOR_WRITE)) goto FAIL; if (!cacheRawBlock(lba, CACHE_FOR_WRITE)) return false;
// mirror second FAT // mirror second FAT
if (fatCount_ > 1) cacheMirrorBlock_ = lba + blocksPerFat_; if (fatCount_ > 1) cacheMirrorBlock_ = lba + blocksPerFat_;
index &= 0x1FF; index &= 0x1FF;
@ -227,7 +211,7 @@ bool SdVolume::fatPut(uint32_t cluster, uint32_t value) {
if (index == 512) { if (index == 512) {
lba++; lba++;
index = 0; index = 0;
if (!cacheRawBlock(lba, CACHE_FOR_WRITE)) goto FAIL; if (!cacheRawBlock(lba, CACHE_FOR_WRITE)) return false;
// mirror second FAT // mirror second FAT
if (fatCount_ > 1) cacheMirrorBlock_ = lba + blocksPerFat_; if (fatCount_ > 1) cacheMirrorBlock_ = lba + blocksPerFat_;
} }
@ -238,51 +222,45 @@ bool SdVolume::fatPut(uint32_t cluster, uint32_t value) {
cacheBuffer_.data[index] = tmp; cacheBuffer_.data[index] = tmp;
return true; return true;
} }
if (fatType_ == 16) {
if (fatType_ == 16)
lba = fatStartBlock_ + (cluster >> 8); lba = fatStartBlock_ + (cluster >> 8);
} else if (fatType_ == 32)
else if (fatType_ == 32) {
lba = fatStartBlock_ + (cluster >> 7); lba = fatStartBlock_ + (cluster >> 7);
} else
else { return false;
goto FAIL;
} if (!cacheRawBlock(lba, CACHE_FOR_WRITE)) return false;
if (!cacheRawBlock(lba, CACHE_FOR_WRITE)) goto FAIL;
// store entry // store entry
if (fatType_ == 16) { if (fatType_ == 16)
cacheBuffer_.fat16[cluster & 0xFF] = value; cacheBuffer_.fat16[cluster & 0xFF] = value;
} else
else {
cacheBuffer_.fat32[cluster & 0x7F] = value; cacheBuffer_.fat32[cluster & 0x7F] = value;
}
// mirror second FAT // mirror second FAT
if (fatCount_ > 1) cacheMirrorBlock_ = lba + blocksPerFat_; if (fatCount_ > 1) cacheMirrorBlock_ = lba + blocksPerFat_;
return true; return true;
FAIL:
return false;
} }
//------------------------------------------------------------------------------
// free a cluster chain // free a cluster chain
bool SdVolume::freeChain(uint32_t cluster) { bool SdVolume::freeChain(uint32_t cluster) {
uint32_t next;
// clear free cluster location // clear free cluster location
allocSearchStart_ = 2; allocSearchStart_ = 2;
do { do {
if (!fatGet(cluster, &next)) goto FAIL; uint32_t next;
if (!fatGet(cluster, &next)) return false;
// free cluster // free cluster
if (!fatPut(cluster, 0)) goto FAIL; if (!fatPut(cluster, 0)) return false;
cluster = next; cluster = next;
} while (!isEOC(cluster)); } while (!isEOC(cluster));
return true; return true;
FAIL:
return false;
} }
//------------------------------------------------------------------------------
/** Volume free space in clusters. /** Volume free space in clusters.
* *
* \return Count of free clusters for success or -1 if an error occurs. * \return Count of free clusters for success or -1 if an error occurs.
@ -292,34 +270,28 @@ int32_t SdVolume::freeClusterCount() {
uint16_t n; uint16_t n;
uint32_t todo = clusterCount_ + 2; uint32_t todo = clusterCount_ + 2;
if (fatType_ == 16) { if (fatType_ == 16)
n = 256; n = 256;
} else if (fatType_ == 32)
else if (fatType_ == 32) {
n = 128; n = 128;
} else // put FAT12 here
else {
// put FAT12 here
return -1; return -1;
}
for (uint32_t lba = fatStartBlock_; todo; todo -= n, lba++) { for (uint32_t lba = fatStartBlock_; todo; todo -= n, lba++) {
if (!cacheRawBlock(lba, CACHE_FOR_READ)) return -1; if (!cacheRawBlock(lba, CACHE_FOR_READ)) return -1;
NOMORE(n, todo); NOMORE(n, todo);
if (fatType_ == 16) { 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++; if (cacheBuffer_.fat16[i] == 0) free++;
} }
}
else { else {
for (uint16_t i = 0; i < n; i++) { for (uint16_t i = 0; i < n; i++)
if (cacheBuffer_.fat32[i] == 0) free++; if (cacheBuffer_.fat32[i] == 0) free++;
} }
} }
}
return free; return free;
} }
//------------------------------------------------------------------------------
/** Initialize a FAT volume. /** Initialize a FAT volume.
* *
* \param[in] dev The SD card where the volume is located. * \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 MBR, Master Boot Record, or zero if the device is formatted as
* a super floppy with the FAT boot sector in block zero. * a super floppy with the FAT boot sector in block zero.
* *
* \return The value one, true, is returned for success and * \return true for success, false for failure.
* the value zero, false, is returned for failure. Reasons for * Reasons for failure include not finding a valid partition, not finding a valid
* failure include not finding a valid partition, not finding a valid
* FAT file system in the specified partition or an I/O error. * FAT file system in the specified partition or an I/O error.
*/ */
bool SdVolume::init(Sd2Card* dev, uint8_t part) { bool SdVolume::init(Sd2Card* dev, uint8_t part) {
uint32_t totalBlocks; uint32_t totalBlocks, volumeStartBlock = 0;
uint32_t volumeStartBlock = 0;
fat32_boot_t* fbs; fat32_boot_t* fbs;
sdCard_ = dev; 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 super floppy with FAT boot sector in block zero
// if part > 0 assume mbr volume with partition table // if part > 0 assume mbr volume with partition table
if (part) { if (part) {
if (part > 4)goto FAIL; if (part > 4) return false;
if (!cacheRawBlock(volumeStartBlock, CACHE_FOR_READ)) goto FAIL; if (!cacheRawBlock(volumeStartBlock, CACHE_FOR_READ)) return false;
part_t* p = &cacheBuffer_.mbr.part[part - 1]; part_t* p = &cacheBuffer_.mbr.part[part - 1];
if ((p->boot & 0x7F) != 0 || if ((p->boot & 0x7F) != 0 || p->totalSectors < 100 || p->firstSector == 0)
p->totalSectors < 100 || return false; // not a valid partition
p->firstSector == 0) {
// not a valid partition
goto FAIL;
}
volumeStartBlock = p->firstSector; volumeStartBlock = p->firstSector;
} }
if (!cacheRawBlock(volumeStartBlock, CACHE_FOR_READ)) goto FAIL; if (!cacheRawBlock(volumeStartBlock, CACHE_FOR_READ)) return false;
fbs = &cacheBuffer_.fbs32; fbs = &cacheBuffer_.fbs32;
if (fbs->bytesPerSector != 512 || if (fbs->bytesPerSector != 512 ||
fbs->fatCount == 0 || fbs->fatCount == 0 ||
fbs->reservedSectorCount == 0 || fbs->reservedSectorCount == 0 ||
fbs->sectorsPerCluster == 0) { fbs->sectorsPerCluster == 0) {
// not valid FAT volume // not valid FAT volume
goto FAIL; return false;
} }
fatCount_ = fbs->fatCount; fatCount_ = fbs->fatCount;
blocksPerCluster_ = fbs->sectorsPerCluster; blocksPerCluster_ = fbs->sectorsPerCluster;
@ -375,7 +341,7 @@ bool SdVolume::init(Sd2Card* dev, uint8_t part) {
clusterSizeShift_ = 0; clusterSizeShift_ = 0;
while (blocksPerCluster_ != _BV(clusterSizeShift_)) { while (blocksPerCluster_ != _BV(clusterSizeShift_)) {
// error if not power of 2 // error if not power of 2
if (clusterSizeShift_++ > 7) goto FAIL; if (clusterSizeShift_++ > 7) return false;
} }
blocksPerFat_ = fbs->sectorsPerFat16 ? blocksPerFat_ = fbs->sectorsPerFat16 ?
fbs->sectorsPerFat16 : fbs->sectorsPerFat32; fbs->sectorsPerFat16 : fbs->sectorsPerFat32;
@ -404,17 +370,15 @@ bool SdVolume::init(Sd2Card* dev, uint8_t part) {
// FAT type is determined by cluster count // FAT type is determined by cluster count
if (clusterCount_ < 4085) { if (clusterCount_ < 4085) {
fatType_ = 12; fatType_ = 12;
if (!FAT12_SUPPORT) goto FAIL; if (!FAT12_SUPPORT) return false;
} }
else if (clusterCount_ < 65525) { else if (clusterCount_ < 65525)
fatType_ = 16; fatType_ = 16;
}
else { else {
rootDirStart_ = fbs->fat32RootCluster; rootDirStart_ = fbs->fat32RootCluster;
fatType_ = 32; fatType_ = 32;
} }
return true; return true;
FAIL:
return false;
} }
#endif
#endif // SDSUPPORT

@ -20,20 +20,20 @@
* *
*/ */
/**
* \file
* \brief SdVolume class
*/
/** /**
* Arduino SdFat Library * Arduino SdFat Library
* Copyright (C) 2009 by William Greiman * Copyright (C) 2009 by William Greiman
* *
* This file is part of the Arduino Sd2Card Library * This file is part of the Arduino Sd2Card Library
*/ */
#include "Marlin.h" #ifndef _SDVOLUME_H_
#if ENABLED(SDSUPPORT) #define _SDVOLUME_H_
#ifndef SdVolume_h
#define SdVolume_h
/**
* \file
* \brief SdVolume class
*/
#include "SdFatConfig.h" #include "SdFatConfig.h"
#include "Sd2Card.h" #include "Sd2Card.h"
#include "SdFatStructs.h" #include "SdFatStructs.h"
@ -44,33 +44,26 @@
* \brief Cache for an SD data block * \brief Cache for an SD data block
*/ */
union cache_t { union cache_t {
/** Used to access cached file data blocks. */ uint8_t data[512]; // Used to access cached file data blocks.
uint8_t data[512]; uint16_t fat16[256]; // Used to access cached FAT16 entries.
/** Used to access cached FAT16 entries. */ uint32_t fat32[128]; // Used to access cached FAT32 entries.
uint16_t fat16[256]; dir_t dir[16]; // Used to access cached directory entries.
/** Used to access cached FAT32 entries. */ mbr_t mbr; // Used to access a cached Master Boot Record.
uint32_t fat32[128]; fat_boot_t fbs; // Used to access to a cached FAT boot sector.
/** Used to access cached directory entries. */ fat32_boot_t fbs32; // Used to access to a cached FAT32 boot sector.
dir_t dir[16]; fat32_fsinfo_t fsinfo; // Used to access to a cached FAT32 FSINFO sector.
/** Used to access a cached Master Boot Record. */
mbr_t mbr;
/** Used to access to a cached FAT boot sector. */
fat_boot_t fbs;
/** Used to access to a cached FAT32 boot sector. */
fat32_boot_t fbs32;
/** Used to access to a cached FAT32 FSINFO sector. */
fat32_fsinfo_t fsinfo;
}; };
//------------------------------------------------------------------------------
/** /**
* \class SdVolume * \class SdVolume
* \brief Access FAT16 and FAT32 volumes on SD and SDHC cards. * \brief Access FAT16 and FAT32 volumes on SD and SDHC cards.
*/ */
class SdVolume { class SdVolume {
public: public:
/** Create an instance of SdVolume */ // Create an instance of SdVolume
SdVolume() : fatType_(0) {} 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. * 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. * \return A pointer to the cache buffer or zero if an error occurs.
*/ */
@ -79,54 +72,53 @@ class SdVolume {
cacheBlockNumber_ = 0xFFFFFFFF; cacheBlockNumber_ = 0xFFFFFFFF;
return &cacheBuffer_; 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. * floppy format.
* *
* \param[in] dev The Sd2Card where the volume is located. * \param[in] dev The Sd2Card where the volume is located.
* *
* \return The value one, true, is returned for success and * \return true for success, false for failure.
* the value zero, false, is returned for failure. Reasons for * Reasons for failure include not finding a valid partition, not finding
* failure include not finding a valid partition, not finding a valid * a valid FAT file system or an I/O error.
* 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); bool init(Sd2Card* dev, uint8_t part);
// inline functions that return volume info // inline functions that return volume info
/** \return The volume's cluster size in blocks. */ uint8_t blocksPerCluster() const { return blocksPerCluster_; } //> \return The volume's cluster size in blocks.
uint8_t blocksPerCluster() const {return blocksPerCluster_;} uint32_t blocksPerFat() const { return blocksPerFat_; } //> \return The number of blocks in one FAT.
/** \return The number of blocks in one FAT. */ uint32_t clusterCount() const { return clusterCount_; } //> \return The total number of clusters in the volume.
uint32_t blocksPerFat() const {return blocksPerFat_;} uint8_t clusterSizeShift() const { return clusterSizeShift_; } //> \return The shift count required to multiply by blocksPerCluster.
/** \return The total number of clusters in the volume. */ uint32_t dataStartBlock() const { return dataStartBlock_; } //> \return The logical block number for the start of file data.
uint32_t clusterCount() const {return clusterCount_;} uint8_t fatCount() const { return fatCount_; } //> \return The number of FAT structures on the volume.
/** \return The shift count required to multiply by blocksPerCluster. */ uint32_t fatStartBlock() const { return fatStartBlock_; } //> \return The logical block number for the start of the first FAT.
uint8_t clusterSizeShift() const {return clusterSizeShift_;} uint8_t fatType() const { return fatType_; } //> \return The FAT type of the volume. Values are 12, 16 or 32.
/** \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_;}
int32_t freeClusterCount(); int32_t freeClusterCount();
/** \return The number of entries in the root directory for FAT16 volumes. */ uint32_t rootDirEntryCount() const { return rootDirEntryCount_; } /** \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. */ * \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_; } uint32_t rootDirStart() const { return rootDirStart_; }
/** Sd2Card object for this volume
/**
* Sd2Card object for this volume
* \return pointer to Sd2Card object. * \return pointer to Sd2Card object.
*/ */
Sd2Card* sdCard() { return sdCard_; } Sd2Card* sdCard() { return sdCard_; }
/** Debug access to FAT table
/**
* Debug access to FAT table
* *
* \param[in] n cluster number. * \param[in] n cluster number.
* \param[out] v value of entry * \param[out] v value of entry
* \return true for success or false for failure * \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: private:
// Allow SdBaseFile access to SdVolume private data. // Allow SdBaseFile access to SdVolume private data.
friend class SdBaseFile; friend class SdBaseFile;
@ -142,13 +134,14 @@ class SdVolume {
Sd2Card* sdCard_; // Sd2Card object for cache Sd2Card* sdCard_; // Sd2Card object for cache
bool cacheDirty_; // cacheFlush() will write block if true bool cacheDirty_; // cacheFlush() will write block if true
uint32_t cacheMirrorBlock_; // block number for mirror FAT uint32_t cacheMirrorBlock_; // block number for mirror FAT
#else // USE_MULTIPLE_CARDS #else
static cache_t cacheBuffer_; // 512 byte cache for device blocks static cache_t cacheBuffer_; // 512 byte cache for device blocks
static uint32_t cacheBlockNumber_; // Logical number of block in the cache static uint32_t cacheBlockNumber_; // Logical number of block in the cache
static Sd2Card* sdCard_; // Sd2Card object for cache static Sd2Card* sdCard_; // Sd2Card object for cache
static bool cacheDirty_; // cacheFlush() will write block if true static bool cacheDirty_; // cacheFlush() will write block if true
static uint32_t cacheMirrorBlock_; // block number for mirror FAT static uint32_t cacheMirrorBlock_; // block number for mirror FAT
#endif // USE_MULTIPLE_CARDS #endif
uint32_t allocSearchStart_; // start cluster for alloc search uint32_t allocSearchStart_; // start cluster for alloc search
uint8_t blocksPerCluster_; // cluster size in blocks uint8_t blocksPerCluster_; // cluster size in blocks
uint32_t blocksPerFat_; // FAT size in blocks uint32_t blocksPerFat_; // FAT size in blocks
@ -160,26 +153,23 @@ class SdVolume {
uint8_t fatType_; // volume type (12, 16, OR 32) uint8_t fatType_; // volume type (12, 16, OR 32)
uint16_t rootDirEntryCount_; // number of entries in FAT16 root dir uint16_t rootDirEntryCount_; // number of entries in FAT16 root dir
uint32_t rootDirStart_; // root start block for FAT16, cluster for FAT32 uint32_t rootDirStart_; // root start block for FAT16, cluster for FAT32
//----------------------------------------------------------------------------
bool allocContiguous(uint32_t count, uint32_t* curCluster); bool allocContiguous(uint32_t count, uint32_t* curCluster);
uint8_t blockOfCluster(uint32_t position) const { uint8_t blockOfCluster(uint32_t position) const { return (position >> 9) & (blocksPerCluster_ - 1); }
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); }
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_; } cache_t* cache() { return &cacheBuffer_; }
uint32_t cacheBlockNumber() {return cacheBlockNumber_;} uint32_t cacheBlockNumber() const { return cacheBlockNumber_; }
#if USE_MULTIPLE_CARDS #if USE_MULTIPLE_CARDS
bool cacheFlush(); bool cacheFlush();
bool cacheRawBlock(uint32_t blockNumber, bool dirty); bool cacheRawBlock(uint32_t blockNumber, bool dirty);
#else // USE_MULTIPLE_CARDS #else
static bool cacheFlush(); static bool cacheFlush();
static bool cacheRawBlock(uint32_t blockNumber, bool dirty); static bool cacheRawBlock(uint32_t blockNumber, bool dirty);
#endif // USE_MULTIPLE_CARDS #endif
// used by SdBaseFile write to assign cache to SD location // used by SdBaseFile write to assign cache to SD location
void cacheSetBlockNumber(uint32_t blockNumber, bool dirty) { void cacheSetBlockNumber(uint32_t blockNumber, bool dirty) {
cacheDirty_ = dirty; cacheDirty_ = dirty;
@ -189,39 +179,33 @@ class SdVolume {
bool chainSize(uint32_t beginCluster, uint32_t* size); bool chainSize(uint32_t beginCluster, uint32_t* size);
bool fatGet(uint32_t cluster, uint32_t* value); bool fatGet(uint32_t cluster, uint32_t* value);
bool fatPut(uint32_t cluster, uint32_t value); bool fatPut(uint32_t cluster, uint32_t value);
bool fatPutEOC(uint32_t cluster) { bool fatPutEOC(uint32_t cluster) { return fatPut(cluster, 0x0FFFFFFF); }
return fatPut(cluster, 0x0FFFFFFF);
}
bool freeChain(uint32_t cluster); bool freeChain(uint32_t cluster);
bool isEOC(uint32_t cluster) const { bool isEOC(uint32_t cluster) const {
if (FAT12_SUPPORT && fatType_ == 12) return cluster >= FAT12EOC_MIN; if (FAT12_SUPPORT && fatType_ == 12) return cluster >= FAT12EOC_MIN;
if (fatType_ == 16) return cluster >= FAT16EOC_MIN; if (fatType_ == 16) return cluster >= FAT16EOC_MIN;
return cluster >= FAT32EOC_MIN; return cluster >= FAT32EOC_MIN;
} }
bool readBlock(uint32_t block, uint8_t* dst) { bool readBlock(uint32_t block, uint8_t* dst) { return sdCard_->readBlock(block, dst); }
return sdCard_->readBlock(block, dst); bool writeBlock(uint32_t block, const uint8_t* dst) { return sdCard_->writeBlock(block, dst); }
}
bool writeBlock(uint32_t block, const uint8_t* dst) { // Deprecated functions
return sdCard_->writeBlock(block, dst); #if ALLOW_DEPRECATED_FUNCTIONS
}
//------------------------------------------------------------------------------
// Deprecated functions - suppress cpplint warnings with NOLINT comment
#if ALLOW_DEPRECATED_FUNCTIONS && !defined(DOXYGEN)
public: public:
/** \deprecated Use: bool SdVolume::init(Sd2Card* dev); /**
* \deprecated Use: bool SdVolume::init(Sd2Card* dev);
* \param[in] dev The SD card where the volume is located. * \param[in] dev The SD card where the volume is located.
* \return true for success or false for failure. * \return true for success or false for failure.
*/ */
bool init(Sd2Card& dev) {return init(&dev);} // NOLINT bool init(Sd2Card& dev) { return init(&dev); }
/** \deprecated Use: bool SdVolume::init(Sd2Card* dev, uint8_t vol); /**
* \deprecated Use: bool SdVolume::init(Sd2Card* dev, uint8_t vol);
* \param[in] dev The SD card where the volume is located. * \param[in] dev The SD card where the volume is located.
* \param[in] part The partition to be used. * \param[in] part The partition to be used.
* \return true for success or false for failure. * \return true for success or false for failure.
*/ */
bool init(Sd2Card& dev, uint8_t part) { // NOLINT bool init(Sd2Card& dev, uint8_t part) { return init(&dev, part); }
return init(&dev, part);
}
#endif // ALLOW_DEPRECATED_FUNCTIONS #endif // ALLOW_DEPRECATED_FUNCTIONS
}; };
#endif // SdVolume
#endif #endif // _SDVOLUME_H_

@ -20,16 +20,16 @@
* *
*/ */
#include "MarlinConfig.h"
#if ENABLED(SDSUPPORT)
#include "cardreader.h" #include "cardreader.h"
#include "ultralcd.h" #include "ultralcd.h"
#include "stepper.h" #include "stepper.h"
#include "language.h" #include "language.h"
#include "Marlin.h"
#if ENABLED(SDSUPPORT)
#define LONGEST_FILENAME (longFilename[0] ? longFilename : filename) #define LONGEST_FILENAME (longFilename[0] ? longFilename : filename)
CardReader::CardReader() { CardReader::CardReader() {
@ -44,8 +44,9 @@ CardReader::CardReader() {
sdprinting = cardOK = saving = logging = false; sdprinting = cardOK = saving = logging = false;
filesize = 0; filesize = 0;
sdpos = 0; sdpos = 0;
workDirDepth = 0;
file_subcall_ctr = 0; file_subcall_ctr = 0;
workDirDepth = 0;
ZERO(workDirParents); 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. 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: * 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_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 * 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*/) { void CardReader::lsDive(const char *prepend, SdFile parent, const char * const match/*=NULL*/) {
dir_t p; dir_t p;
uint8_t cnt = 0; 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; if (!filenameIsDir && (p.name[8] != 'G' || p.name[9] == '~')) continue;
switch (lsAction) { switch (lsAction) { // 1 based file count
case LS_Count: case LS_Count:
nrFiles++; nrFiles++;
break; break;
@ -147,7 +151,7 @@ void CardReader::lsDive(const char *prepend, SdFile parent, const char * const m
if (match != NULL) { if (match != NULL) {
if (strcasecmp(match, filename) == 0) return; if (strcasecmp(match, filename) == 0) return;
} }
else if (cnt == nrFiles) return; else if (cnt == nrFile_index) return; // 0 based index
cnt++; cnt++;
break; break;
} }
@ -255,16 +259,7 @@ void CardReader::initsd() {
SERIAL_ECHO_START(); SERIAL_ECHO_START();
SERIAL_ECHOLNPGM(MSG_SD_CARD_OK); SERIAL_ECHOLNPGM(MSG_SD_CARD_OK);
} }
workDir = root; setroot();
curDir = &root;
#if ENABLED(SDCARD_SORT_ALPHA)
presort();
#endif
/**
if (!workDir.openRoot(&volume)) {
SERIAL_ECHOLNPGM(MSG_SD_WORKDIR_FAIL);
}
*/
} }
void CardReader::setroot() { void CardReader::setroot() {
@ -310,26 +305,33 @@ void CardReader::openLogFile(char* name) {
openFile(name, false); 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) { void CardReader::getAbsFilename(char *t) {
uint8_t cnt = 0; *t++ = '/'; // Root folder
*t = '/'; t++; cnt++; uint8_t cnt = 1;
for (uint8_t i = 0; i < workDirDepth; i++) {
workDirParents[i].getFilename(t); //SDBaseFile.getfilename! for (uint8_t i = 0; i < workDirDepth; i++) // Loop to current work dir
while (*t && cnt < MAXPATHNAMELENGTH) { t++; cnt++; } //crawl counter forward. appendAtom(workDirParents[i], t, cnt);
if (cnt < MAXPATHNAMELENGTH - (FILENAME_LENGTH)) {
appendAtom(file, t, cnt);
--t;
} }
if (cnt < MAXPATHNAMELENGTH - (FILENAME_LENGTH)) *t = '\0';
file.getFilename(t);
else
t[0] = 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; if (!cardOK) return;
uint8_t doing = 0; uint8_t doing = 0;
if (isFileOpen()) { //replacing current file by new file, or subfile call if (isFileOpen()) { // Replacing current file or doing a subroutine
if (push_current) { if (subcall) {
if (file_subcall_ctr > SD_PROCEDURE_DEPTH - 1) { if (file_subcall_ctr > SD_PROCEDURE_DEPTH - 1) {
SERIAL_ERROR_START(); SERIAL_ERROR_START();
SERIAL_ERRORPGM("trying to call sub-gcode files with too many levels. MAX level is:"); SERIAL_ERRORPGM("trying to call sub-gcode files with too many levels. MAX level is:");
@ -338,19 +340,22 @@ void CardReader::openFile(char* name, bool read, bool push_current/*=false*/) {
return; return;
} }
// Store current filename and position // Store current filename (based on workDirParents) and position
getAbsFilename(proc_filenames[file_subcall_ctr]); getAbsFilename(proc_filenames[file_subcall_ctr]);
filespos[file_subcall_ctr] = sdpos;
SERIAL_ECHO_START(); SERIAL_ECHO_START();
SERIAL_ECHOPAIR("SUBROUTINE CALL target:\"", name); SERIAL_ECHOPAIR("SUBROUTINE CALL target:\"", name);
SERIAL_ECHOPAIR("\" parent:\"", proc_filenames[file_subcall_ctr]); SERIAL_ECHOPAIR("\" parent:\"", proc_filenames[file_subcall_ctr]);
SERIAL_ECHOLNPAIR("\" pos", sdpos); SERIAL_ECHOLNPAIR("\" pos", sdpos);
filespos[file_subcall_ctr] = sdpos;
file_subcall_ctr++; file_subcall_ctr++;
} }
else { else
doing = 1; doing = 1;
} }
else if (subcall) { // Returning from a subcall?
SERIAL_ECHO_START();
SERIAL_ECHOLNPGM("END SUBROUTINE");
} }
else { // Opening fresh file else { // Opening fresh file
doing = 2; doing = 2;
@ -360,7 +365,7 @@ void CardReader::openFile(char* name, bool read, bool push_current/*=false*/) {
if (doing) { if (doing) {
SERIAL_ECHO_START(); SERIAL_ECHO_START();
SERIAL_ECHOPGM("Now "); SERIAL_ECHOPGM("Now ");
SERIAL_ECHO(doing == 1 ? "doing" : "fresh"); serialprintPGM(doing == 1 ? PSTR("doing") : PSTR("fresh"));
SERIAL_ECHOLNPAIR(" file: ", name); 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) { if (dirname_end != NULL && dirname_end > dirname_start) {
char subdirname[FILENAME_LENGTH]; char subdirname[FILENAME_LENGTH];
strncpy(subdirname, dirname_start, dirname_end - dirname_start); strncpy(subdirname, dirname_start, dirname_end - dirname_start);
subdirname[dirname_end - dirname_start] = 0; subdirname[dirname_end - dirname_start] = '\0';
SERIAL_ECHOLN(subdirname);
if (!myDir.open(curDir, subdirname, O_READ)) { if (!myDir.open(curDir, subdirname, O_READ)) {
SERIAL_PROTOCOLPGM(MSG_SD_OPEN_FILE_FAIL); SERIAL_PROTOCOLPGM(MSG_SD_OPEN_FILE_FAIL);
SERIAL_PROTOCOL(subdirname); SERIAL_PROTOCOL(subdirname);
@ -403,17 +407,15 @@ void CardReader::openFile(char* name, bool read, bool push_current/*=false*/) {
} }
} }
} }
else { //relative path else
curDir = &workDir; curDir = &workDir; // Relative paths start in current directory
}
if (read) { if (read) {
if (file.open(curDir, fname, O_READ)) { if (file.open(curDir, fname, O_READ)) {
filesize = file.fileSize(); filesize = file.fileSize();
sdpos = 0;
SERIAL_PROTOCOLPAIR(MSG_SD_FILE_OPENED, fname); SERIAL_PROTOCOLPAIR(MSG_SD_FILE_OPENED, fname);
SERIAL_PROTOCOLLNPAIR(MSG_SD_SIZE, filesize); SERIAL_PROTOCOLLNPAIR(MSG_SD_SIZE, filesize);
sdpos = 0;
SERIAL_PROTOCOLLNPGM(MSG_SD_FILE_SELECTED); SERIAL_PROTOCOLLNPGM(MSG_SD_FILE_SELECTED);
getfilename(0, fname); getfilename(0, fname);
lcd_setstatus(longFilename[0] ? longFilename : 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; if (!cardOK) return;
stopSDPrint(); stopSDPrint();
SdFile myDir; SdFile myDir;
curDir = &root; curDir = &root;
char *fname = name; const char *fname = name;
char *dirname_start, *dirname_end; char *dirname_start, *dirname_end;
if (name[0] == '/') { if (name[0] == '/') {
@ -460,29 +462,23 @@ void CardReader::removeFile(char* name) {
subdirname[dirname_end - dirname_start] = 0; subdirname[dirname_end - dirname_start] = 0;
SERIAL_ECHOLN(subdirname); SERIAL_ECHOLN(subdirname);
if (!myDir.open(curDir, subdirname, O_READ)) { if (!myDir.open(curDir, subdirname, O_READ)) {
SERIAL_PROTOCOLPAIR("open failed, File: ", subdirname); SERIAL_PROTOCOLPAIR(MSG_SD_OPEN_FILE_FAIL, subdirname);
SERIAL_PROTOCOLCHAR('.'); SERIAL_PROTOCOLCHAR('.');
SERIAL_EOL(); SERIAL_EOL();
return; return;
} }
else {
//SERIAL_ECHOLNPGM("dive ok");
}
curDir = &myDir; curDir = &myDir;
dirname_start = dirname_end + 1; dirname_start = dirname_end + 1;
} }
else { // the remainder after all /fsa/fdsa/ is the filename else {
fname = dirname_start; fname = dirname_start;
//SERIAL_ECHOLNPGM("remainder");
//SERIAL_ECHOLN(fname);
break; break;
} }
} }
} }
else { // relative path else // Relative paths are rooted in the current directory
curDir = &workDir; curDir = &workDir;
}
if (file.remove(curDir, fname)) { if (file.remove(curDir, fname)) {
SERIAL_PROTOCOLPGM("File deleted:"); SERIAL_PROTOCOLPGM("File deleted:");
@ -506,14 +502,13 @@ void CardReader::getStatus() {
SERIAL_PROTOCOLCHAR('/'); SERIAL_PROTOCOLCHAR('/');
SERIAL_PROTOCOLLN(filesize); SERIAL_PROTOCOLLN(filesize);
} }
else { else
SERIAL_PROTOCOLLNPGM(MSG_SD_NOT_PRINTING); SERIAL_PROTOCOLLNPGM(MSG_SD_NOT_PRINTING);
} }
}
void CardReader::write_command(char *buf) { void CardReader::write_command(char *buf) {
char* begin = buf; char* begin = buf;
char* npos = 0; char* npos = NULL;
char* end = buf + strlen(buf) - 1; char* end = buf + strlen(buf) - 1;
file.writeError = false; file.writeError = false;
@ -595,7 +590,7 @@ void CardReader::getfilename(uint16_t nr, const char * const match/*=NULL*/) {
#endif // SDSORT_CACHE_NAMES #endif // SDSORT_CACHE_NAMES
curDir = &workDir; curDir = &workDir;
lsAction = LS_GetFilename; lsAction = LS_GetFilename;
nrFiles = nr; nrFile_index = nr;
curDir->rewind(); curDir->rewind();
lsDive("", *curDir, match); lsDive("", *curDir, match);
} }
@ -611,20 +606,20 @@ uint16_t CardReader::getnrfilenames() {
} }
void CardReader::chdir(const char * relpath) { void CardReader::chdir(const char * relpath) {
SdFile newfile; SdFile newDir;
SdFile *parent = &root; SdFile *parent = &root;
if (workDir.isOpen()) parent = &workDir; if (workDir.isOpen()) parent = &workDir;
if (!newfile.open(*parent, relpath, O_READ)) { if (!newDir.open(*parent, relpath, O_READ)) {
SERIAL_ECHO_START(); SERIAL_ECHO_START();
SERIAL_ECHOPGM(MSG_SD_CANT_ENTER_SUBDIR); SERIAL_ECHOPGM(MSG_SD_CANT_ENTER_SUBDIR);
SERIAL_ECHOLN(relpath); SERIAL_ECHOLN(relpath);
} }
else { else {
workDir = newDir;
if (workDirDepth < MAX_DIR_DEPTH) if (workDirDepth < MAX_DIR_DEPTH)
workDirParents[workDirDepth++] = *parent; workDirParents[workDirDepth++] = workDir;
workDir = newfile;
#if ENABLED(SDCARD_SORT_ALPHA) #if ENABLED(SDCARD_SORT_ALPHA)
presort(); presort();
#endif #endif
@ -632,8 +627,8 @@ void CardReader::chdir(const char * relpath) {
} }
void CardReader::updir() { void CardReader::updir() {
if (workDirDepth > 0) { if (workDirDepth > 0) { // At least 1 dir has been saved
workDir = workDirParents[--workDirDepth]; workDir = --workDirDepth ? workDirParents[workDirDepth] : root; // Use parent, or root if none
#if ENABLED(SDCARD_SORT_ALPHA) #if ENABLED(SDCARD_SORT_ALPHA)
presort(); presort();
#endif #endif
@ -696,7 +691,7 @@ void CardReader::updir() {
sortnames = new char*[fileCnt]; sortnames = new char*[fileCnt];
#endif #endif
#elif ENABLED(SDSORT_USES_STACK) #elif ENABLED(SDSORT_USES_STACK)
char sortnames[fileCnt][LONG_FILENAME_LENGTH]; char sortnames[fileCnt][SORTED_LONGNAME_MAXLEN];
#endif #endif
// Folder sorting needs 1 bit per entry for flags. // Folder sorting needs 1 bit per entry for flags.
@ -735,7 +730,12 @@ void CardReader::updir() {
#endif #endif
#else #else
// Copy filenames into the static array // 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) #if ENABLED(SDSORT_CACHE_NAMES)
strcpy(sortshort[i], filename); strcpy(sortshort[i], filename);
#endif #endif
@ -826,13 +826,22 @@ void CardReader::updir() {
#if ENABLED(SDSORT_DYNAMIC_RAM) #if ENABLED(SDSORT_DYNAMIC_RAM)
sortnames = new char*[1]; sortnames = new char*[1];
sortnames[0] = strdup(LONGEST_FILENAME); // malloc sortnames[0] = strdup(LONGEST_FILENAME); // malloc
#if ENABLED(SDSORT_CACHE_NAMES)
sortshort = new char*[1]; sortshort = new char*[1];
sortshort[0] = strdup(filename); // malloc sortshort[0] = strdup(filename); // malloc
#endif
isDir = new uint8_t[1]; isDir = new uint8_t[1];
#else #else
strcpy(sortnames[0], LONGEST_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); strcpy(sortshort[0], filename);
#endif #endif
#endif
isDir[0] = filenameIsDir ? 0x01 : 0x00; isDir[0] = filenameIsDir ? 0x01 : 0x00;
#endif #endif
} }
@ -860,6 +869,16 @@ void CardReader::updir() {
#endif // SDCARD_SORT_ALPHA #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() { void CardReader::printingHasFinished() {
stepper.synchronize(); stepper.synchronize();
file.close(); file.close();

@ -20,8 +20,8 @@
* *
*/ */
#ifndef CARDREADER_H #ifndef _CARDREADER_H_
#define CARDREADER_H #define _CARDREADER_H_
#include "MarlinConfig.h" #include "MarlinConfig.h"
@ -30,7 +30,6 @@
#define MAX_DIR_DEPTH 10 // Maximum folder depth #define MAX_DIR_DEPTH 10 // Maximum folder depth
#include "SdFile.h" #include "SdFile.h"
#include "types.h" #include "types.h"
#include "enum.h" #include "enum.h"
@ -40,13 +39,15 @@ public:
void initsd(); void initsd();
void write_command(char *buf); void write_command(char *buf);
//files auto[0-9].g on the sd card are performed in a row // Files auto[0-9].g on the sd card are performed in sequence.
//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 // 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 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 openLogFile(char* name);
void removeFile(char* name); void removeFile(const char * const name);
void closefile(bool store_location=false); void closefile(bool store_location=false);
void release(); void release();
void openAndPrintFile(const char *name); void openAndPrintFile(const char *name);
@ -69,6 +70,8 @@ public:
void updir(); void updir();
void setroot(); void setroot();
uint16_t get_num_Files();
#if ENABLED(SDCARD_SORT_ALPHA) #if ENABLED(SDCARD_SORT_ALPHA)
void presort(); void presort();
void getfilename_sorted(const uint16_t nr); void getfilename_sorted(const uint16_t nr);
@ -111,6 +114,12 @@ private:
uint8_t sort_order[SDSORT_LIMIT]; uint8_t sort_order[SDSORT_LIMIT];
#endif #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. // Cache filenames to speed up SD menus.
#if ENABLED(SDSORT_USES_RAM) #if ENABLED(SDSORT_USES_RAM)
@ -120,10 +129,10 @@ private:
char **sortshort, **sortnames; char **sortshort, **sortnames;
#else #else
char sortshort[SDSORT_LIMIT][FILENAME_LENGTH]; char sortshort[SDSORT_LIMIT][FILENAME_LENGTH];
char sortnames[SDSORT_LIMIT][SDSORT_CACHE_VFATS * FILENAME_LENGTH + 1]; char sortnames[SDSORT_LIMIT][SORTED_LONGNAME_MAXLEN];
#endif #endif
#elif DISABLED(SDSORT_USES_STACK) #elif DISABLED(SDSORT_USES_STACK)
char sortnames[SDSORT_LIMIT][LONG_FILENAME_LENGTH]; char sortnames[SDSORT_LIMIT][SORTED_LONGNAME_MAXLEN];
#endif #endif
// Folder sorting uses an isDir array when caching items. // Folder sorting uses an isDir array when caching items.
@ -148,8 +157,7 @@ private:
uint8_t file_subcall_ctr; uint8_t file_subcall_ctr;
uint32_t filespos[SD_PROCEDURE_DEPTH]; uint32_t filespos[SD_PROCEDURE_DEPTH];
char proc_filenames[SD_PROCEDURE_DEPTH][MAXPATHNAMELENGTH]; char proc_filenames[SD_PROCEDURE_DEPTH][MAXPATHNAMELENGTH];
uint32_t filesize; uint32_t filesize, sdpos;
uint32_t sdpos;
millis_t next_autostart_ms; 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. 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 #endif
}; };
extern CardReader card;
#define IS_SD_PRINTING (card.sdprinting)
#define IS_SD_FILE_OPEN (card.isFileOpen())
#if PIN_EXISTS(SD_DETECT) #if PIN_EXISTS(SD_DETECT)
#if ENABLED(SD_DETECT_INVERTED) #if ENABLED(SD_DETECT_INVERTED)
#define IS_SD_INSERTED (READ(SD_DETECT_PIN) != 0) #define IS_SD_INSERTED (READ(SD_DETECT_PIN) == HIGH)
#else #else
#define IS_SD_INSERTED (READ(SD_DETECT_PIN) == 0) #define IS_SD_INSERTED (READ(SD_DETECT_PIN) == LOW)
#endif #endif
#else #else
// No card detect line? Assume the card is inserted. // No card detect line? Assume the card is inserted.
#define IS_SD_INSERTED true #define IS_SD_INSERTED true
#endif #endif
#else extern CardReader card;
#endif // SDSUPPORT
#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_PRINTING (false)
#define IS_SD_FILE_OPEN (false) #define IS_SD_FILE_OPEN (false)
#endif
#endif // SDSUPPORT #endif // _CARDREADER_H_
#endif // __CARDREADER_H

@ -3799,7 +3799,8 @@ void kill_screen(const char* lcd_msg) {
void lcd_sdcard_menu() { void lcd_sdcard_menu() {
ENCODER_DIRECTION_MENUS(); ENCODER_DIRECTION_MENUS();
const uint16_t fileCnt = card.getnrfilenames(); const uint16_t fileCnt = card.get_num_Files();
START_MENU(); START_MENU();
MENU_BACK(MSG_MAIN); MENU_BACK(MSG_MAIN);
card.getWorkDirName(); card.getWorkDirName();

Loading…
Cancel
Save