Cleanup, apply standards to SD lib, cardreader

master
Scott Lahteine 7 years ago
parent d8b1343279
commit 2390f6d3ab

@ -26,19 +26,19 @@
*
* This file is part of the Arduino Sd2Card Library
*/
#include "Marlin.h"
#include "MarlinConfig.h"
#if ENABLED(SDSUPPORT)
#include "Sd2Card.h"
#if ENABLED(USE_WATCHDOG)
#include "watchdog.h"
#endif
//------------------------------------------------------------------------------
#if DISABLED(SOFTWARE_SPI)
// functions for hardware SPI
//------------------------------------------------------------------------------
// make sure SPCR rate is in expected bits
#if (SPR0 != 0 || SPR1 != 1)
#error "unexpected SPCR bits"
@ -52,14 +52,14 @@
SPCR = _BV(SPE) | _BV(MSTR) | (spiRate >> 1);
SPSR = spiRate & 1 || spiRate == 6 ? 0 : _BV(SPI2X);
}
//------------------------------------------------------------------------------
/** SPI receive a byte */
static uint8_t spiRec() {
SPDR = 0xFF;
while (!TEST(SPSR, SPIF)) { /* Intentionally left empty */ }
return SPDR;
}
//------------------------------------------------------------------------------
/** SPI read data - only one call so force inline */
static inline __attribute__((always_inline))
void spiRead(uint8_t* buf, uint16_t nbyte) {
@ -73,13 +73,13 @@
while (!TEST(SPSR, SPIF)) { /* Intentionally left empty */ }
buf[nbyte] = SPDR;
}
//------------------------------------------------------------------------------
/** SPI send a byte */
static void spiSend(uint8_t b) {
SPDR = b;
while (!TEST(SPSR, SPIF)) { /* Intentionally left empty */ }
}
//------------------------------------------------------------------------------
/** SPI send block - only one call so force inline */
static inline __attribute__((always_inline))
void spiSendBlock(uint8_t token, const uint8_t* buf) {
@ -95,9 +95,10 @@
//------------------------------------------------------------------------------
#else // SOFTWARE_SPI
//------------------------------------------------------------------------------
/** nop to tune soft SPI timing */
#define nop asm volatile ("nop\n\t")
//------------------------------------------------------------------------------
/** Soft SPI receive byte */
static uint8_t spiRec() {
uint8_t data = 0;
@ -123,13 +124,13 @@
sei();
return data;
}
//------------------------------------------------------------------------------
/** Soft SPI read data */
static void spiRead(uint8_t* buf, uint16_t nbyte) {
for (uint16_t i = 0; i < nbyte; i++)
buf[i] = spiRec();
}
//------------------------------------------------------------------------------
/** Soft SPI send byte */
static void spiSend(uint8_t data) {
// no interrupts during byte send - about 8 us
@ -153,7 +154,7 @@
// enable interrupts
sei();
}
//------------------------------------------------------------------------------
/** Soft SPI send block */
void spiSendBlock(uint8_t token, const uint8_t* buf) {
spiSend(token);
@ -161,7 +162,7 @@
spiSend(buf[i]);
}
#endif // SOFTWARE_SPI
//------------------------------------------------------------------------------
// send command and return error code. Return zero for OK
uint8_t Sd2Card::cardCommand(uint8_t cmd, uint32_t arg) {
// select card
@ -189,7 +190,7 @@ uint8_t Sd2Card::cardCommand(uint8_t cmd, uint32_t arg) {
for (uint8_t i = 0; ((status_ = spiRec()) & 0x80) && i != 0xFF; i++) { /* Intentionally left empty */ }
return status_;
}
//------------------------------------------------------------------------------
/**
* Determine the size of an SD flash memory card.
*
@ -217,19 +218,20 @@ uint32_t Sd2Card::cardSize() {
return 0;
}
}
//------------------------------------------------------------------------------
void Sd2Card::chipSelectHigh() {
digitalWrite(chipSelectPin_, HIGH);
}
//------------------------------------------------------------------------------
void Sd2Card::chipSelectLow() {
#if DISABLED(SOFTWARE_SPI)
spiInit(spiRate_);
#endif // SOFTWARE_SPI
digitalWrite(chipSelectPin_, LOW);
}
//------------------------------------------------------------------------------
/** Erase a range of blocks.
/**
* Erase a range of blocks.
*
* \param[in] firstBlock The address of the first block in the range.
* \param[in] lastBlock The address of the last block in the range.
@ -239,8 +241,7 @@ void Sd2Card::chipSelectLow() {
* either 0 or 1, depends on the card vendor. The card must support
* single block erase.
*
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* \return true for success, false for failure.
*/
bool Sd2Card::erase(uint32_t firstBlock, uint32_t lastBlock) {
csd_t csd;
@ -275,26 +276,26 @@ bool Sd2Card::erase(uint32_t firstBlock, uint32_t lastBlock) {
chipSelectHigh();
return false;
}
//------------------------------------------------------------------------------
/** Determine if card supports single block erase.
/**
* Determine if card supports single block erase.
*
* \return The value one, true, is returned if single block erase is supported.
* The value zero, false, is returned if single block erase is not supported.
* \return true if single block erase is supported.
* false if single block erase is not supported.
*/
bool Sd2Card::eraseSingleBlockEnable() {
csd_t csd;
return readCSD(&csd) ? csd.v1.erase_blk_en : false;
}
//------------------------------------------------------------------------------
/**
* Initialize an SD flash memory card.
*
* \param[in] sckRateID SPI clock rate selector. See setSckRate().
* \param[in] chipSelectPin SD chip select pin number.
*
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure. The reason for failure
* can be determined by calling errorCode() and errorData().
* \return true for success, false for failure.
* The reason for failure can be determined by calling errorCode() and errorData().
*/
bool Sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin) {
errorCode_ = type_ = 0;
@ -384,14 +385,13 @@ bool Sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin) {
chipSelectHigh();
return false;
}
//------------------------------------------------------------------------------
/**
* Read a 512 byte block from an SD card.
*
* \param[in] blockNumber Logical block to be read.
* \param[out] dst Pointer to the location that will receive the data.
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* \return true for success, false for failure.
*/
bool Sd2Card::readBlock(uint32_t blockNumber, uint8_t* dst) {
// use address if not SDHC card
@ -399,19 +399,18 @@ bool Sd2Card::readBlock(uint32_t blockNumber, uint8_t* dst) {
#if ENABLED(SD_CHECK_AND_RETRY)
uint8_t retryCnt = 3;
do {
if (!cardCommand(CMD17, blockNumber)) {
if (readData(dst, 512)) return true;
}
else
for(;;) {
if (cardCommand(CMD17, blockNumber))
error(SD_CARD_ERROR_CMD17);
else if (readData(dst, 512))
return true;
if (!--retryCnt) break;
chipSelectHigh();
cardCommand(CMD12, 0); // Try sending a stop command, ignore the result.
errorCode_ = 0;
} while (true);
}
#else
if (cardCommand(CMD17, blockNumber))
error(SD_CARD_ERROR_CMD17);
@ -422,13 +421,13 @@ bool Sd2Card::readBlock(uint32_t blockNumber, uint8_t* dst) {
chipSelectHigh();
return false;
}
//------------------------------------------------------------------------------
/** Read one data block in a multiple block read sequence
/**
* Read one data block in a multiple block read sequence
*
* \param[in] dst Pointer to the location for the data to be read.
*
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* \return true for success, false for failure.
*/
bool Sd2Card::readData(uint8_t* dst) {
chipSelectLow();
@ -436,50 +435,49 @@ bool Sd2Card::readData(uint8_t* dst) {
}
#if ENABLED(SD_CHECK_AND_RETRY)
static const uint16_t crctab[] PROGMEM = {
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
};
static uint16_t CRC_CCITT(const uint8_t* data, size_t n) {
uint16_t crc = 0;
for (size_t i = 0; i < n; i++) {
crc = pgm_read_word(&crctab[(crc >> 8 ^ data[i]) & 0xFF]) ^ (crc << 8);
}
return crc;
}
#endif
static const uint16_t crctab[] PROGMEM = {
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
};
static uint16_t CRC_CCITT(const uint8_t* data, size_t n) {
uint16_t crc = 0;
for (size_t i = 0; i < n; i++) {
crc = pgm_read_word(&crctab[(crc >> 8 ^ data[i]) & 0xFF]) ^ (crc << 8);
}
return crc;
}
#endif // SD_CHECK_AND_RETRY
//------------------------------------------------------------------------------
bool Sd2Card::readData(uint8_t* dst, uint16_t count) {
// wait for start block token
uint16_t t0 = millis();
@ -521,61 +519,55 @@ bool Sd2Card::readData(uint8_t* dst, uint16_t count) {
spiSend(0XFF);
return false;
}
//------------------------------------------------------------------------------
/** read CID or CSR register */
bool Sd2Card::readRegister(uint8_t cmd, void* buf) {
uint8_t* dst = reinterpret_cast<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.
*
@ -596,25 +588,22 @@ bool Sd2Card::setSckRate(uint8_t sckRateID) {
spiRate_ = sckRateID;
return true;
}
//------------------------------------------------------------------------------
// wait for card to go not busy
bool Sd2Card::waitNotBusy(uint16_t timeoutMillis) {
uint16_t t0 = millis();
while (spiRec() != 0XFF) {
if (((uint16_t)millis() - t0) >= timeoutMillis) goto FAIL;
}
while (spiRec() != 0XFF)
if (((uint16_t)millis() - t0) >= timeoutMillis) return false;
return true;
FAIL:
return false;
}
//------------------------------------------------------------------------------
/**
* Writes a 512 byte block to an SD card.
*
* \param[in] blockNumber Logical block to be written.
* \param[in] src Pointer to the location of the data to be written.
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* \return true for success, false for failure.
*/
bool Sd2Card::writeBlock(uint32_t blockNumber, const uint8_t* src) {
// use address if not SDHC card
@ -641,25 +630,24 @@ bool Sd2Card::writeBlock(uint32_t blockNumber, const uint8_t* src) {
chipSelectHigh();
return false;
}
//------------------------------------------------------------------------------
/** Write one data block in a multiple block write sequence
/**
* Write one data block in a multiple block write sequence
* \param[in] src Pointer to the location of the data to be written.
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* \return true for success, false for failure.
*/
bool Sd2Card::writeData(const uint8_t* src) {
chipSelectLow();
// wait for previous write to finish
if (!waitNotBusy(SD_WRITE_TIMEOUT)) goto FAIL;
if (!writeData(WRITE_MULTIPLE_TOKEN, src)) goto FAIL;
if (!waitNotBusy(SD_WRITE_TIMEOUT) || !writeData(WRITE_MULTIPLE_TOKEN, src)) {
error(SD_CARD_ERROR_WRITE_MULTIPLE);
chipSelectHigh();
return false;
}
chipSelectHigh();
return true;
FAIL:
error(SD_CARD_ERROR_WRITE_MULTIPLE);
chipSelectHigh();
return false;
}
//------------------------------------------------------------------------------
// send one block of data for write block or write multiple blocks
bool Sd2Card::writeData(uint8_t token, const uint8_t* src) {
spiSendBlock(token, src);
@ -670,15 +658,14 @@ bool Sd2Card::writeData(uint8_t token, const uint8_t* src) {
status_ = spiRec();
if ((status_ & DATA_RES_MASK) != DATA_RES_ACCEPTED) {
error(SD_CARD_ERROR_WRITE);
goto FAIL;
chipSelectHigh();
return false;
}
return true;
FAIL:
chipSelectHigh();
return false;
}
//------------------------------------------------------------------------------
/** Start a write multiple blocks sequence.
/**
* Start a write multiple blocks sequence.
*
* \param[in] blockNumber Address of first block in sequence.
* \param[in] eraseCount The number of blocks to be pre-erased.
@ -686,8 +673,7 @@ bool Sd2Card::writeData(uint8_t token, const uint8_t* src) {
* \note This function is used with writeData() and writeStop()
* for optimized multiple block writes.
*
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* \return true for success, false for failure.
*/
bool Sd2Card::writeStart(uint32_t blockNumber, uint32_t eraseCount) {
// send pre-erase count
@ -707,11 +693,11 @@ bool Sd2Card::writeStart(uint32_t blockNumber, uint32_t eraseCount) {
chipSelectHigh();
return false;
}
//------------------------------------------------------------------------------
/** End a write multiple blocks sequence.
/**
* End a write multiple blocks sequence.
*
* \return The value one, true, is returned for success and
* the value zero, false, is returned for failure.
* \return true for success, false for failure.
*/
bool Sd2Card::writeStop() {
chipSelectLow();
@ -726,4 +712,4 @@ bool Sd2Card::writeStop() {
return false;
}
#endif
#endif // SDSUPPORT

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

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

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

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

@ -20,35 +20,31 @@
*
*/
/**
* \file
* \brief FAT file structures
*/
/**
* Arduino SdFat Library
* Copyright (C) 2009 by William Greiman
*
* This file is part of the Arduino Sd2Card Library
*/
#include "Marlin.h"
#if ENABLED(SDSUPPORT)
#ifndef SdFatStructs_h
#define SdFatStructs_h
#ifndef SDFATSTRUCTS_H
#define SDFATSTRUCTS_H
#define PACKED __attribute__((__packed__))
/**
* \file
* \brief FAT file structures
*/
/**
* mostly from Microsoft document fatgen103.doc
* http://www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx
*/
//------------------------------------------------------------------------------
/** Value for byte 510 of boot block or MBR */
uint8_t const BOOTSIG0 = 0x55;
/** Value for byte 511 of boot block or MBR */
uint8_t const BOOTSIG1 = 0xAA;
/** Value for bootSignature field int FAT/FAT32 boot sector */
uint8_t const EXTENDED_BOOT_SIG = 0x29;
//------------------------------------------------------------------------------
uint8_t const BOOTSIG0 = 0x55, // Value for byte 510 of boot block or MBR
BOOTSIG1 = 0xAA, // Value for byte 511 of boot block or MBR
EXTENDED_BOOT_SIG = 0x29; // Value for bootSignature field int FAT/FAT32 boot sector
/**
* \struct partitionTable
* \brief MBR partition table entry
@ -57,59 +53,58 @@ uint8_t const EXTENDED_BOOT_SIG = 0x29;
* The MBR partition table has four entries.
*/
struct partitionTable {
/**
* Boot Indicator . Indicates whether the volume is the active
* partition. Legal values include: 0x00. Do not use for booting.
* 0x80 Active partition.
*/
/**
* Boot Indicator . Indicates whether the volume is the active
* partition. Legal values include: 0x00. Do not use for booting.
* 0x80 Active partition.
*/
uint8_t boot;
/**
* Head part of Cylinder-head-sector address of the first block in
* the partition. Legal values are 0-255. Only used in old PC BIOS.
*/
/**
* Head part of Cylinder-head-sector address of the first block in
* the partition. Legal values are 0-255. Only used in old PC BIOS.
*/
uint8_t beginHead;
/**
* Sector part of Cylinder-head-sector address of the first block in
* the partition. Legal values are 1-63. Only used in old PC BIOS.
*/
/**
* Sector part of Cylinder-head-sector address of the first block in
* the partition. Legal values are 1-63. Only used in old PC BIOS.
*/
unsigned beginSector : 6;
/** High bits cylinder for first block in partition. */
/** High bits cylinder for first block in partition. */
unsigned beginCylinderHigh : 2;
/**
* Combine beginCylinderLow with beginCylinderHigh. Legal values
* are 0-1023. Only used in old PC BIOS.
*/
/**
* Combine beginCylinderLow with beginCylinderHigh. Legal values
* are 0-1023. Only used in old PC BIOS.
*/
uint8_t beginCylinderLow;
/**
* Partition type. See defines that begin with PART_TYPE_ for
* some Microsoft partition types.
*/
/**
* Partition type. See defines that begin with PART_TYPE_ for
* some Microsoft partition types.
*/
uint8_t type;
/**
* head part of cylinder-head-sector address of the last sector in the
* partition. Legal values are 0-255. Only used in old PC BIOS.
*/
/**
* head part of cylinder-head-sector address of the last sector in the
* partition. Legal values are 0-255. Only used in old PC BIOS.
*/
uint8_t endHead;
/**
* Sector part of cylinder-head-sector address of the last sector in
* the partition. Legal values are 1-63. Only used in old PC BIOS.
*/
/**
* Sector part of cylinder-head-sector address of the last sector in
* the partition. Legal values are 1-63. Only used in old PC BIOS.
*/
unsigned endSector : 6;
/** High bits of end cylinder */
/** High bits of end cylinder */
unsigned endCylinderHigh : 2;
/**
* Combine endCylinderLow with endCylinderHigh. Legal values
* are 0-1023. Only used in old PC BIOS.
*/
/**
* Combine endCylinderLow with endCylinderHigh. Legal values
* are 0-1023. Only used in old PC BIOS.
*/
uint8_t endCylinderLow;
/** Logical block address of the first block in the partition. */
uint32_t firstSector;
/** Length of the partition, in blocks. */
uint32_t totalSectors;
uint32_t firstSector; // Logical block address of the first block in the partition.
uint32_t totalSectors; // Length of the partition, in blocks.
} PACKED;
/** Type name for partitionTable */
typedef struct partitionTable part_t;
//------------------------------------------------------------------------------
typedef struct partitionTable part_t; // Type name for partitionTable
/**
* \struct masterBootRecord
*
@ -118,22 +113,16 @@ typedef struct partitionTable part_t;
* The first block of a storage device that is formatted with a MBR.
*/
struct masterBootRecord {
/** Code Area for master boot program. */
uint8_t codeArea[440];
/** Optional Windows NT disk signature. May contain boot code. */
uint32_t diskSignature;
/** Usually zero but may be more boot code. */
uint16_t usuallyZero;
/** Partition tables. */
part_t part[4];
/** First MBR signature byte. Must be 0x55 */
uint8_t mbrSig0;
/** Second MBR signature byte. Must be 0xAA */
uint8_t mbrSig1;
uint8_t codeArea[440]; // Code Area for master boot program.
uint32_t diskSignature; // Optional Windows NT disk signature. May contain boot code.
uint16_t usuallyZero; // Usually zero but may be more boot code.
part_t part[4]; // Partition tables.
uint8_t mbrSig0; // First MBR signature byte. Must be 0x55
uint8_t mbrSig1; // Second MBR signature byte. Must be 0xAA
} PACKED;
/** Type name for masterBootRecord */
typedef struct masterBootRecord mbr_t;
//------------------------------------------------------------------------------
/**
* \struct fat_boot
*
@ -141,285 +130,280 @@ typedef struct masterBootRecord mbr_t;
*
*/
struct fat_boot {
/**
* The first three bytes of the boot sector must be valid,
* executable x 86-based CPU instructions. This includes a
* jump instruction that skips the next nonexecutable bytes.
*/
/**
* The first three bytes of the boot sector must be valid,
* executable x 86-based CPU instructions. This includes a
* jump instruction that skips the next nonexecutable bytes.
*/
uint8_t jump[3];
/**
* This is typically a string of characters that identifies
* the operating system that formatted the volume.
*/
/**
* This is typically a string of characters that identifies
* the operating system that formatted the volume.
*/
char oemId[8];
/**
* The size of a hardware sector. Valid decimal values for this
* field are 512, 1024, 2048, and 4096. For most disks used in
* the United States, the value of this field is 512.
*/
/**
* The size of a hardware sector. Valid decimal values for this
* field are 512, 1024, 2048, and 4096. For most disks used in
* the United States, the value of this field is 512.
*/
uint16_t bytesPerSector;
/**
* Number of sectors per allocation unit. This value must be a
* power of 2 that is greater than 0. The legal values are
* 1, 2, 4, 8, 16, 32, 64, and 128. 128 should be avoided.
*/
/**
* Number of sectors per allocation unit. This value must be a
* power of 2 that is greater than 0. The legal values are
* 1, 2, 4, 8, 16, 32, 64, and 128. 128 should be avoided.
*/
uint8_t sectorsPerCluster;
/**
* The number of sectors preceding the start of the first FAT,
* including the boot sector. The value of this field is always 1.
*/
/**
* The number of sectors preceding the start of the first FAT,
* including the boot sector. The value of this field is always 1.
*/
uint16_t reservedSectorCount;
/**
* The number of copies of the FAT on the volume.
* The value of this field is always 2.
*/
/**
* The number of copies of the FAT on the volume.
* The value of this field is always 2.
*/
uint8_t fatCount;
/**
* For FAT12 and FAT16 volumes, this field contains the count of
* 32-byte directory entries in the root directory. For FAT32 volumes,
* this field must be set to 0. For FAT12 and FAT16 volumes, this
* value should always specify a count that when multiplied by 32
* results in a multiple of bytesPerSector. FAT16 volumes should
* use the value 512.
*/
/**
* For FAT12 and FAT16 volumes, this field contains the count of
* 32-byte directory entries in the root directory. For FAT32 volumes,
* this field must be set to 0. For FAT12 and FAT16 volumes, this
* value should always specify a count that when multiplied by 32
* results in a multiple of bytesPerSector. FAT16 volumes should
* use the value 512.
*/
uint16_t rootDirEntryCount;
/**
* This field is the old 16-bit total count of sectors on the volume.
* This count includes the count of all sectors in all four regions
* of the volume. This field can be 0; if it is 0, then totalSectors32
* must be nonzero. For FAT32 volumes, this field must be 0. For
* FAT12 and FAT16 volumes, this field contains the sector count, and
* totalSectors32 is 0 if the total sector count fits
* (is less than 0x10000).
*/
/**
* This field is the old 16-bit total count of sectors on the volume.
* This count includes the count of all sectors in all four regions
* of the volume. This field can be 0; if it is 0, then totalSectors32
* must be nonzero. For FAT32 volumes, this field must be 0. For
* FAT12 and FAT16 volumes, this field contains the sector count, and
* totalSectors32 is 0 if the total sector count fits
* (is less than 0x10000).
*/
uint16_t totalSectors16;
/**
* This dates back to the old MS-DOS 1.x media determination and is
* no longer usually used for anything. 0xF8 is the standard value
* for fixed (nonremovable) media. For removable media, 0xF0 is
* frequently used. Legal values are 0xF0 or 0xF8-0xFF.
*/
/**
* This dates back to the old MS-DOS 1.x media determination and is
* no longer usually used for anything. 0xF8 is the standard value
* for fixed (nonremovable) media. For removable media, 0xF0 is
* frequently used. Legal values are 0xF0 or 0xF8-0xFF.
*/
uint8_t mediaType;
/**
* Count of sectors occupied by one FAT on FAT12/FAT16 volumes.
* On FAT32 volumes this field must be 0, and sectorsPerFat32
* contains the FAT size count.
*/
/**
* Count of sectors occupied by one FAT on FAT12/FAT16 volumes.
* On FAT32 volumes this field must be 0, and sectorsPerFat32
* contains the FAT size count.
*/
uint16_t sectorsPerFat16;
/** Sectors per track for interrupt 0x13. Not used otherwise. */
uint16_t sectorsPerTrack;
/** Number of heads for interrupt 0x13. Not used otherwise. */
uint16_t headCount;
/**
* Count of hidden sectors preceding the partition that contains this
* FAT volume. This field is generally only relevant for media
* visible on interrupt 0x13.
*/
uint16_t sectorsPerTrack; // Sectors per track for interrupt 0x13. Not used otherwise.
uint16_t headCount; // Number of heads for interrupt 0x13. Not used otherwise.
/**
* Count of hidden sectors preceding the partition that contains this
* FAT volume. This field is generally only relevant for media
* visible on interrupt 0x13.
*/
uint32_t hidddenSectors;
/**
* This field is the new 32-bit total count of sectors on the volume.
* This count includes the count of all sectors in all four regions
* of the volume. This field can be 0; if it is 0, then
* totalSectors16 must be nonzero.
*/
/**
* This field is the new 32-bit total count of sectors on the volume.
* This count includes the count of all sectors in all four regions
* of the volume. This field can be 0; if it is 0, then
* totalSectors16 must be nonzero.
*/
uint32_t totalSectors32;
/**
* Related to the BIOS physical drive number. Floppy drives are
* identified as 0x00 and physical hard disks are identified as
* 0x80, regardless of the number of physical disk drives.
* Typically, this value is set prior to issuing an INT 13h BIOS
* call to specify the device to access. The value is only
* relevant if the device is a boot device.
*/
/**
* Related to the BIOS physical drive number. Floppy drives are
* identified as 0x00 and physical hard disks are identified as
* 0x80, regardless of the number of physical disk drives.
* Typically, this value is set prior to issuing an INT 13h BIOS
* call to specify the device to access. The value is only
* relevant if the device is a boot device.
*/
uint8_t driveNumber;
/** used by Windows NT - should be zero for FAT */
uint8_t reserved1;
/** 0x29 if next three fields are valid */
uint8_t bootSignature;
/**
* A random serial number created when formatting a disk,
* which helps to distinguish between disks.
* Usually generated by combining date and time.
*/
uint8_t reserved1; // used by Windows NT - should be zero for FAT
uint8_t bootSignature; // 0x29 if next three fields are valid
/**
* A random serial number created when formatting a disk,
* which helps to distinguish between disks.
* Usually generated by combining date and time.
*/
uint32_t volumeSerialNumber;
/**
* A field once used to store the volume label. The volume label
* is now stored as a special file in the root directory.
*/
/**
* A field once used to store the volume label. The volume label
* is now stored as a special file in the root directory.
*/
char volumeLabel[11];
/**
* A field with a value of either FAT, FAT12 or FAT16,
* depending on the disk format.
*/
/**
* A field with a value of either FAT, FAT12 or FAT16,
* depending on the disk format.
*/
char fileSystemType[8];
/** X86 boot code */
uint8_t bootCode[448];
/** must be 0x55 */
uint8_t bootSectorSig0;
/** must be 0xAA */
uint8_t bootSectorSig1;
uint8_t bootCode[448]; // X86 boot code
uint8_t bootSectorSig0; // must be 0x55
uint8_t bootSectorSig1; // must be 0xAA
} PACKED;
/** Type name for FAT Boot Sector */
typedef struct fat_boot fat_boot_t;
//------------------------------------------------------------------------------
typedef struct fat_boot fat_boot_t; // Type name for FAT Boot Sector
/**
* \struct fat32_boot
*
* \brief Boot sector for a FAT32 volume.
*
*/
struct fat32_boot {
/**
* The first three bytes of the boot sector must be valid,
* executable x 86-based CPU instructions. This includes a
* jump instruction that skips the next nonexecutable bytes.
*/
/**
* The first three bytes of the boot sector must be valid,
* executable x 86-based CPU instructions. This includes a
* jump instruction that skips the next nonexecutable bytes.
*/
uint8_t jump[3];
/**
* This is typically a string of characters that identifies
* the operating system that formatted the volume.
*/
/**
* This is typically a string of characters that identifies
* the operating system that formatted the volume.
*/
char oemId[8];
/**
* The size of a hardware sector. Valid decimal values for this
* field are 512, 1024, 2048, and 4096. For most disks used in
* the United States, the value of this field is 512.
*/
/**
* The size of a hardware sector. Valid decimal values for this
* field are 512, 1024, 2048, and 4096. For most disks used in
* the United States, the value of this field is 512.
*/
uint16_t bytesPerSector;
/**
* Number of sectors per allocation unit. This value must be a
* power of 2 that is greater than 0. The legal values are
* 1, 2, 4, 8, 16, 32, 64, and 128. 128 should be avoided.
*/
/**
* Number of sectors per allocation unit. This value must be a
* power of 2 that is greater than 0. The legal values are
* 1, 2, 4, 8, 16, 32, 64, and 128. 128 should be avoided.
*/
uint8_t sectorsPerCluster;
/**
* The number of sectors preceding the start of the first FAT,
* including the boot sector. Must not be zero
*/
/**
* The number of sectors preceding the start of the first FAT,
* including the boot sector. Must not be zero
*/
uint16_t reservedSectorCount;
/**
* The number of copies of the FAT on the volume.
* The value of this field is always 2.
*/
/**
* The number of copies of the FAT on the volume.
* The value of this field is always 2.
*/
uint8_t fatCount;
/**
* FAT12/FAT16 only. For FAT32 volumes, this field must be set to 0.
*/
/**
* FAT12/FAT16 only. For FAT32 volumes, this field must be set to 0.
*/
uint16_t rootDirEntryCount;
/**
* For FAT32 volumes, this field must be 0.
*/
/**
* For FAT32 volumes, this field must be 0.
*/
uint16_t totalSectors16;
/**
* This dates back to the old MS-DOS 1.x media determination and is
* no longer usually used for anything. 0xF8 is the standard value
* for fixed (nonremovable) media. For removable media, 0xF0 is
* frequently used. Legal values are 0xF0 or 0xF8-0xFF.
*/
/**
* This dates back to the old MS-DOS 1.x media determination and is
* no longer usually used for anything. 0xF8 is the standard value
* for fixed (nonremovable) media. For removable media, 0xF0 is
* frequently used. Legal values are 0xF0 or 0xF8-0xFF.
*/
uint8_t mediaType;
/**
* On FAT32 volumes this field must be 0, and sectorsPerFat32
* contains the FAT size count.
*/
/**
* On FAT32 volumes this field must be 0, and sectorsPerFat32
* contains the FAT size count.
*/
uint16_t sectorsPerFat16;
/** Sectors per track for interrupt 0x13. Not used otherwise. */
uint16_t sectorsPerTrack;
/** Number of heads for interrupt 0x13. Not used otherwise. */
uint16_t headCount;
/**
* Count of hidden sectors preceding the partition that contains this
* FAT volume. This field is generally only relevant for media
* visible on interrupt 0x13.
*/
uint16_t sectorsPerTrack; // Sectors per track for interrupt 0x13. Not used otherwise.
uint16_t headCount; // Number of heads for interrupt 0x13. Not used otherwise.
/**
* Count of hidden sectors preceding the partition that contains this
* FAT volume. This field is generally only relevant for media
* visible on interrupt 0x13.
*/
uint32_t hidddenSectors;
/**
* Contains the total number of sectors in the FAT32 volume.
*/
/**
* Contains the total number of sectors in the FAT32 volume.
*/
uint32_t totalSectors32;
/**
* Count of sectors occupied by one FAT on FAT32 volumes.
*/
/**
* Count of sectors occupied by one FAT on FAT32 volumes.
*/
uint32_t sectorsPerFat32;
/**
* This field is only defined for FAT32 media and does not exist on
* FAT12 and FAT16 media.
* Bits 0-3 -- Zero-based number of active FAT.
* Only valid if mirroring is disabled.
* Bits 4-6 -- Reserved.
* Bit 7 -- 0 means the FAT is mirrored at runtime into all FATs.
* -- 1 means only one FAT is active; it is the one referenced
* in bits 0-3.
* Bits 8-15 -- Reserved.
*/
/**
* This field is only defined for FAT32 media and does not exist on
* FAT12 and FAT16 media.
* Bits 0-3 -- Zero-based number of active FAT.
* Only valid if mirroring is disabled.
* Bits 4-6 -- Reserved.
* Bit 7 -- 0 means the FAT is mirrored at runtime into all FATs.
* -- 1 means only one FAT is active; it is the one referenced
* in bits 0-3.
* Bits 8-15 -- Reserved.
*/
uint16_t fat32Flags;
/**
* FAT32 version. High byte is major revision number.
* Low byte is minor revision number. Only 0.0 define.
*/
/**
* FAT32 version. High byte is major revision number.
* Low byte is minor revision number. Only 0.0 define.
*/
uint16_t fat32Version;
/**
* Cluster number of the first cluster of the root directory for FAT32.
* This usually 2 but not required to be 2.
*/
/**
* Cluster number of the first cluster of the root directory for FAT32.
* This usually 2 but not required to be 2.
*/
uint32_t fat32RootCluster;
/**
* Sector number of FSINFO structure in the reserved area of the
* FAT32 volume. Usually 1.
*/
/**
* Sector number of FSINFO structure in the reserved area of the
* FAT32 volume. Usually 1.
*/
uint16_t fat32FSInfo;
/**
* If nonzero, indicates the sector number in the reserved area
* of the volume of a copy of the boot record. Usually 6.
* No value other than 6 is recommended.
*/
/**
* If nonzero, indicates the sector number in the reserved area
* of the volume of a copy of the boot record. Usually 6.
* No value other than 6 is recommended.
*/
uint16_t fat32BackBootBlock;
/**
* Reserved for future expansion. Code that formats FAT32 volumes
* should always set all of the bytes of this field to 0.
*/
/**
* Reserved for future expansion. Code that formats FAT32 volumes
* should always set all of the bytes of this field to 0.
*/
uint8_t fat32Reserved[12];
/**
* Related to the BIOS physical drive number. Floppy drives are
* identified as 0x00 and physical hard disks are identified as
* 0x80, regardless of the number of physical disk drives.
* Typically, this value is set prior to issuing an INT 13h BIOS
* call to specify the device to access. The value is only
* relevant if the device is a boot device.
*/
/**
* Related to the BIOS physical drive number. Floppy drives are
* identified as 0x00 and physical hard disks are identified as
* 0x80, regardless of the number of physical disk drives.
* Typically, this value is set prior to issuing an INT 13h BIOS
* call to specify the device to access. The value is only
* relevant if the device is a boot device.
*/
uint8_t driveNumber;
/** used by Windows NT - should be zero for FAT */
uint8_t reserved1;
/** 0x29 if next three fields are valid */
uint8_t bootSignature;
/**
* A random serial number created when formatting a disk,
* which helps to distinguish between disks.
* Usually generated by combining date and time.
*/
uint8_t reserved1; // Used by Windows NT - should be zero for FAT
uint8_t bootSignature; // 0x29 if next three fields are valid
/**
* A random serial number created when formatting a disk,
* which helps to distinguish between disks.
* Usually generated by combining date and time.
*/
uint32_t volumeSerialNumber;
/**
* A field once used to store the volume label. The volume label
* is now stored as a special file in the root directory.
*/
/**
* A field once used to store the volume label. The volume label
* is now stored as a special file in the root directory.
*/
char volumeLabel[11];
/**
* A text field with a value of FAT32.
*/
/**
* A text field with a value of FAT32.
*/
char fileSystemType[8];
/** X86 boot code */
uint8_t bootCode[420];
/** must be 0x55 */
uint8_t bootSectorSig0;
/** must be 0xAA */
uint8_t bootSectorSig1;
uint8_t bootCode[420]; // X86 boot code
uint8_t bootSectorSig0; // must be 0x55
uint8_t bootSectorSig1; // must be 0xAA
} PACKED;
/** Type name for FAT32 Boot Sector */
typedef struct fat32_boot fat32_boot_t;
//------------------------------------------------------------------------------
/** Lead signature for a FSINFO sector */
uint32_t const FSINFO_LEAD_SIG = 0x41615252;
/** Struct signature for a FSINFO sector */
uint32_t const FSINFO_STRUCT_SIG = 0x61417272;
typedef struct fat32_boot fat32_boot_t; // Type name for FAT32 Boot Sector
uint32_t const FSINFO_LEAD_SIG = 0x41615252, // 'AaRR' Lead signature for a FSINFO sector
FSINFO_STRUCT_SIG = 0x61417272; // 'aArr' Struct signature for a FSINFO sector
/**
* \struct fat32_fsinfo
*
@ -427,12 +411,9 @@ uint32_t const FSINFO_STRUCT_SIG = 0x61417272;
*
*/
struct fat32_fsinfo {
/** must be 0x52, 0x52, 0x61, 0x41 */
uint32_t leadSignature;
/** must be zero */
uint8_t reserved1[480];
/** must be 0x72, 0x72, 0x41, 0x61 */
uint32_t structSignature;
uint32_t leadSignature; // must be 0x52, 0x52, 0x61, 0x41 'RRaA'
uint8_t reserved1[480]; // must be zero
uint32_t structSignature; // must be 0x72, 0x72, 0x41, 0x61 'rrAa'
/**
* Contains the last known free cluster count on the volume.
* If the value is 0xFFFFFFFF, then the free count is unknown
@ -448,30 +429,22 @@ struct fat32_fsinfo {
* should start looking at cluster 2.
*/
uint32_t nextFree;
/** must be zero */
uint8_t reserved2[12];
/** must be 0x00, 0x00, 0x55, 0xAA */
uint8_t tailSignature[4];
uint8_t reserved2[12]; // must be zero
uint8_t tailSignature[4]; // must be 0x00, 0x00, 0x55, 0xAA
} PACKED;
/** Type name for FAT32 FSINFO Sector */
typedef struct fat32_fsinfo fat32_fsinfo_t;
//------------------------------------------------------------------------------
typedef struct fat32_fsinfo fat32_fsinfo_t; // Type name for FAT32 FSINFO Sector
// End Of Chain values for FAT entries
/** FAT12 end of chain value used by Microsoft. */
uint16_t const FAT12EOC = 0xFFF;
/** Minimum value for FAT12 EOC. Use to test for EOC. */
uint16_t const FAT12EOC_MIN = 0xFF8;
/** FAT16 end of chain value used by Microsoft. */
uint16_t const FAT16EOC = 0xFFFF;
/** Minimum value for FAT16 EOC. Use to test for EOC. */
uint16_t const FAT16EOC_MIN = 0xFFF8;
/** FAT32 end of chain value used by Microsoft. */
uint32_t const FAT32EOC = 0x0FFFFFFF;
/** Minimum value for FAT32 EOC. Use to test for EOC. */
uint32_t const FAT32EOC_MIN = 0x0FFFFFF8;
/** Mask a for FAT32 entry. Entries are 28 bits. */
uint32_t const FAT32MASK = 0x0FFFFFFF;
//------------------------------------------------------------------------------
uint16_t const FAT12EOC = 0xFFF, // FAT12 end of chain value used by Microsoft.
FAT12EOC_MIN = 0xFF8, // Minimum value for FAT12 EOC. Use to test for EOC.
FAT16EOC = 0xFFFF, // FAT16 end of chain value used by Microsoft.
FAT16EOC_MIN = 0xFFF8; // Minimum value for FAT16 EOC. Use to test for EOC.
uint32_t const FAT32EOC = 0x0FFFFFFF, // FAT32 end of chain value used by Microsoft.
FAT32EOC_MIN = 0x0FFFFFF8, // Minimum value for FAT32 EOC. Use to test for EOC.
FAT32MASK = 0x0FFFFFFF; // Mask a for FAT32 entry. Entries are 28 bits.
/**
* \struct directoryEntry
* \brief FAT short directory entry
@ -503,54 +476,54 @@ uint32_t const FAT32MASK = 0x0FFFFFFF;
* The valid time range is from Midnight 00:00:00 to 23:59:58.
*/
struct directoryEntry {
/** Short 8.3 name.
*
* The first eight bytes contain the file name with blank fill.
* The last three bytes contain the file extension with blank fill.
*/
/**
* Short 8.3 name.
*
* The first eight bytes contain the file name with blank fill.
* The last three bytes contain the file extension with blank fill.
*/
uint8_t name[11];
/** Entry attributes.
*
* The upper two bits of the attribute byte are reserved and should
* always be set to 0 when a file is created and never modified or
* looked at after that. See defines that begin with DIR_ATT_.
*/
/**
* Entry attributes.
*
* The upper two bits of the attribute byte are reserved and should
* always be set to 0 when a file is created and never modified or
* looked at after that. See defines that begin with DIR_ATT_.
*/
uint8_t attributes;
/**
* Reserved for use by Windows NT. Set value to 0 when a file is
* created and never modify or look at it after that.
*/
/**
* Reserved for use by Windows NT. Set value to 0 when a file is
* created and never modify or look at it after that.
*/
uint8_t reservedNT;
/**
* The granularity of the seconds part of creationTime is 2 seconds
* so this field is a count of tenths of a second and it's valid
* value range is 0-199 inclusive. (WHG note - seems to be hundredths)
*/
/**
* The granularity of the seconds part of creationTime is 2 seconds
* so this field is a count of tenths of a second and it's valid
* value range is 0-199 inclusive. (WHG note - seems to be hundredths)
*/
uint8_t creationTimeTenths;
/** Time file was created. */
uint16_t creationTime;
/** Date file was created. */
uint16_t creationDate;
/**
* Last access date. Note that there is no last access time, only
* a date. This is the date of last read or write. In the case of
* a write, this should be set to the same date as lastWriteDate.
*/
uint16_t creationTime; // Time file was created.
uint16_t creationDate; // Date file was created.
/**
* Last access date. Note that there is no last access time, only
* a date. This is the date of last read or write. In the case of
* a write, this should be set to the same date as lastWriteDate.
*/
uint16_t lastAccessDate;
/**
* High word of this entry's first cluster number (always 0 for a
* FAT12 or FAT16 volume).
*/
/**
* High word of this entry's first cluster number (always 0 for a
* FAT12 or FAT16 volume).
*/
uint16_t firstClusterHigh;
/** Time of last write. File creation is considered a write. */
uint16_t lastWriteTime;
/** Date of last write. File creation is considered a write. */
uint16_t lastWriteDate;
/** Low word of this entry's first cluster number. */
uint16_t firstClusterLow;
/** 32-bit unsigned holding this file's size in bytes. */
uint32_t fileSize;
uint16_t lastWriteTime; // Time of last write. File creation is considered a write.
uint16_t lastWriteDate; // Date of last write. File creation is considered a write.
uint16_t firstClusterLow; // Low word of this entry's first cluster number.
uint32_t fileSize; // 32-bit unsigned holding this file's size in bytes.
} PACKED;
/**
* \struct directoryVFATEntry
* \brief VFAT long filename directory entry
@ -568,54 +541,36 @@ struct directoryVFATEntry {
* bit 0-4: the position of this long filename block (first block is 1)
*/
uint8_t sequenceNumber;
/** First set of UTF-16 characters */
uint16_t name1[5];//UTF-16
/** attributes (at the same location as in directoryEntry), always 0x0F */
uint8_t attributes;
/** Reserved for use by Windows NT. Always 0. */
uint8_t reservedNT;
/** Checksum of the short 8.3 filename, can be used to checked if the file system as modified by a not-long-filename aware implementation. */
uint8_t checksum;
/** Second set of UTF-16 characters */
uint16_t name2[6];//UTF-16
/** firstClusterLow is always zero for longFilenames */
uint16_t firstClusterLow;
/** Third set of UTF-16 characters */
uint16_t name3[2];//UTF-16
uint16_t name1[5]; // First set of UTF-16 characters
uint8_t attributes; // attributes (at the same location as in directoryEntry), always 0x0F
uint8_t reservedNT; // Reserved for use by Windows NT. Always 0.
uint8_t checksum; // Checksum of the short 8.3 filename, can be used to checked if the file system as modified by a not-long-filename aware implementation.
uint16_t name2[6]; // Second set of UTF-16 characters
uint16_t firstClusterLow; // firstClusterLow is always zero for longFilenames
uint16_t name3[2]; // Third set of UTF-16 characters
} PACKED;
//------------------------------------------------------------------------------
// Definitions for directory entries
//
/** Type name for directoryEntry */
typedef struct directoryEntry dir_t;
/** Type name for directoryVFATEntry */
typedef struct directoryVFATEntry vfat_t;
/** escape for name[0] = 0xE5 */
uint8_t const DIR_NAME_0xE5 = 0x05;
/** name[0] value for entry that is free after being "deleted" */
uint8_t const DIR_NAME_DELETED = 0xE5;
/** name[0] value for entry that is free and no allocated entries follow */
uint8_t const DIR_NAME_FREE = 0x00;
/** file is read-only */
uint8_t const DIR_ATT_READ_ONLY = 0x01;
/** File should hidden in directory listings */
uint8_t const DIR_ATT_HIDDEN = 0x02;
/** Entry is for a system file */
uint8_t const DIR_ATT_SYSTEM = 0x04;
/** Directory entry contains the volume label */
uint8_t const DIR_ATT_VOLUME_ID = 0x08;
/** Entry is for a directory */
uint8_t const DIR_ATT_DIRECTORY = 0x10;
/** Old DOS archive bit for backup support */
uint8_t const DIR_ATT_ARCHIVE = 0x20;
/** Test value for long name entry. Test is
(d->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME. */
uint8_t const DIR_ATT_LONG_NAME = 0x0F;
/** Test mask for long name entry */
uint8_t const DIR_ATT_LONG_NAME_MASK = 0x3F;
/** defined attribute bits */
uint8_t const DIR_ATT_DEFINED_BITS = 0x3F;
/** Directory entry is part of a long name
typedef struct directoryEntry dir_t; // Type name for directoryEntry
typedef struct directoryVFATEntry vfat_t; // Type name for directoryVFATEntry
uint8_t const DIR_NAME_0xE5 = 0x05, // escape for name[0] = 0xE5
DIR_NAME_DELETED = 0xE5, // name[0] value for entry that is free after being "deleted"
DIR_NAME_FREE = 0x00, // name[0] value for entry that is free and no allocated entries follow
DIR_ATT_READ_ONLY = 0x01, // file is read-only
DIR_ATT_HIDDEN = 0x02, // File should hidden in directory listings
DIR_ATT_SYSTEM = 0x04, // Entry is for a system file
DIR_ATT_VOLUME_ID = 0x08, // Directory entry contains the volume label
DIR_ATT_DIRECTORY = 0x10, // Entry is for a directory
DIR_ATT_ARCHIVE = 0x20, // Old DOS archive bit for backup support
DIR_ATT_LONG_NAME = 0x0F, // Test value for long name entry. Test is (d->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME.
DIR_ATT_LONG_NAME_MASK = 0x3F, // Test mask for long name entry
DIR_ATT_DEFINED_BITS = 0x3F; // defined attribute bits
/**
* Directory entry is part of a long name
* \param[in] dir Pointer to a directory entry.
*
* \return true if the entry is for part of a long name else false.
@ -623,9 +578,12 @@ uint8_t const DIR_ATT_DEFINED_BITS = 0x3F;
static inline uint8_t DIR_IS_LONG_NAME(const dir_t* dir) {
return (dir->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME;
}
/** Mask for file/subdirectory tests */
uint8_t const DIR_ATT_FILE_TYPE_MASK = (DIR_ATT_VOLUME_ID | DIR_ATT_DIRECTORY);
/** Directory entry is for a file
/**
* Directory entry is for a file
* \param[in] dir Pointer to a directory entry.
*
* \return true if the entry is for a normal file else false.
@ -633,7 +591,9 @@ uint8_t const DIR_ATT_FILE_TYPE_MASK = (DIR_ATT_VOLUME_ID | DIR_ATT_DIRECTORY);
static inline uint8_t DIR_IS_FILE(const dir_t* dir) {
return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == 0;
}
/** Directory entry is for a subdirectory
/**
* Directory entry is for a subdirectory
* \param[in] dir Pointer to a directory entry.
*
* \return true if the entry is for a subdirectory else false.
@ -641,7 +601,9 @@ static inline uint8_t DIR_IS_FILE(const dir_t* dir) {
static inline uint8_t DIR_IS_SUBDIR(const dir_t* dir) {
return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == DIR_ATT_DIRECTORY;
}
/** Directory entry is for a file or subdirectory
/**
* Directory entry is for a file or subdirectory
* \param[in] dir Pointer to a directory entry.
*
* \return true if the entry is for a normal file or subdirectory else false.
@ -649,7 +611,5 @@ static inline uint8_t DIR_IS_SUBDIR(const dir_t* dir) {
static inline uint8_t DIR_IS_FILE_OR_SUBDIR(const dir_t* dir) {
return (dir->attributes & DIR_ATT_VOLUME_ID) == 0;
}
#endif // SdFatStructs_h
#endif
#endif // SDFATSTRUCTS_H

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

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

@ -26,21 +26,24 @@
*
* This file is part of the Arduino Sd2Card Library
*/
#include "Marlin.h"
#include "MarlinConfig.h"
#if ENABLED(SDSUPPORT)
#include "SdFile.h"
/** Create a file object and open it in the current working directory.
/**
* Create a file object and open it in the current working directory.
*
* \param[in] path A path with a valid 8.3 DOS name for a file to be opened.
*
* \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive
* OR of open flags. see SdBaseFile::open(SdBaseFile*, const char*, uint8_t).
*/
SdFile::SdFile(const char* path, uint8_t oflag) : SdBaseFile(path, oflag) {
}
//------------------------------------------------------------------------------
/** Write data to an open file.
SdFile::SdFile(const char* path, uint8_t oflag) : SdBaseFile(path, oflag) { }
/**
* Write data to an open file.
*
* \note Data is moved to the cache but may not be written to the
* storage device until sync() is called.
@ -55,41 +58,37 @@ SdFile::SdFile(const char* path, uint8_t oflag) : SdBaseFile(path, oflag) {
* for a read-only file, device is full, a corrupt file system or an I/O error.
*
*/
int16_t SdFile::write(const void* buf, uint16_t nbyte) {
return SdBaseFile::write(buf, nbyte);
}
//------------------------------------------------------------------------------
/** Write a byte to a file. Required by the Arduino Print class.
int16_t SdFile::write(const void* buf, uint16_t nbyte) { return SdBaseFile::write(buf, nbyte); }
/**
* Write a byte to a file. Required by the Arduino Print class.
* \param[in] b the byte to be written.
* Use writeError to check for errors.
*/
#if ARDUINO >= 100
size_t SdFile::write(uint8_t b) {
return SdBaseFile::write(&b, 1);
}
size_t SdFile::write(uint8_t b) { return SdBaseFile::write(&b, 1); }
#else
void SdFile::write(uint8_t b) {
SdBaseFile::write(&b, 1);
}
void SdFile::write(uint8_t b) { SdBaseFile::write(&b, 1); }
#endif
//------------------------------------------------------------------------------
/** Write a string to a file. Used by the Arduino Print class.
/**
* Write a string to a file. Used by the Arduino Print class.
* \param[in] str Pointer to the string.
* Use writeError to check for errors.
*/
void SdFile::write(const char* str) {
SdBaseFile::write(str, strlen(str));
}
//------------------------------------------------------------------------------
/** Write a PROGMEM string to a file.
void SdFile::write(const char* str) { SdBaseFile::write(str, strlen(str)); }
/**
* Write a PROGMEM string to a file.
* \param[in] str Pointer to the PROGMEM string.
* Use writeError to check for errors.
*/
void SdFile::write_P(PGM_P str) {
for (uint8_t c; (c = pgm_read_byte(str)); str++) write(c);
}
//------------------------------------------------------------------------------
/** Write a PROGMEM string followed by CR/LF to a file.
/**
* Write a PROGMEM string followed by CR/LF to a file.
* \param[in] str Pointer to the PROGMEM string.
* Use writeError to check for errors.
*/
@ -98,5 +97,4 @@ void SdFile::writeln_P(PGM_P str) {
write_P(PSTR("\r\n"));
}
#endif
#endif // SDSUPPORT

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

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

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

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

@ -20,16 +20,16 @@
*
*/
#include "MarlinConfig.h"
#if ENABLED(SDSUPPORT)
#include "cardreader.h"
#include "ultralcd.h"
#include "stepper.h"
#include "language.h"
#include "Marlin.h"
#if ENABLED(SDSUPPORT)
#define LONGEST_FILENAME (longFilename[0] ? longFilename : filename)
CardReader::CardReader() {
@ -44,8 +44,9 @@ CardReader::CardReader() {
sdprinting = cardOK = saving = logging = false;
filesize = 0;
sdpos = 0;
workDirDepth = 0;
file_subcall_ctr = 0;
workDirDepth = 0;
ZERO(workDirParents);
autostart_stilltocheck = true; //the SD start is delayed, because otherwise the serial cannot answer fast enough to make contact with the host software.
@ -255,16 +256,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() {
@ -328,8 +320,8 @@ void CardReader::openFile(char* name, bool read, bool push_current/*=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 (file_subcall_ctr > SD_PROCEDURE_DEPTH - 1) {
SERIAL_ERROR_START();
SERIAL_ERRORPGM("trying to call sub-gcode files with too many levels. MAX level is:");
@ -338,19 +330,18 @@ 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
doing = 2;
@ -360,7 +351,7 @@ void CardReader::openFile(char* name, bool read, bool push_current/*=false*/) {
if (doing) {
SERIAL_ECHO_START();
SERIAL_ECHOPGM("Now ");
SERIAL_ECHO(doing == 1 ? "doing" : "fresh");
serialprintPGM(doing == 1 ? PSTR("doing") : PSTR("fresh"));
SERIAL_ECHOLNPAIR(" file: ", name);
}
@ -380,8 +371,7 @@ void CardReader::openFile(char* name, bool read, bool push_current/*=false*/) {
if (dirname_end != NULL && dirname_end > dirname_start) {
char subdirname[FILENAME_LENGTH];
strncpy(subdirname, dirname_start, dirname_end - dirname_start);
subdirname[dirname_end - dirname_start] = 0;
SERIAL_ECHOLN(subdirname);
subdirname[dirname_end - dirname_start] = '\0';
if (!myDir.open(curDir, subdirname, O_READ)) {
SERIAL_PROTOCOLPGM(MSG_SD_OPEN_FILE_FAIL);
SERIAL_PROTOCOL(subdirname);
@ -403,17 +393,15 @@ void CardReader::openFile(char* name, bool read, bool push_current/*=false*/) {
}
}
}
else { //relative path
curDir = &workDir;
}
else
curDir = &workDir; // Relative paths start in current directory
if (read) {
if (file.open(curDir, fname, O_READ)) {
filesize = file.fileSize();
sdpos = 0;
SERIAL_PROTOCOLPAIR(MSG_SD_FILE_OPENED, fname);
SERIAL_PROTOCOLLNPAIR(MSG_SD_SIZE, filesize);
sdpos = 0;
SERIAL_PROTOCOLLNPGM(MSG_SD_FILE_SELECTED);
getfilename(0, fname);
lcd_setstatus(longFilename[0] ? longFilename : fname);
@ -438,14 +426,14 @@ void CardReader::openFile(char* name, bool read, bool push_current/*=false*/) {
}
}
void CardReader::removeFile(char* name) {
void CardReader::removeFile(const char * const name) {
if (!cardOK) return;
stopSDPrint();
SdFile myDir;
curDir = &root;
char *fname = name;
const char *fname = name;
char *dirname_start, *dirname_end;
if (name[0] == '/') {
@ -460,29 +448,23 @@ void CardReader::removeFile(char* name) {
subdirname[dirname_end - dirname_start] = 0;
SERIAL_ECHOLN(subdirname);
if (!myDir.open(curDir, subdirname, O_READ)) {
SERIAL_PROTOCOLPAIR("open failed, File: ", subdirname);
SERIAL_PROTOCOLPAIR(MSG_SD_OPEN_FILE_FAIL, subdirname);
SERIAL_PROTOCOLCHAR('.');
SERIAL_EOL();
return;
}
else {
//SERIAL_ECHOLNPGM("dive ok");
}
curDir = &myDir;
dirname_start = dirname_end + 1;
}
else { // the remainder after all /fsa/fdsa/ is the filename
else {
fname = dirname_start;
//SERIAL_ECHOLNPGM("remainder");
//SERIAL_ECHOLN(fname);
break;
}
}
}
else { // relative path
else // Relative paths are rooted in the current directory
curDir = &workDir;
}
if (file.remove(curDir, fname)) {
SERIAL_PROTOCOLPGM("File deleted:");
@ -506,14 +488,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;

@ -20,8 +20,8 @@
*
*/
#ifndef CARDREADER_H
#define CARDREADER_H
#ifndef _CARDREADER_H_
#define _CARDREADER_H_
#include "MarlinConfig.h"
@ -30,7 +30,6 @@
#define MAX_DIR_DEPTH 10 // Maximum folder depth
#include "SdFile.h"
#include "types.h"
#include "enum.h"
@ -40,13 +39,15 @@ public:
void initsd();
void write_command(char *buf);
//files auto[0-9].g on the sd card are performed in a row
//this is to delay autostart and hence the initialisaiton of the sd card to some seconds after the normal init, so the device is available quick after a reset
// Files auto[0-9].g on the sd card are performed in sequence.
// This is to delay autostart and hence the initialisation of
// the sd card to some seconds after the normal init, so the
// device is available soon after a reset.
void checkautostart(bool x);
void openFile(char* name, bool read, bool push_current=false);
void 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);
@ -148,8 +149,7 @@ private:
uint8_t file_subcall_ctr;
uint32_t filespos[SD_PROCEDURE_DEPTH];
char proc_filenames[SD_PROCEDURE_DEPTH][MAXPATHNAMELENGTH];
uint32_t filesize;
uint32_t sdpos;
uint32_t filesize, sdpos;
millis_t next_autostart_ms;
bool autostart_stilltocheck; //the sd start is delayed, because otherwise the serial cannot answer fast enought to make contact with the hostsoftware.
@ -164,27 +164,27 @@ private:
#endif
};
extern CardReader card;
#define IS_SD_PRINTING (card.sdprinting)
#define IS_SD_FILE_OPEN (card.isFileOpen())
#if PIN_EXISTS(SD_DETECT)
#if ENABLED(SD_DETECT_INVERTED)
#define IS_SD_INSERTED (READ(SD_DETECT_PIN) != 0)
#define IS_SD_INSERTED (READ(SD_DETECT_PIN) == HIGH)
#else
#define IS_SD_INSERTED (READ(SD_DETECT_PIN) == 0)
#define IS_SD_INSERTED (READ(SD_DETECT_PIN) == LOW)
#endif
#else
//No card detect line? Assume the card is inserted.
// No card detect line? Assume the card is inserted.
#define IS_SD_INSERTED true
#endif
#else
#define IS_SD_PRINTING (false)
#define IS_SD_FILE_OPEN (false)
extern CardReader card;
#endif // SDSUPPORT
#endif // __CARDREADER_H
#if ENABLED(SDSUPPORT)
#define IS_SD_PRINTING (card.sdprinting)
#define IS_SD_FILE_OPEN (card.isFileOpen())
#else
#define IS_SD_PRINTING (false)
#define IS_SD_FILE_OPEN (false)
#endif
#endif // _CARDREADER_H_

Loading…
Cancel
Save