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