diff --git a/Changelog b/Changelog index d0b4de2ae..92f897a82 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,30 @@ +MCL 4.40 25/02/2023 + +Please install Machinedrum OS X.08 & Monomachine OS X.01 before using MCL 4.40. +Perform the USB DFU firmware procedure to upgrade the USB microcontroller for class compliant USB MIDI. + +- Improvements: + + Increased GUI responsiveness and framerate. + Grid page rendering speed increased from 14FPS -> 21FPS + + Increased SDCard read speed: + SdCard slot read performance increased by 30% + + Automated Slot Loading, and Queueing has been overhauled for + robust, predictable behaviour. + +- Fixes + + Grid Page: Allow length adustment across multiple slots when slot speeds are the + same. + + Program Change Advanced mode did not work over USB MIDI + + Copy/Paste pattern will now copy/paste MD Master FX. + + Bugs related to parameter locks when pasting MD Tracks. + MCL 4.30 12/01/2023 Please install Machinedrum OS X.08 & Monomachine OS X.01 before using MCL 4.30. diff --git a/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.cpp b/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.cpp index 75beda65e..2c2867448 100644 --- a/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.cpp +++ b/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.cpp @@ -43,6 +43,7 @@ SPISettings oledspi = SPISettings(16000000, MSBFIRST, SPI_MODE0); #define _BV(bit) (1 << (bit)) #endif +#include "MCLSd.h" // a 5x7 font table extern const uint8_t PROGMEM font[]; @@ -222,33 +223,13 @@ void Adafruit_SSD1305::fillScreen(uint8_t color) { } } -void Adafruit_SSD1305::begin(uint8_t i2caddr) { - _i2caddr = i2caddr; - - // set pin directions - if (sclk != -1) { - pinMode(sid, OUTPUT); - pinMode(sclk, OUTPUT); -#ifdef __AVR__ - clkport = portOutputRegister(digitalPinToPort(sclk)); - clkpinmask = digitalPinToBitMask(sclk); - mosiport = portOutputRegister(digitalPinToPort(sid)); - mosipinmask = digitalPinToBitMask(sid); -#endif - } else if (cs != -1) { - // hardware SPI +void Adafruit_SSD1305::begin() { + SD.m_card->setDedicatedSpi(false); + // set pin directions + // hardware SPI SPI.begin(); - } else { - // I2C - Wire.begin(); - } - - if (cs != -1) { pinMode(dc, OUTPUT); pinMode(cs, OUTPUT); - } - - if (rst != -1) { pinMode(rst, OUTPUT); digitalWrite(rst, HIGH); @@ -260,8 +241,6 @@ void Adafruit_SSD1305::begin(uint8_t i2caddr) { delay(10); // bring out of reset digitalWrite(rst, HIGH); - } - #if defined SSD1305_128_32 // Init sequence for 128x32 OLED module command(SSD1305_DISPLAYOFF); // 0xAE @@ -336,6 +315,7 @@ void Adafruit_SSD1305::begin(uint8_t i2caddr) { #endif command(SSD1305_DISPLAYON); //--turn on oled panel + SD.m_card->setDedicatedSpi(true); } void Adafruit_SSD1305::invertDisplay(uint8_t i) { @@ -348,72 +328,48 @@ void Adafruit_SSD1305::invertDisplay(uint8_t i) { void Adafruit_SSD1305::command(uint8_t c) { - if (cs != -1) { // SPI of sorts digitalWrite(cs, HIGH); digitalWrite(dc, LOW); delay(1); - if (sclk == -1) { #ifdef SPI_HAS_TRANSACTION SPI.beginTransaction(oledspi); #else SPI.setDataMode(SPI_MODE0); SPI.setClockDivider(ADAFRUIT_SSD1305_SPI); #endif - } digitalWrite(cs, LOW); - spixfer(c); + SPI.transfer(c); digitalWrite(cs, HIGH); #ifdef SPI_HAS_TRANSACTION - if (sclk == -1) SPI.endTransaction(); // release the SPI bus #endif - } else { - // I2C - uint8_t control = 0x00; // Co = 0, D/C = 0 - Wire.beginTransmission(_i2caddr); - Wire.write(control); - Wire.write(c); - Wire.endTransmission(); - } } uint8_t Adafruit_SSD1305::getBuffer(uint16_t i) { return buffer[i]; } uint8_t* Adafruit_SSD1305::getBuffer() { return buffer; } void Adafruit_SSD1305::data(uint8_t c) { - if (cs != -1) { // SPI of sorts digitalWrite(cs, HIGH); digitalWrite(dc, HIGH); - if (sclk == -1) { #ifdef SPI_HAS_TRANSACTION SPI.beginTransaction(oledspi); #else SPI.setDataMode(SPI_MODE0); SPI.setClockDivider(ADAFRUIT_SSD1305_SPI); #endif - } digitalWrite(cs, LOW); - spixfer(c); + SPI.transfer(c); digitalWrite(cs, HIGH); #ifdef SPI_HAS_TRANSACTION - if (sclk == -1) SPI.endTransaction(); // release the SPI bus #endif - } else { - // I2C - uint8_t control = 0x40; // Co = 0, D/C = 1 - Wire.beginTransmission(_i2caddr); - Wire.write(control); - Wire.write(c); - Wire.endTransmission(); - } } void Adafruit_SSD1305::textbox(const char *text, const char *text2, uint16_t delay) { @@ -440,7 +396,8 @@ void Adafruit_SSD1305::display(void) { textbox_enabled = false; } } - + //For dedicated SPI we do this. + SD.m_card->setDedicatedSpi(false); #ifdef ENABLE_DIAG_LOGGING if (diag_page.is_active()) { diag_page.draw(); @@ -454,62 +411,33 @@ void Adafruit_SSD1305::display(void) { screen_saver_active = screen_saver; uint16_t i = 0; - + uint8_t *p = buffer; for (uint8_t page = 0; page < 4; page++) { command(SSD1305_SETPAGESTART + page); command(0x00); command(0x10); - if (cs == -1) { - // save I2C bitrate -#ifdef __AVR__ - uint8_t twbrbackup = TWBR; - TWBR = 12; // upgrade to 400KHz! -#endif - - // Serial.println(TWBR, DEC); - // Serial.println(TWSR & 0x3, DEC); - - // I2C has max 16 bytes per xmision - // send a bunch of data in one xmission - for (uint8_t w = 0; w < 128 / 16; w++) { - Wire.beginTransmission(_i2caddr); - Wire.write(0x40); - for (uint8_t x = 0; x < 16; x++) { - Wire.write(buffer[i++]); - } - Wire.endTransmission(); - } - -#ifdef __AVR__ - TWBR = twbrbackup; -#endif - } else { - // SPI - if (sclk == -1) { + // SPI #ifdef SPI_HAS_TRANSACTION SPI.beginTransaction(oledspi); #else SPI.setDataMode(SPI_MODE0); SPI.setClockDivider(ADAFRUIT_SSD1305_SPI); #endif - } - digitalWrite(cs, HIGH); +// digitalWrite(cs, HIGH); digitalWrite(dc, HIGH); digitalWrite(cs, LOW); - for (uint8_t x = 0; x < 128; x++) { - spixfer(buffer[i++]); - } + SPI.transfer(p,128); + p += 128; digitalWrite(cs, HIGH); #ifdef SPI_HAS_TRANSACTION - if (sclk == -1) SPI.endTransaction(); // release the SPI bus #endif - } } + SD.m_card->setDedicatedSpi(true); display_lock = false; } @@ -518,29 +446,4 @@ void Adafruit_SSD1305::clearDisplay(void) { memset(buffer, 0, (SSD1305_LCDWIDTH * SSD1305_LCDHEIGHT / 8)); } -void Adafruit_SSD1305::spixfer(uint8_t x) { - if (sclk == -1) { - SPI.transfer(x); - return; - } - // software spi - // Serial.println("Software SPI"); - - for (uint8_t bit = 0x80; bit; bit >>= 1) { -#if defined(AVR) - *clkport &= ~clkpinmask; - if (x & bit) - *mosiport |= mosipinmask; - else - *mosiport &= ~mosipinmask; - *clkport |= clkpinmask; -#else - digitalWrite(sclk, LOW); - if (x & bit) - digitalWrite(sid, HIGH); - else - digitalWrite(sid, LOW); - digitalWrite(sclk, HIGH); -#endif - } -} + diff --git a/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.h b/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.h index 725a28bd8..ec84fe9a5 100644 --- a/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.h +++ b/avr/cores/megacommand/Adafruit_SSD1305_Library/Adafruit_SSD1305.h @@ -114,11 +114,9 @@ class Adafruit_SSD1305 : public Adafruit_GFX { Adafruit_SSD1305(int8_t SID, int8_t SCLK, int8_t DC, int8_t RST) :sid(SID), sclk(SCLK), dc(DC), rst(RST), cs(-1), Adafruit_GFX(SSD1305_LCDWIDTH, SSD1305_LCDHEIGHT) {} Adafruit_SSD1305(int8_t DC, int8_t RST, int8_t CS) :sid(-1), sclk(-1), dc(DC), rst(RST), cs(CS), Adafruit_GFX(SSD1305_LCDWIDTH, SSD1305_LCDHEIGHT) {} - Adafruit_SSD1305(int8_t RST) :sid(-1), sclk(-1), dc(-1), rst(RST), cs(-1), Adafruit_GFX(SSD1305_LCDWIDTH, SSD1305_LCDHEIGHT) {} - - - void begin(uint8_t i2caddr = SSD1305_I2C_ADDRESS); + void begin(); + void command(uint8_t c); void data(uint8_t c); diff --git a/avr/cores/megacommand/GUI/GUI.cpp b/avr/cores/megacommand/GUI/GUI.cpp index b2d0b5541..01743f7e6 100644 --- a/avr/cores/megacommand/GUI/GUI.cpp +++ b/avr/cores/megacommand/GUI/GUI.cpp @@ -1,8 +1,8 @@ #include "GUI.h" #include "MidiUart.h" -#include "WProgram.h" -#include "Sketch.h" #include "Pages.h" +#include "Sketch.h" +#include "WProgram.h" #define SCREEN_SAVER_TIME 5 @@ -11,9 +11,7 @@ Sketch _defaultSketch((char *)"DFT"); /************************************************/ -GuiClass::GuiClass() { - setSketch(&_defaultSketch); -} +GuiClass::GuiClass() { setSketch(&_defaultSketch); } void GuiClass::setSketch(Sketch *_sketch) { if (sketch != NULL) { @@ -90,7 +88,6 @@ void GuiClass::loop() { } } - MidiUartParent::handle_midi_lock = 1; for (int i = 0; i < tasks.size; i++) { if (tasks.arr[i] != NULL) { @@ -129,12 +126,14 @@ void GuiClass::loop() { } } MidiUartParent::handle_midi_lock = _midi_lock_tmp; - } -void GuiClass::display_lcd() { +void GuiClass::display_lcd() {} -} +#ifdef DEBUGMODE +uint8_t fps = 0; +uint16_t fps_clock = 0; +#endif void GuiClass::display() { PageParent *page = NULL; @@ -145,6 +144,19 @@ void GuiClass::display() { page->redisplay = false; } } + +#ifdef DEBUGMODE + if (fps == 0 && fps_clock == 0) { + fps_clock = slowclock; + } + fps++; + if (clock_diff(fps_clock, slowclock) > 1000) { + DEBUG_PRINT("FPS: "); + DEBUG_PRINTLN(fps); + fps = 0; + fps_clock = slowclock; + } +#endif #ifdef UART_USB #ifndef DEBUGMODE if (display_mirror) { @@ -171,8 +183,8 @@ void GuiClass::display() { MidiUartUSB.m_putc(buf, sizeof(buf)); n = n + 7; } - MidiUartUSB.m_putc(0xF7); - MidiUartParent::handle_midi_lock = 0; + MidiUartUSB.m_putc(0xF7); + MidiUartParent::handle_midi_lock = 0; } #endif #endif diff --git a/avr/cores/megacommand/MCL/A4Track.cpp b/avr/cores/megacommand/MCL/A4Track.cpp index f61230076..27fa9587a 100644 --- a/avr/cores/megacommand/MCL/A4Track.cpp +++ b/avr/cores/megacommand/MCL/A4Track.cpp @@ -32,12 +32,11 @@ bool A4Track::get_track_from_sysex(uint8_t tracknumber) { } void A4Track::load_immediate(uint8_t tracknumber, SeqTrack *seq_track) { - store_in_mem(tracknumber); load_seq_data(seq_track); } bool A4Track::store_in_grid(uint8_t column, uint16_t row, SeqTrack *seq_track, uint8_t merge, - bool online) { + bool online, Grid *grid) { active = A4_TRACK_TYPE; @@ -57,7 +56,8 @@ bool A4Track::store_in_grid(uint8_t column, uint16_t row, SeqTrack *seq_track, u memcpy(&seq_data, ext_track->data(), sizeof(seq_data)); } #endif - ret = proj.write_grid((uint8_t *)this, sizeof(A4Track), column, row); + ret = write_grid((uint8_t *)(this), len, column, row, grid); + if (!ret) { return false; } diff --git a/avr/cores/megacommand/MCL/A4Track.h b/avr/cores/megacommand/MCL/A4Track.h index 19fee792e..2ec3f6c4f 100644 --- a/avr/cores/megacommand/MCL/A4Track.h +++ b/avr/cores/megacommand/MCL/A4Track.h @@ -27,7 +27,7 @@ class A4Track : public ExtTrack { bool get_track_from_sysex(uint8_t tracknumber); bool store_in_grid(uint8_t column, uint16_t row, SeqTrack *seq_track = nullptr, uint8_t merge = 0, - bool online = false); + bool online = false, Grid *grid = nullptr); bool convert(A4Track_270 *old) { link.row = old->link.row; link.loops = old->link.loops; diff --git a/avr/cores/megacommand/MCL/DeviceTrack.cpp b/avr/cores/megacommand/MCL/DeviceTrack.cpp index a8159db48..0bf570a94 100644 --- a/avr/cores/megacommand/MCL/DeviceTrack.cpp +++ b/avr/cores/megacommand/MCL/DeviceTrack.cpp @@ -37,8 +37,42 @@ DeviceTrack *DeviceTrack::init_track_type(uint8_t track_type) { return this; } +DeviceTrack *DeviceTrack::load_from_grid_512(uint8_t column, uint16_t row, Grid *grid) { + if (!GridTrack::load_from_grid_512(column, row, grid)) { + return nullptr; + } + + // header read successfully. now reconstruct the object. + auto ptrack = init_track_type(active); + + if (ptrack == nullptr) { + DEBUG_PRINTLN("unrecognized track type"); + return nullptr; + } + + // virtual functions are ready + + if (active != EMPTY_TRACK_TYPE) { + if ( ptrack->get_track_size() < 512) { return ptrack; } + size_t len = ptrack->get_track_size() - 512; + if (grid) { + if (!grid->read((uint8_t*) this + 512, len)) { + DEBUG_PRINTLN(F("read failed")); + return nullptr; + } + } + else { + if (!proj.read_grid((uint8_t*) this + 512, len)) { + DEBUG_PRINTLN(F("read failed")); + return nullptr; + } + } + } + return ptrack; +} + + DeviceTrack *DeviceTrack::load_from_grid(uint8_t column, uint16_t row) { - DEBUG_PRINTLN(F("lfg")); if (!GridTrack::load_from_grid(column, row)) { return nullptr; } @@ -52,11 +86,14 @@ DeviceTrack *DeviceTrack::load_from_grid(uint8_t column, uint16_t row) { } // virtual functions are ready - uint32_t len = ptrack->get_track_size(); - if (!proj.read_grid(ptrack, len, column, row)) { - DEBUG_PRINTLN(F("read failed")); - return nullptr; + if (active != EMPTY_TRACK_TYPE) { + uint32_t len = ptrack->get_track_size(); + + if (!proj.read_grid(ptrack, len, column, row)) { + DEBUG_PRINTLN(F("read failed")); + return nullptr; + } } auto ptrack2 = ptrack->init_track_type(active); diff --git a/avr/cores/megacommand/MCL/DeviceTrack.h b/avr/cores/megacommand/MCL/DeviceTrack.h index 104df2fe1..d6457cbcd 100644 --- a/avr/cores/megacommand/MCL/DeviceTrack.h +++ b/avr/cores/megacommand/MCL/DeviceTrack.h @@ -54,7 +54,8 @@ class DeviceTrack : public GridTrack { public: // bool get_track_from_sysex(int tracknumber, uint8_t column); // void place_track_in_sysex(int tracknumber, uint8_t column); - virtual bool store_in_grid(uint8_t column, uint16_t row, SeqTrack *seq_track = nullptr, uint8_t merge = 0, bool online = false) = 0; + +// virtual bool store_in_grid(uint8_t column, uint16_t row, SeqTrack *seq_track = nullptr, uint8_t merge = 0, bool online = false, Grid *grid = nullptr) = 0; virtual uint16_t get_track_size() = 0; virtual void* get_sound_data_ptr() = 0; virtual size_t get_sound_data_size() = 0; @@ -68,6 +69,7 @@ class DeviceTrack : public GridTrack { return p; } + DeviceTrack *load_from_grid_512(uint8_t column, uint16_t row, Grid *grid = nullptr); DeviceTrack *load_from_grid(uint8_t column, uint16_t row); template T *load_from_grid(uint8_t col, uint16_t row) { auto *p = load_from_grid(col, row); @@ -80,9 +82,9 @@ class DeviceTrack : public GridTrack { template bool is() { return _dynamik_kast(this) != nullptr; } template T *as() { return _dynamik_kast(this); } /// downloads from BANK1 to the runtime object - DeviceTrack* load_from_mem(uint8_t col, uint8_t track_type) { + DeviceTrack* load_from_mem(uint8_t col, uint8_t track_type, size_t size = 0) { DeviceTrack *that = init_track_type(track_type); - if (!that->GridTrack::load_from_mem(col)) { + if (!that->GridTrack::load_from_mem(col, size)) { return nullptr; } if (that->active != track_type) { diff --git a/avr/cores/megacommand/MCL/ExtTrack.cpp b/avr/cores/megacommand/MCL/ExtTrack.cpp index cf7144605..12350be48 100644 --- a/avr/cores/megacommand/MCL/ExtTrack.cpp +++ b/avr/cores/megacommand/MCL/ExtTrack.cpp @@ -11,7 +11,7 @@ void ExtTrack::transition_load(uint8_t tracknumber, SeqTrack* seq_track, uint8_t } void ExtTrack::load_immediate(uint8_t tracknumber, SeqTrack *seq_track) { - store_in_mem(tracknumber); + DEBUG_PRINTLN("load immediate, ext"); load_seq_data(seq_track); } @@ -20,7 +20,7 @@ bool ExtTrack::get_track_from_sysex(uint8_t tracknumber) { return true; } -bool ExtTrack::load_seq_data(SeqTrack *seq_track) { +void ExtTrack::load_seq_data(SeqTrack *seq_track) { #ifdef EXT_TRACKS ExtSeqTrack *ext_track = (ExtSeqTrack *) seq_track; @@ -38,11 +38,10 @@ bool ExtTrack::load_seq_data(SeqTrack *seq_track) { ext_track->set_length(seq_track->length); seq_track->mute_state = old_mute; #endif - return true; } bool ExtTrack::store_in_grid(uint8_t column, uint16_t row, SeqTrack *seq_track, uint8_t merge, - bool online) { + bool online, Grid *grid) { /*Assign a track to Grid i*/ /*Extraact track data from received pattern and kit and store in track * object*/ @@ -62,8 +61,7 @@ bool ExtTrack::store_in_grid(uint8_t column, uint16_t row, SeqTrack *seq_track, memcpy(&seq_data, ext_track->data(), sizeof(seq_data)); } #endif - - ret = proj.write_grid((uint8_t *)this, sizeof(ExtTrack), column, row); + ret = write_grid((uint8_t *)(this), len, column, row, grid); if (!ret) { DEBUG_PRINTLN(F("Write failed")); return false; diff --git a/avr/cores/megacommand/MCL/ExtTrack.h b/avr/cores/megacommand/MCL/ExtTrack.h index 87a20234e..c4c509b63 100644 --- a/avr/cores/megacommand/MCL/ExtTrack.h +++ b/avr/cores/megacommand/MCL/ExtTrack.h @@ -23,11 +23,11 @@ class ExtTrack : public DeviceTrack { virtual void transition_load(uint8_t tracknumber, SeqTrack *seq_track, uint8_t slotnumber); - bool load_seq_data(SeqTrack *seq_track); + void load_seq_data(SeqTrack *seq_track); virtual bool get_track_from_sysex(uint8_t tracknumber); bool store_in_grid(uint8_t column, uint16_t row, SeqTrack *seq_track = nullptr, uint8_t merge = 0, - bool online = false); + bool online = false, Grid *grid = nullptr); virtual void init(uint8_t tracknumber, SeqTrack *seq_track) { ExtSeqTrack *ext_seq_track = (ExtSeqTrack *)seq_track; @@ -69,8 +69,9 @@ class ExtTrack : public DeviceTrack { } virtual uint8_t get_model() { return EXT_TRACK_TYPE; } - virtual uint16_t get_track_size() { return GRID2_TRACK_LEN; } + virtual uint16_t get_track_size() { return sizeof(ExtTrack); } virtual uint32_t get_region() { return BANK1_A4_TRACKS_START; } + virtual uint16_t get_region_size() { return GRID2_TRACK_LEN; } virtual uint8_t get_device_type() { return EXT_TRACK_TYPE; } virtual void *get_sound_data_ptr() { return nullptr; } diff --git a/avr/cores/megacommand/MCL/FileBrowserPage.cpp b/avr/cores/megacommand/MCL/FileBrowserPage.cpp index af197f33a..201321288 100644 --- a/avr/cores/megacommand/MCL/FileBrowserPage.cpp +++ b/avr/cores/megacommand/MCL/FileBrowserPage.cpp @@ -94,7 +94,9 @@ void FileBrowserPage::query_filesystem() { DEBUG_PRINTLN(file_menu_encoder.max); // reset directory pointer - SD.vwd()->rewind(); + File d; + d.open(lwd); + d.rewind(); numEntries = 0; cur_file = 255; if (show_save) { @@ -104,7 +106,9 @@ void FileBrowserPage::query_filesystem() { add_entry("[ SAVE ]"); } } - SD.vwd()->getName(temp_entry, FILE_ENTRY_SIZE); + //SD.vwd()->getName(temp_entry, FILE_ENTRY_SIZE); + //SD.vwd()->getName(temp_entry, FILE_ENTRY_SIZE); + file.getName(temp_entry, FILE_ENTRY_SIZE); if ((show_parent) && !(strcmp(temp_entry, "/") == 0)) { add_entry(".."); @@ -113,13 +117,14 @@ void FileBrowserPage::query_filesystem() { encoders[1]->cur = 1; encoders[1]->old = 1; // iterate through the files - while (file.openNext(SD.vwd(), O_READ) && (numEntries < MAX_ENTRIES)) { + while (file.openNext(&d, O_READ) && (numEntries < MAX_ENTRIES)) { for (uint8_t c = 0; c < FILE_ENTRY_SIZE; c++) { temp_entry[c] = 0; } file.getName(temp_entry, FILE_ENTRY_SIZE); bool is_match_file = false; - DEBUG_DUMP(temp_entry); + DEBUG_PRINTLN(numEntries); + DEBUG_PRINTLN(temp_entry); if (temp_entry[0] == '.') { is_match_file = false; } else if (file.isDirectory() && show_dirs) { @@ -131,7 +136,7 @@ void FileBrowserPage::query_filesystem() { is_match_file = true; } } - if (is_match_file) { + if (is_match_file && (strlen(temp_entry) > 0)) { DEBUG_PRINTLN(F("file matched")); if (add_entry(temp_entry)) { if (strcmp(temp_entry, mcl_cfg.project) == 0) { @@ -405,13 +410,15 @@ bool FileBrowserPage::_handle_filemenu() { bool FileBrowserPage::rm_dir(const char *dir) { char temp_entry[FILE_ENTRY_SIZE]; - SD.vwd()->getName(temp_entry, FILE_ENTRY_SIZE); + //SD.vwd()->getName(temp_entry, FILE_ENTRY_SIZE); DEBUG_PRINTLN("preparing to delete"); DEBUG_PRINTLN(dir); if (_cd(dir)) { - SD.vwd()->rewind(); + File d; + d.open(lwd); + d.rewind(); // bool ret = SD.vwd()->rmRfStar(); // extra 276 bytes - while (file.openNext(SD.vwd(), O_READ)) { + while (file.openNext(&d, O_READ)) { file.getName(temp_entry, FILE_ENTRY_SIZE); DEBUG_PRINT("deleting "); DEBUG_PRINTLN(temp_entry); file.close(); diff --git a/avr/cores/megacommand/MCL/GridLoadPage.cpp b/avr/cores/megacommand/MCL/GridLoadPage.cpp index e3001fe0e..f4459be36 100644 --- a/avr/cores/megacommand/MCL/GridLoadPage.cpp +++ b/avr/cores/megacommand/MCL/GridLoadPage.cpp @@ -180,7 +180,7 @@ void GridLoadPage::display() { } void GridLoadPage::load() { display_load(); - oled_display.display(); +// oled_display.display(); /// !Note, note_off_event has reentry issues, so we have to first set /// the page to avoid driving this code path again. @@ -199,8 +199,8 @@ void GridLoadPage::load() { } GUI.setPage(&grid_page); trig_interface.off(); - mcl_actions.load_tracks(grid_page.getRow(), - track_select_array); + + grid_task.load_queue.put(mcl_cfg.load_mode, grid_page.getRow(), track_select_array); } void GridLoadPage::group_select() { @@ -213,16 +213,15 @@ void GridLoadPage::group_load(uint8_t row, bool silent) { if (row >= GRID_LENGTH) { return; } uint8_t track_select_array[NUM_SLOTS] = {0}; - track_select_array_from_type_select(track_select_array); // load_tracks_to_md(-1); if (!silent) { oled_display.textbox("LOAD GROUPS", ""); } - oled_display.display(); + //oled_display.display(); mcl_actions.write_original = 1; - mcl_actions.load_tracks(row, track_select_array); + grid_task.load_queue.put(mcl_cfg.load_mode, row, track_select_array); } bool GridLoadPage::handleEvent(gui_event_t *event) { @@ -262,6 +261,10 @@ bool GridLoadPage::handleEvent(gui_event_t *event) { uint8_t key = event->source - 64; if (event->mask == EVENT_BUTTON_PRESSED) { switch (key) { + default: { + GUI.setPage(&grid_page); + return false; + } case MDX_KEY_YES: { group_select(); return true; @@ -280,7 +283,10 @@ bool GridLoadPage::handleEvent(gui_event_t *event) { if (event->mask == EVENT_BUTTON_RELEASED) { switch (key) { case MDX_KEY_YES: + if (show_track_type) { goto load_groups; + } + return true; } } return false; diff --git a/avr/cores/megacommand/MCL/GridLoadPage.h b/avr/cores/megacommand/MCL/GridLoadPage.h index 8e70351e7..c88eaf3fb 100644 --- a/avr/cores/megacommand/MCL/GridLoadPage.h +++ b/avr/cores/megacommand/MCL/GridLoadPage.h @@ -10,6 +10,8 @@ class GridLoadPage : public GridIOPage { GridLoadPage(Encoder *e1 = NULL, Encoder *e2 = NULL, Encoder *e3 = NULL, Encoder *e4 = NULL) : GridIOPage(e1, e2, e3 ,e4) { } + uint8_t track_select_array[NUM_SLOTS]; + uint8_t load_row; void get_modestr(char *modestr); void load(); void group_select(); diff --git a/avr/cores/megacommand/MCL/GridPage.cpp b/avr/cores/megacommand/MCL/GridPage.cpp index 7e0d1ecbb..2b0f24c37 100644 --- a/avr/cores/megacommand/MCL/GridPage.cpp +++ b/avr/cores/megacommand/MCL/GridPage.cpp @@ -174,7 +174,7 @@ void GridPage::loop() { mcl_cfg.tempo = MidiClock.get_tempo(); DEBUG_PRINTLN(F("write cfg")); - mcl_cfg.write_cfg(); + if (MidiClock.state != 2) { mcl_cfg.write_cfg(); } grid_lastclock = slowclock; write_cfg = false; // } @@ -738,7 +738,7 @@ void GridPage::apply_slot_changes(bool ignore_undo, bool ignore_func) { } } if (slot_load == 1) { - mcl_actions.load_tracks(ypos, track_select_array); + grid_task.load_queue.put(mcl_cfg.load_mode,ypos, track_select_array); } // If all slots are deleted then clear the row name else if ((header.is_empty() && (slot_clear == 1)) || (activate_header)) { diff --git a/avr/cores/megacommand/MCL/GridSavePage.cpp b/avr/cores/megacommand/MCL/GridSavePage.cpp index cb502ff84..fe4fe0e65 100644 --- a/avr/cores/megacommand/MCL/GridSavePage.cpp +++ b/avr/cores/megacommand/MCL/GridSavePage.cpp @@ -146,6 +146,10 @@ bool GridSavePage::handleEvent(gui_event_t *event) { if (event->mask == EVENT_BUTTON_PRESSED) { switch (key) { + default: { + GUI.setPage(&grid_page); + return false; + } case MDX_KEY_YES: { group_select(); return true; @@ -162,7 +166,10 @@ bool GridSavePage::handleEvent(gui_event_t *event) { if (event->mask == EVENT_BUTTON_RELEASED) { switch (key) { case MDX_KEY_YES: - goto save_groups; + if (show_track_type) { + goto save_groups; + } + return true; } } } diff --git a/avr/cores/megacommand/MCL/GridTask.cpp b/avr/cores/megacommand/MCL/GridTask.cpp index 12ed3354a..b42ec8dc7 100644 --- a/avr/cores/megacommand/MCL/GridTask.cpp +++ b/avr/cores/megacommand/MCL/GridTask.cpp @@ -1,4 +1,3 @@ - #include "MCL_impl.h" #define DIV16_MARGIN 8 @@ -28,48 +27,44 @@ void GridTask::sync_cursor() { } MDSeqTrack::sync_cursor = 0; } - } void GridTask::run() { // DEBUG_PRINTLN(MidiClock.div32th_counter / 2); // A4Track *a4_track = (A4Track *)&temp_track; // ExtTrack *ext_track = (ExtTrack *)&temp_track; - if (load_row != 255) { - bool silent = true; - grid_load_page.group_load(load_row, silent); - load_row = 255; - } + // MD GUI update. + if (stop_hard_callback) { + mcl_actions_callbacks.StopHardCallback(); + stop_hard_callback = false; + load_queue.init(); + return; + } + + sync_cursor(); + GridTask::transition_handler(); - if ((midi_load) && (clock_diff(midi_event_clock, clock) > 60)) { + if (!load_queue.is_empty()) { + uint8_t mode; + uint8_t row_select_array[NUM_SLOTS]; uint8_t track_select[NUM_SLOTS] = {0}; - uint8_t r = 255; - DEBUG_PRINTLN("process midi load"); + load_queue.get(&mode, row_select_array); + DEBUG_PRINTLN("load queue get"); + DEBUG_PRINTLN(mode); for (uint8_t n = 0; n < NUM_SLOTS; n++) { - if (midi_track_select[n] < 128) { + if (row_select_array[n] < 128) { track_select[n] = 1; } + DEBUG_PRINT(n); DEBUG_PRINT(" "); DEBUG_PRINT(track_select[n]); DEBUG_PRINT(" "); DEBUG_PRINTLN(row_select_array[n]); } mcl_actions.write_original = 1; - mcl_actions.load_tracks(r, track_select, midi_track_select); - midi_load = false; + mcl_actions.load_tracks(255, track_select, row_select_array, mode); } - if (stop_hard_callback) { - mcl_actions_callbacks.StopHardCallback(); - stop_hard_callback = false; - return; - } - // MD GUI update. - sync_cursor(); - GridTask::transition_handler(); } void GridTask::transition_handler() { - if (MidiClock.state != 2 || mcl_actions.next_transition == (uint16_t)-1) { - return; - } MidiDevice *devs[2] = { midi_active_peering.get_device(UART1_PORT), midi_active_peering.get_device(UART2_PORT), @@ -87,178 +82,173 @@ void GridTask::transition_handler() { bool send_ext_slots = false; bool send_md_slots = false; - uint8_t div32th_margin = 8; + uint8_t div32th_margin = 6; + - uint32_t div32th_counter; + GUI.removeTask(&grid_task); + + while (MidiClock.clock_less_than( + MidiClock.div32th_counter + 0.240 * (MidiClock.get_tempo()* 0.133333333333f), + (uint32_t)mcl_actions.next_transition * 2) <= 0) { - // Get within four 16th notes of the next transition. - if (MidiClock.clock_less_than(MidiClock.div32th_counter + div32th_margin, - (uint32_t)mcl_actions.next_transition * 2) <= - 0) { + float div32th_per_second = MidiClock.get_tempo() * 0.133333333333f; + float div32th_time = 1.0 / div32th_per_second; + + if (MidiClock.state != 2 || mcl_actions.next_transition == (uint16_t)-1) { + break; + } DEBUG_PRINTLN(F("Preparing for next transition:")); DEBUG_PRINTLN(MidiClock.div16th_counter); DEBUG_PRINTLN(mcl_actions.next_transition); - // Transition window - div32th_counter = MidiClock.div32th_counter + div32th_margin; - } else { - return; - } - DEBUG_PRINTLN((int)SP); - GUI.removeTask(&grid_task); - uint8_t track_idx, dev_idx; + DEBUG_PRINTLN((int)SP); + uint8_t track_idx, dev_idx; - uint8_t row = 255; + uint8_t row = 255; - for (uint8_t n = 0; n < NUM_SLOTS; n++) { - slots_changed[n] = 255; + for (uint8_t n = 0; n < NUM_SLOTS; n++) { + slots_changed[n] = 255; - if ((mcl_actions.links[n].loops == 0) || - (grid_page.active_slots[n] == SLOT_DISABLED)) - continue; + DEBUG_PRINTLN(n); + DEBUG_PRINT(mcl_actions.next_transition); DEBUG_PRINT(" "); DEBUG_PRINTLN(mcl_actions.next_transitions[n]); - uint32_t next_transition = (uint32_t)mcl_actions.next_transitions[n] * 2; + if ((mcl_actions.links[n].loops == 0) || + (grid_page.active_slots[n] == SLOT_DISABLED) || (mcl_actions.next_transition != mcl_actions.next_transitions[n])) + continue; - // If next_transition[n] is less than or equal to transition window, then - // flag track for transition. - if (MidiClock.clock_less_than(div32th_counter, next_transition)) - continue; + GridDeviceTrack *gdt = + mcl_actions.get_grid_dev_track(n, &track_idx, &dev_idx); - // if ((mcl_actions.links[n].row != grid_page.active_slots[n]) || - // (mcl_actions.chains[n].mode == LOAD_MANUAL)) { + if (gdt == nullptr) { + continue; + } - GridDeviceTrack *gdt = - mcl_actions.get_grid_dev_track(n, &track_idx, &dev_idx); + if (link_load(n, track_idx, slots_changed, track_select_array, gdt)) { + send_device[dev_idx] = true; + } - if (gdt == nullptr) { - continue; + if (row == 255) { + row = slots_changed[n]; + } } - if (link_load(n, track_idx, slots_changed, track_select_array, gdt)) { - send_device[dev_idx] = true; - } + DEBUG_PRINTLN(F("sending tracks")); + bool wait; - // if (mcl_actions.chains[n].mode == LOAD_MANUAL) { - if (row == 255) { - row = slots_changed[n]; + if (mcl_cfg.uart2_prg_out > 0 && row != 255) { + MidiUart2.sendProgramChange(mcl_cfg.uart2_prg_out - 1, row); } - // mcl_actions.links[n].loops = 0; - // } - } - - DEBUG_PRINTLN(F("sending tracks")); - bool wait; - - if (mcl_cfg.uart2_prg_out > 0 && row != 255) { - MidiUart2.sendProgramChange(mcl_cfg.uart2_prg_out - 1, row); - } + float tempo = MidiClock.get_tempo(); + // float div192th_per_second = tempo * 0.8f; + // float div192th_time = 1.0 / div192th_per_second; + float div192th_time = 1.0 / (tempo * 0.8f); + // diff * div19th_time > 0.8ms equivalent to diff > (0.8/1.25) * tempo + for (int8_t c = NUM_DEVS - 1; c >= 0; c--) { + wait = true; + + for (uint8_t n = 0; n < NUM_SLOTS; n++) { + if (slots_changed[n] == 255) + continue; + + GridDeviceTrack *gdt = + mcl_actions.get_grid_dev_track(n, &track_idx, &dev_idx); + if ((gdt == nullptr) || (dev_idx != c)) + continue; + + // Wait on first track of each device; + + if (wait && send_device[c]) { + + uint32_t go_step = mcl_actions.next_transition * 12 - + mcl_actions.div192th_total_latency - 1; + + mcl_actions.div192th_total_latency -= + mcl_actions.dev_latency[dev_idx].latency; + + uint32_t diff; + + while (((diff = MidiClock.clock_diff_div192( + MidiClock.div192th_counter, go_step)) != 0) && + (MidiClock.div192th_counter < go_step) && + (MidiClock.state == 2)) { + MidiUartParent::handle_midi_lock = 1; + handleIncomingMidi(); + MidiUartParent::handle_midi_lock = 0; + if ((float)diff > (tempo * 0.8f) * 0.08) { + GUI.loop(); + } + } + } + wait = false; + if (transition_load(n, track_idx, dev_idx, gdt)) { + grid_page.active_slots[n] = slots_changed[n]; + } + } + } + DEBUG_PRINTLN(F("SP pre cache")); + DEBUG_PRINTLN((int)SP); - for (int8_t c = NUM_DEVS - 1; c >= 0; c--) { - wait = true; + bool update_gui = true; - for (uint8_t n = 0; n < NUM_SLOTS; n++) { - if (slots_changed[n] == 255) - continue; + DEBUG_PRINTLN("cache next"); - GridDeviceTrack *gdt = - mcl_actions.get_grid_dev_track(n, &track_idx, &dev_idx); - if ((gdt == nullptr) || (dev_idx != c)) - continue; + volatile uint32_t clk = slowclock; + mcl_actions.cache_next_tracks(track_select_array, update_gui); - // Wait on first track of each device; + uint32_t t = clock_diff(clk, slowclock); + DEBUG_PRINTLN("time"); + DEBUG_PRINTLN(t); - if (wait && send_device[c]) { + // Once tracks are cached, we can calculate their next transition + uint8_t last_slot = 255; + for (uint8_t n = 0; n < NUM_SLOTS; n++) { + bool ignore_chain_settings = true; + if (track_select_array[n] > 0) { + last_slot = n; + ignore_chain_settings = false; + } + mcl_actions.calc_next_slot_transition(n, ignore_chain_settings); + } - uint32_t go_step = mcl_actions.next_transition * 12 - - mcl_actions.div192th_total_latency - 1; + if (last_slot != 255 && slots_changed[last_slot] < GRID_LENGTH) { + // GridDeviceTrack *gdt = + // mcl_actions.get_grid_dev_track(last_slot, &track_idx, &dev_idx); + last_active_row = slots_changed[last_slot]; + next_active_row = mcl_actions.links[last_slot].row; + chain_behaviour = mcl_actions.chains[last_slot].mode > 1; - mcl_actions.div192th_total_latency -= - mcl_actions.dev_latency[dev_idx].latency; + GridDeviceTrack *gdt = + mcl_actions.get_grid_dev_track(last_slot, &track_idx, &dev_idx); - uint32_t diff; + GridRowHeader row_header; + proj.read_grid_row_header(&row_header, last_active_row); + dev_idx = 0; - float tempo = MidiClock.get_tempo(); - float div192th_per_second = tempo * 0.8f; + if (elektron_devs[dev_idx]) { + uint8_t len = elektron_devs[dev_idx]->sysex_protocol.kitname_length; - while (((diff = MidiClock.clock_diff_div192(MidiClock.div192th_counter, - go_step)) != 0) && - (MidiClock.div192th_counter < go_step) && - (MidiClock.state == 2)) { - if ((float)diff * div192th_per_second > 0.160) { - handleIncomingMidi(); - if (GUI.currentPage() == &grid_load_page) { - GUI.display(); - } else { - GUI.loop(); - } - } + if (row_header.active) { + memcpy(kit_names[dev_idx], row_header.name, len); + kit_names[dev_idx][len - 1] = '\0'; + } else { + strcpy(kit_names[dev_idx], "NEW_KIT"); } } - wait = false; - if (transition_load(n, track_idx, dev_idx, gdt)) { - grid_page.active_slots[n] = slots_changed[n]; - } - } - } - DEBUG_PRINTLN(F("SP pre cache")); - DEBUG_PRINTLN((int)SP); - - bool update_gui = true; - mcl_actions.cache_next_tracks(track_select_array, update_gui); - - // Once tracks are cached, we can calculate their next transition - uint8_t last_slot = 255; - for (uint8_t n = 0; n < NUM_SLOTS; n++) { - if (track_select_array[n] > 0) { - mcl_actions.calc_next_slot_transition(n); - last_slot = n; } - } - if (last_slot != 255 && slots_changed[last_slot] < GRID_LENGTH) { - // GridDeviceTrack *gdt = - // mcl_actions.get_grid_dev_track(last_slot, &track_idx, &dev_idx); - last_active_row = slots_changed[last_slot]; - next_active_row = mcl_actions.links[last_slot].row; - chain_behaviour = mcl_actions.chains[last_slot].mode > 1; - - GridDeviceTrack *gdt = - mcl_actions.get_grid_dev_track(last_slot, &track_idx, &dev_idx); - - GridRowHeader row_header; - DEBUG_PRINTLN("read row header"); - DEBUG_PRINTLN(last_active_row); - proj.read_grid_row_header(&row_header, last_active_row); - dev_idx = 0; - - if (elektron_devs[dev_idx]) { - uint8_t len = elektron_devs[dev_idx]->sysex_protocol.kitname_length; - - if (row_header.active) { - memcpy(kit_names[dev_idx], row_header.name, len); - kit_names[dev_idx][len - 1] = '\0'; - } else { - strcpy(kit_names[dev_idx], "NEW_KIT"); - } - } + sync_cursor(); + mcl_actions.calc_next_transition(); + mcl_actions.calc_latency(); } - - sync_cursor(); - mcl_actions.calc_next_transition(); - mcl_actions.calc_latency(); -// } else { -// mcl_actions.next_transition = (uint16_t)-1; -// } -end: GUI.addTask(&grid_task); } bool GridTask::link_load(uint8_t n, uint8_t track_idx, uint8_t *slots_changed, uint8_t *track_select_array, GridDeviceTrack *gdt) { EmptyTrack empty_track; - auto *pmem_track = - empty_track.load_from_mem(gdt->mem_slot_idx, gdt->track_type); + auto *pmem_track = empty_track.load_from_mem( + gdt->mem_slot_idx, gdt->track_type, sizeof(GridTrack)); if (pmem_track == nullptr) { return false; } @@ -286,8 +276,9 @@ bool GridTask::transition_load(uint8_t n, uint8_t track_idx, uint8_t dev_idx, auto *pmem_track = empty_track.load_from_mem(gdt->mem_slot_idx, gdt->track_type); - if (pmem_track == nullptr) + if (pmem_track == nullptr) { return false; + } gdt->seq_track->count_down = -1; if (mcl_actions.send_machine[n] == 0) { diff --git a/avr/cores/megacommand/MCL/GridTask.h b/avr/cores/megacommand/MCL/GridTask.h index 58da80c97..a4bad79ca 100644 --- a/avr/cores/megacommand/MCL/GridTask.h +++ b/avr/cores/megacommand/MCL/GridTask.h @@ -4,6 +4,54 @@ #include "MCL.h" #include "Task.h" #include "Elektron.h" +#include "GridChain.h" + +class LoadQueue { + public: + uint8_t row_selects[NUM_LINKS][NUM_SLOTS]; + uint8_t modes[NUM_LINKS]; + uint8_t rd; + uint8_t wr; + + void init() { + rd = 0; + wr = 0; + } + + void put(uint8_t mode, uint8_t *row_select) { + memcpy(row_selects[wr],row_select,NUM_SLOTS); + modes[wr++] = mode; + if (wr == NUM_LINKS) { + wr = 0; + } + } + + + void put(uint8_t mode, uint8_t row, uint8_t *track_select_array) { + for (uint8_t n = 0; n < NUM_SLOTS; n++) { + row_selects[wr][n] = 255; + if (track_select_array[n]) { row_selects[wr][n] = row; } + } + modes[wr++] = mode; + if (wr == NUM_LINKS) { + wr = 0; + } + } + + void get(uint8_t *mode, uint8_t *row_select) { + memcpy(row_select,row_selects[rd],NUM_SLOTS); + *mode = modes[rd++]; + if (rd == NUM_LINKS) { + rd = 0; + } + } + + bool is_empty() { + bool ret = (rd == wr); + return ret; + } + +}; class GridTask : public Task { @@ -17,13 +65,17 @@ class GridTask : public Task { bool chain_behaviour; + uint8_t load_row_midi = 255; uint8_t load_row = 255; + uint8_t midi_row = 255; - uint8_t midi_track_select[NUM_SLOTS]; + uint8_t load_track_select[NUM_SLOTS]; uint8_t midi_row_select = 255; - uint16_t midi_event_clock; + bool midi_load; + LoadQueue load_queue; + GridTask(uint16_t interval) : Task(interval) { setup(interval); } void setup(uint16_t interval = 0); @@ -33,13 +85,14 @@ class GridTask : public Task { void sync_cursor(); void init() { reset_midi_states(); + load_queue.init(); } void reset_midi_states() { + midi_row = 255; load_row = 255; - memset(midi_track_select, 255, sizeof(midi_track_select)); + memset(load_track_select, 255, sizeof(load_track_select)); //midi_row_select = 255; - midi_event_clock = -1; midi_load = false; } diff --git a/avr/cores/megacommand/MCL/GridTrack.cpp b/avr/cores/megacommand/MCL/GridTrack.cpp index 8a7543caf..5247f3c28 100644 --- a/avr/cores/megacommand/MCL/GridTrack.cpp +++ b/avr/cores/megacommand/MCL/GridTrack.cpp @@ -1,11 +1,18 @@ #include "MCL_impl.h" + +bool GridTrack::write_grid(void *data, size_t len, uint8_t column, uint16_t row, Grid *grid = nullptr) { + if (grid == nullptr) { + return proj.write_grid((uint8_t *)(this), len, column, row); + } + else { + return grid->write((uint8_t *)(this), len, column, row); + } +} + void GridTrack::load_link_data(SeqTrack *seq_track) { seq_track->speed = link.speed; seq_track->length = link.length; - DEBUG_PRINTLN("link"); - DEBUG_PRINTLN(link.speed); - DEBUG_PRINTLN(link.length); } void GridTrack::transition_load(uint8_t tracknumber, SeqTrack *seq_track, @@ -18,6 +25,30 @@ void GridTrack::transition_load(uint8_t tracknumber, SeqTrack *seq_track, CLEAR_LOCK(); } +bool GridTrack::load_from_grid_512(uint8_t column, uint16_t row, Grid *grid) { + + if (grid) { + if (!grid->read(this, 512, column, row)) { + DEBUG_PRINTLN(F("read failed")); + return false; + } + } + else { + if (!proj.read_grid(this, 512, column, row)) { + DEBUG_PRINTLN(F("read failed")); + return false; + } + } + auto tmp = this->active; + ::new (this) GridTrack; + this->active = tmp; + + if ((active == EMPTY_TRACK_TYPE) || (active == 255)) { + init(); + } + + return true; +} bool GridTrack::load_from_grid(uint8_t column, uint16_t row) { @@ -39,11 +70,11 @@ bool GridTrack::load_from_grid(uint8_t column, uint16_t row) { // merge and online are ignored here. bool GridTrack::store_in_grid(uint8_t column, uint16_t row, SeqTrack *seq_track, uint8_t merge, - bool online) { + bool online, Grid *grid) { DEBUG_PRINT_FN(); - if (!proj.write_grid(this, sizeof(GridTrack), column, row)) { + if (!write_grid(this, sizeof(GridTrack), column, row, grid)) { DEBUG_PRINTLN(F("write failed")); return false; } diff --git a/avr/cores/megacommand/MCL/GridTrack.h b/avr/cores/megacommand/MCL/GridTrack.h index 30e576770..aacdff188 100644 --- a/avr/cores/megacommand/MCL/GridTrack.h +++ b/avr/cores/megacommand/MCL/GridTrack.h @@ -29,6 +29,8 @@ #include "MCLMemory.h" #include "SeqTrack.h" +class Grid; + class GridTrack_270 { public: uint8_t active = EMPTY_TRACK_TYPE; @@ -47,14 +49,17 @@ class GridTrack { bool is_ext_track() { return (active == EXT_TRACK_TYPE || active == MNM_TRACK_TYPE || active == A4_TRACK_TYPE); } // load header without data from grid + bool load_from_grid_512(uint8_t column, uint16_t row, Grid *grid = nullptr); bool load_from_grid(uint8_t column, uint16_t row); // save header without data to grid - virtual bool store_in_grid(uint8_t column, uint16_t row, SeqTrack *seq_track = nullptr, uint8_t merge = 0, bool online = false); + bool write_grid(void *data, size_t len, uint8_t column, uint16_t row, Grid *grid = nullptr); + + virtual bool store_in_grid(uint8_t column, uint16_t row, SeqTrack *seq_track = nullptr, uint8_t merge = 0, bool online = false, Grid *grid = nullptr); /// caller guarantees that the type is reconstructed correctly /// uploads from the runtime object to BANK1 bool store_in_mem(uint8_t column) { - uint32_t pos = get_region() + get_track_size() * (uint32_t)(column); + uint32_t pos = get_region() + get_region_size() * (uint32_t)(column); volatile uint8_t *ptr = reinterpret_cast(pos); memcpy_bank1(ptr, this, get_track_size()); return true; @@ -62,10 +67,11 @@ class GridTrack { /// caller guarantees that the type is reconstructed correctly /// downloads from BANK1 to the runtime object - bool load_from_mem(uint8_t column) { - uint32_t pos = get_region() + get_track_size() * (uint32_t)(column); + bool load_from_mem(uint8_t column, size_t size = 0) { + uint32_t bytes = size ? size : get_track_size(); + uint32_t pos = get_region() + get_region_size() * (uint32_t)(column); volatile uint8_t *ptr = reinterpret_cast(pos); - memcpy_bank1(this, ptr, get_track_size()); + memcpy_bank1(this, ptr, bytes); return true; } @@ -79,11 +85,17 @@ class GridTrack { virtual void init(uint8_t tracknumber, SeqTrack *seq_track) {} virtual void load_immediate(uint8_t tracknumber, SeqTrack *seq_track) {} - virtual void transition_cache(uint8_t tracknumber, uint8_t slotnumber) {} + virtual bool transition_cache(uint8_t tracknumber, uint8_t slotnumber) { return false; } virtual void transition_send(uint8_t tracknumber, uint8_t slotnumber) {} virtual void transition_load(uint8_t tracknumber, SeqTrack* seq_track, uint8_t slotnumber); + virtual void load_seq_data(SeqTrack *seq_track) {} + + virtual void paste_track(uint8_t src_track, uint8_t dest_track, SeqTrack *seq_track) { + load_immediate(dest_track, seq_track); + } virtual uint16_t get_track_size() { return sizeof(GridTrack); } + virtual uint16_t get_region_size() { return get_track_size(); } virtual uint32_t get_region() { return BANK1_MD_TRACKS_START; } bool is_external() { return get_region() != BANK1_MD_TRACKS_START; } /* Calibrate data members on slot copy */ diff --git a/avr/cores/megacommand/MCL/MCL.cpp b/avr/cores/megacommand/MCL/MCL.cpp index c8ec4aea2..83b2644ff 100644 --- a/avr/cores/megacommand/MCL/MCL.cpp +++ b/avr/cores/megacommand/MCL/MCL.cpp @@ -1,6 +1,36 @@ #include "MCL_impl.h" #include "ResourceManager.h" +void sdcard_bench() { + + EmptyTrack empty_track; + DeviceTrack *ptrack; + while (1) { + uint16_t cl = slowclock; + proj.select_grid(0); + for (uint8_t n = 0; n < 16; n++) { + auto *ptrack = empty_track.load_from_grid_512(n, 0); + ptrack->init_track_type(MD_TRACK_TYPE); + USE_LOCK(); + SET_LOCK(); + if (ptrack) ptrack->store_in_mem(0); + CLEAR_LOCK(); + } + proj.select_grid(1); + for (uint8_t n = 0; n < 16; n++) { + auto *ptrack = empty_track.load_from_grid_512(n, 0); + ptrack->init_track_type(A4_TRACK_TYPE); + USE_LOCK(); + SET_LOCK(); + if (ptrack) ptrack->store_in_mem(0); + CLEAR_LOCK(); + } + uint16_t diff = clock_diff(cl, slowclock); + DEBUG_PRINT("Clock :"); + DEBUG_PRINTLN(diff); + } +} + void mcl_setup() { mcl.setup(); } void MCL::setup() { diff --git a/avr/cores/megacommand/MCL/MCL.h b/avr/cores/megacommand/MCL/MCL.h index f577adce1..c0f63ccb8 100644 --- a/avr/cores/megacommand/MCL/MCL.h +++ b/avr/cores/megacommand/MCL/MCL.h @@ -16,8 +16,8 @@ #include "Fonts/TomThumb.h" #include "Fonts/Elektrothic.h" -#define VERSION 4030 -#define VERSION_STR "4.30" +#define VERSION 4040 +#define VERSION_STR "4.40" #define CALLBACK_TIMEOUT 500 #define GUI_NAME_TIMEOUT 800 diff --git a/avr/cores/megacommand/MCL/MCLActions.cpp b/avr/cores/megacommand/MCL/MCLActions.cpp index 59b0fa2e5..2e5a1c0b0 100644 --- a/avr/cores/megacommand/MCL/MCLActions.cpp +++ b/avr/cores/megacommand/MCL/MCLActions.cpp @@ -27,7 +27,7 @@ //} void MCLActions::setup() { - DEBUG_PRINTLN(F("mcl actions setup")); + // DEBUG_PRINTLN(F("mcl actions setup")); mcl_actions_callbacks.setup_callbacks(); mcl_actions_midievents.setup_callbacks(); for (uint8_t i = 0; i < NUM_SLOTS; i++) { @@ -46,7 +46,7 @@ void MCLActions::init_chains() { } void MCLActions::kit_reload(uint8_t pattern) { - DEBUG_PRINT_FN(); + // DEBUG_PRINT_FN(); /* if (mcl_actions.do_kit_reload != 255) { if (mcl_actions.writepattern == pattern) { @@ -140,7 +140,7 @@ void md_import() { void MCLActions::save_tracks(int row, uint8_t *slot_select_array, uint8_t merge, uint8_t readpattern) { - DEBUG_PRINT_FN(); + // DEBUG_PRINT_FN(); EmptyTrack empty_track; @@ -181,10 +181,10 @@ void MCLActions::save_tracks(int row, uint8_t *slot_select_array, uint8_t merge, if (elektron_devs[i] != nullptr) { if (save_dev_tracks[i]) { if (merge > 0) { - DEBUG_PRINTLN(F("fetching pattern")); - DEBUG_PRINTLN(readpattern); + // DEBUG_PRINTLN(F("fetching pattern")); + // DEBUG_PRINTLN(readpattern); if (!elektron_devs[i]->getBlockingPattern(readpattern)) { - DEBUG_PRINTLN(F("could not receive pattern")); + // DEBUG_PRINTLN(F("could not receive pattern")); save_dev_tracks[i] = false; continue; } @@ -194,13 +194,13 @@ void MCLActions::save_tracks(int row, uint8_t *slot_select_array, uint8_t merge, continue; } if (!elektron_devs[i]->getBlockingKit(p->getKit())) { - DEBUG_PRINTLN(F("could not receive kit")); + // DEBUG_PRINTLN(F("could not receive kit")); continue; } } else { if (elektron_devs[i]->canReadWorkspaceKit()) { if (!elektron_devs[i]->getWorkSpaceKit()) { - DEBUG_PRINTLN(F("could not receive kit")); + // DEBUG_PRINTLN(F("could not receive kit")); save_dev_tracks[i] = false; continue; } @@ -208,7 +208,7 @@ void MCLActions::save_tracks(int row, uint8_t *slot_select_array, uint8_t merge, auto kit = elektron_devs[i]->getCurrentKit(); elektron_devs[i]->saveCurrentKit(kit); if (!elektron_devs[i]->getBlockingKit(kit)) { - DEBUG_PRINTLN(F("could not receive kit")); + // DEBUG_PRINTLN(F("could not receive kit")); save_dev_tracks[i] = false; continue; } @@ -251,7 +251,7 @@ void MCLActions::save_tracks(int row, uint8_t *slot_select_array, uint8_t merge, // Preserve existing link settings before save. if (row_headers[grid_idx].track_type[track_idx] != EMPTY_TRACK_TYPE) { - DEBUG_PRINTLN(F("tl")); + // DEBUG_PRINTLN(F("tl")); if (!grid_track.load_from_grid(track_idx, row)) continue; memcpy(&empty_track.link, &grid_track.link, sizeof(GridLink)); @@ -284,19 +284,24 @@ void MCLActions::save_tracks(int row, uint8_t *slot_select_array, uint8_t merge, } void MCLActions::load_tracks(int row, uint8_t *slot_select_array, - uint8_t *_row_array) { - DEBUG_PRINT_FN(); + uint8_t *_row_array, uint8_t load_mode) { + // DEBUG_PRINT_FN(); ElektronDevice *elektron_devs[2] = { midi_active_peering.get_device(UART1_PORT)->asElektronDevice(), midi_active_peering.get_device(UART2_PORT)->asElektronDevice(), }; - + if (load_mode == 255) { + load_mode = mcl_cfg.load_mode; + } uint8_t row_array[NUM_SLOTS] = {}; uint8_t cache_track_array[NUM_SLOTS] = {}; uint8_t track_idx, dev_idx; bool recache = false; + DEBUG_PRINTLN("load tracks"); + DEBUG_PRINTLN(load_mode); for (uint8_t n = 0; n < NUM_SLOTS; ++n) { GridDeviceTrack *gdt = get_grid_dev_track(n, &track_idx, &dev_idx); + // DEBUG_PRINTLN(slot_select_array[n]); if ((slot_select_array[n] == 0) || (gdt == nullptr)) { continue; } @@ -306,8 +311,9 @@ void MCLActions::load_tracks(int row, uint8_t *slot_select_array, row_array[n] = _row_array[n]; } - if (mcl_cfg.load_mode == LOAD_QUEUE) { - chains[n].add(row, get_chain_length()); + if (load_mode == LOAD_QUEUE) { + chains[n].add(row_array[n], get_chain_length()); + DEBUG_PRINTLN("adding link"); if (chains[n].num_of_links > 1) { slot_select_array[n] = 0; if (chains[n].num_of_links == 2) { @@ -318,11 +324,11 @@ void MCLActions::load_tracks(int row, uint8_t *slot_select_array, } else { chains[n].init(); } - chains[n].mode = mcl_cfg.load_mode; + chains[n].mode = load_mode; } if (MidiClock.state == 2) { - manual_transition(row, slot_select_array); + manual_transition(slot_select_array, row_array); return; } @@ -340,7 +346,8 @@ void MCLActions::load_tracks(int row, uint8_t *slot_select_array, } } -void MCLActions::collect_tracks(int row, uint8_t *slot_select_array) { +void MCLActions::collect_tracks(uint8_t *slot_select_array, + uint8_t *row_array) { uint8_t old_grid = proj.get_grid(); memset(dev_sync_slot, 255, NUM_DEVS); @@ -357,8 +364,9 @@ void MCLActions::collect_tracks(int row, uint8_t *slot_select_array) { slot_select_array[n] = 0; continue; } + uint8_t row = row_array[n]; EmptyTrack empty_track; - DEBUG_PRINTLN(F("cl")); + // DEBUG_PRINTLN(F("cl")); auto *device_track = empty_track.load_from_grid(track_idx, row); if (device_track == nullptr || device_track->active != gdt->track_type) { @@ -369,8 +377,7 @@ void MCLActions::collect_tracks(int row, uint8_t *slot_select_array) { } send_machine[n] = 1; } else { - device_track->transition_cache(track_idx, n); - send_machine[n] = 0; + send_machine[n] = device_track->transition_cache(track_idx, n); dev_sync_slot[dev_idx] = n; } if (device_track) { @@ -381,13 +388,14 @@ void MCLActions::collect_tracks(int row, uint8_t *slot_select_array) { proj.select_grid(old_grid); } -void MCLActions::manual_transition(int row, uint8_t *slot_select_array) { - DEBUG_PRINT_FN(); +void MCLActions::manual_transition(uint8_t *slot_select_array, + uint8_t *row_array) { + // DEBUG_PRINT_FN(); uint8_t q = get_quant(); - DEBUG_CHECK_STACK(); + // DEBUG_CHECK_STACK(); - collect_tracks(row, slot_select_array); + collect_tracks(slot_select_array, row_array); uint16_t next_step = (MidiClock.div16th_counter / q) * q + q; uint8_t loops = 1; @@ -395,17 +403,20 @@ void MCLActions::manual_transition(int row, uint8_t *slot_select_array) { uint8_t track_idx, dev_idx; bool recalc_latency = true; - DEBUG_PRINTLN("manual trans"); + ////DEBUG_PRINTLN("manual trans"); again: + uint8_t row = grid_task.next_active_row; uint16_t div16th_counter = MidiClock.div16th_counter; - if (q == 255) { - for (uint8_t n = 0; n < NUM_SLOTS; n++) { - if (slot_select_array[n] > 0) { + for (uint8_t n = 0; n < NUM_SLOTS; n++) { + + if (slot_select_array[n] > 0) { + row = row_array[n]; + if (q == 255) { GridDeviceTrack *gdt = get_grid_dev_track(n, &track_idx, &dev_idx); if (gdt != nullptr) { transition_level[n] = 0; next_transitions[n] = - div16th_counter - (gdt->seq_track->step_count * + div16th_counter - ((float)gdt->seq_track->step_count * gdt->seq_track->get_speed_multiplier()); links[n].speed = gdt->seq_track->speed; links[n].length = gdt->seq_track->length; @@ -415,21 +426,19 @@ void MCLActions::manual_transition(int row, uint8_t *slot_select_array) { calc_next_slot_transition(n, ignore_chain_settings); grid_page.active_slots[n] = SLOT_PENDING; } - } - } - } else { - for (uint8_t n = 0; n < NUM_SLOTS; n++) { - - if (slot_select_array[n] > 0) { + } else { // transition_level[n] = gridio_param3.getValue(); transition_level[n] = 0; next_transitions[n] = next_step; links[n].row = row; links[n].loops = 1; // if (grid_page.active_slots[n] < 0) { + DEBUG_PRINT("slot man trans "); DEBUG_PRINT(n); DEBUG_PRINT(" "); DEBUG_PRINTLN(next_transitions[n]); grid_page.active_slots[n] = SLOT_PENDING; // } } + } else { + //calc_next_slot_transition(n); } } @@ -443,10 +452,10 @@ void MCLActions::manual_transition(int row, uint8_t *slot_select_array) { calc_latency(); } - DEBUG_PRINTLN("NEXT STEP"); - DEBUG_PRINTLN(next_step); - DEBUG_PRINTLN(next_transition); - DEBUG_PRINTLN(MidiClock.div16th_counter); + // //DEBUG_PRINTLN("NEXT STEP"); + // //DEBUG_PRINTLN(next_step); + // //DEBUG_PRINTLN(next_transition); + // //DEBUG_PRINTLN(MidiClock.div16th_counter); // int32_t pos = next_transition - (div192th_total_latency / 12) - // MidiClock.div16th_counter; next transition should always be at least 2 @@ -457,7 +466,7 @@ void MCLActions::manual_transition(int row, uint8_t *slot_select_array) { if (q == 255) { loops += 1; } else { - DEBUG_PRINTLN("try again"); + // DEBUG_PRINTLN("try again"); next_step += q; } recalc_latency = false; @@ -468,10 +477,10 @@ void MCLActions::manual_transition(int row, uint8_t *slot_select_array) { bool MCLActions::load_track(uint8_t track_idx, uint8_t row, uint8_t pos, GridDeviceTrack *gdt, uint8_t *send_masks) { EmptyTrack empty_track; - auto *ptrack = empty_track.load_from_grid(track_idx, row); + auto *ptrack = empty_track.load_from_grid_512(track_idx, row); if (ptrack == nullptr) { - DEBUG_PRINTLN("bad read"); + // DEBUG_PRINTLN("bad read"); return false; } // read failure @@ -479,13 +488,15 @@ bool MCLActions::load_track(uint8_t track_idx, uint8_t row, uint8_t pos, if (ptrack->active != gdt->track_type) { empty_track.clear(); - DEBUG_PRINTLN("Clearing track"); - DEBUG_PRINTLN(pos); + DEBUG_PRINTLN("Clearing track"); + DEBUG_PRINTLN(pos); ptrack->init_track_type(gdt->track_type); ptrack->init(track_idx, gdt->seq_track); - ptrack->transition_load(track_idx, gdt->seq_track, pos); + ptrack->load_seq_data(gdt->seq_track); } else { + DEBUG_PRINTLN("load immediate track"); ptrack->load_immediate(track_idx, gdt->seq_track); + ptrack->store_in_mem(track_idx); send_masks[pos] = 1; } return true; @@ -493,7 +504,8 @@ bool MCLActions::load_track(uint8_t track_idx, uint8_t row, uint8_t pos, void MCLActions::send_tracks_to_devices(uint8_t *slot_select_array, uint8_t *row_array) { - DEBUG_PRINT_FN(); + // DEBUG_PRINT_FN(); + DEBUG_PRINTLN("send tracks to devices"); uint8_t select_array[NUM_SLOTS]; // Take a copy, because we call GUI.loop later. @@ -511,9 +523,9 @@ void MCLActions::send_tracks_to_devices(uint8_t *slot_select_array, uint8_t track_idx, dev_idx; - DEBUG_PRINTLN("send tracks 1"); - DEBUG_PRINTLN((int)SP); - DEBUG_CHECK_STACK(); + // DEBUG_PRINTLN("send tracks 1"); + // DEBUG_PRINTLN((int)SP); + // DEBUG_CHECK_STACK(); uint8_t last_slot = 255; for (uint8_t i = 0; i < NUM_SLOTS; i++) { @@ -543,8 +555,8 @@ void MCLActions::send_tracks_to_devices(uint8_t *slot_select_array, grid_page.active_slots[i] = row; - DEBUG_DUMP("here"); - DEBUG_DUMP(row); + // DEBUG_DUMP("here"); + // DEBUG_DUMP(row); if (!load_track(track_idx, row, i, gdt, send_masks)) { select_array[i] = 0; @@ -576,8 +588,8 @@ void MCLActions::send_tracks_to_devices(uint8_t *slot_select_array, } else { strcpy(dst, "NEW KIT"); } - DEBUG_PRINTLN("SEND NAME"); - DEBUG_PRINTLN(dst); + // DEBUG_PRINTLN("SEND NAME"); + // DEBUG_PRINTLN(dst); } latency_ms += elektron_dev->sendKitParams(send_masks + i * GRID_WIDTH); } @@ -606,7 +618,7 @@ void MCLActions::send_tracks_to_devices(uint8_t *slot_select_array, // } // Cache - DEBUG_CHECK_STACK(); + // DEBUG_CHECK_STACK(); bool gui_update = false; cache_next_tracks(select_array, gui_update); @@ -640,40 +652,15 @@ void MCLActions::send_tracks_to_devices(uint8_t *slot_select_array, } void MCLActions::cache_track(uint8_t n, uint8_t track_idx, uint8_t dev_idx, - GridDeviceTrack *gdt) { - EmptyTrack empty_track; - DEBUG_CHECK_STACK(); - auto *ptrack = empty_track.load_from_grid(track_idx, links[n].row); - send_machine[n] = 1; - - if (ptrack == nullptr || ptrack->active != gdt->track_type) { - // EMPTY_TRACK_TYPE - DEBUG_PRINTLN(F("clear track")); - empty_track.clear(); - ptrack = empty_track.init_track_type(gdt->track_type); - ptrack->init(track_idx, gdt->seq_track); - } else { - if (!ptrack->get_sound_data_ptr() || !ptrack->get_sound_data_size()) - //something wrong - return; - if (ptrack->memcmp_sound(gdt->mem_slot_idx) != 0) { - ptrack->transition_cache(track_idx, n); - send_machine[n] = 0; - dev_sync_slot[dev_idx] = n; - } - } - if (ptrack == nullptr) { return; } - ptrack->store_in_mem(gdt->mem_slot_idx); - return; -} + GridDeviceTrack *gdt) {} void MCLActions::cache_next_tracks(uint8_t *slot_select_array, bool gui_update) { - DEBUG_PRINT_FN(); + // //DEBUG_PRINT_FN(); - DEBUG_PRINTLN("cache next"); - DEBUG_PRINTLN((int)SP); - DEBUG_CHECK_STACK(); + // //DEBUG_PRINTLN("cache next"); + // //DEBUG_PRINTLN((int)SP); + // DEBUG_CHECK_STACK(); /* while (SeqTrack::in_countdown && (MidiClock.state == 2)) { gui_loop(); @@ -685,9 +672,6 @@ void MCLActions::cache_next_tracks(uint8_t *slot_select_array, memset(dev_sync_slot, 255, sizeof(dev_sync_slot)); - const uint8_t count_max = 8; - uint8_t count = 0; - const uint8_t div32th_margin = 1; uint32_t diff = 0; @@ -695,45 +679,33 @@ void MCLActions::cache_next_tracks(uint8_t *slot_select_array, // div32th_per_second: tempo / 60.0f * 4.0f * 2.0f = tempo * 8 / 60 float div32th_per_second = tempo * 0.133333333333f; // div32th_per_second: tempo / 60.0f * 4.0f * 2.0f * 6.0f = tempo * 8 / 10 - float div192th_per_second = tempo * 0.8f; + // float div192th_per_second = tempo * 0.8f; + // float div192th_time = 1.0 / div192th_per_second; + float div192th_time = 1.25 / tempo; - //Do this in reverse as slot loading in grid task is in reverse - uint8_t n = NUM_SLOTS; - while (n--) { + // float div192th_time = 1.25 / tempo; + // diff * div19th_time > 80ms equivalent to diff > (0.08/1.25) * tempo + float ms = (0.80 / 1.25) * tempo; + + for (uint8_t n = 0; n < NUM_SLOTS; n++) { if (slot_select_array[n] == 0) continue; - count--; GridDeviceTrack *gdt = get_grid_dev_track(n, &track_idx, &dev_idx); uint8_t grid_idx = get_grid_idx(n); if (gdt == nullptr) continue; - bool gui_loop = false; - - if (gui_update && count == 0) { - } - - while ((gdt->seq_track->count_down && (MidiClock.state == 2)) || count == 0) { - gui_loop = false; + uint32_t diff = MidiClock.clock_diff_div192( + MidiClock.div192th_counter, (uint32_t)next_transition * 12 + 4 * 12); + while ((gdt->seq_track->count_down && (MidiClock.state == 2))) { proj.select_grid(old_grid); - MidiUartParent::handle_midi_lock = 1; handleIncomingMidi(); - MidiUartParent::handle_midi_lock = 0; - diff = - MidiClock.clock_less_than(MidiClock.div32th_counter + div32th_margin, - (uint32_t)mcl_actions.next_transition * 2); - if ((float)diff * div32th_per_second < 0.240) { - count = count_max; - if (GUI.currentPage() == &grid_load_page) { - GUI.display(); - } else { + if (((float)diff > 0.08 * 0.8 * tempo) && gui_update) { GUI.loop(); } - } - count = count_max; } proj.select_grid(grid_idx); @@ -754,20 +726,45 @@ void MCLActions::cache_next_tracks(uint8_t *slot_select_array, setLed2(); } } - //if (links[n].row >= GRID_LENGTH) - if (links[n].row >= GRID_LENGTH || links[n].row == grid_page.active_slots[n] || links[n].loops == 0) + + // if (links[n].row >= GRID_LENGTH) + if (links[n].row >= GRID_LENGTH || + links[n].row == grid_page.active_slots[n] || links[n].loops == 0) continue; - cache_track(n, track_idx, dev_idx, gdt); + + EmptyTrack empty_track; + + auto *ptrack = empty_track.load_from_grid_512(track_idx, links[n].row); + send_machine[n] = 1; + + if (ptrack == nullptr || ptrack->active != gdt->track_type) { + // EMPTY_TRACK_TYPE + ////DEBUG_PRINTLN(F("clear track")); + empty_track.clear(); + ptrack = empty_track.init_track_type(gdt->track_type); + ptrack->init(track_idx, gdt->seq_track); + } else { + if (ptrack->get_sound_data_ptr() && ptrack->get_sound_data_size()) { + if (ptrack->memcmp_sound(gdt->mem_slot_idx) != 0) { + ptrack->transition_cache(track_idx, n); + send_machine[n] = 0; + dev_sync_slot[dev_idx] = n; + } + } + } + if (ptrack == nullptr) { + continue; + } + ptrack->store_in_mem(gdt->mem_slot_idx); } - DEBUG_PRINTLN("cache finished"); + // //DEBUG_PRINTLN("cache finished"); proj.select_grid(old_grid); } void MCLActions::calc_next_slot_transition(uint8_t n, bool ignore_chain_settings) { - DEBUG_PRINT_FN(); - // DEBUG_PRINTLN(next_transitions[n]); + // DEBUG_PRINT_FN(); if (!ignore_chain_settings) { switch (chains[n].mode) { @@ -788,6 +785,11 @@ void MCLActions::calc_next_slot_transition(uint8_t n, } } + // next transition[n] already valid, use this. + if (next_transitions[n] != -1 && next_transitions[n] > next_transition) { + return; + } + uint8_t track_idx, dev_idx; GridDeviceTrack *gdt = get_grid_dev_track(n, &track_idx, &dev_idx); @@ -817,8 +819,8 @@ void MCLActions::calc_next_slot_transition(uint8_t n, len++; } - DEBUG_DUMP(len - (uint16_t)(len)); - DEBUG_DUMP(transition_offsets[n]); + // DEBUG_DUMP(len - (uint16_t)(len)); + // DEBUG_DUMP(transition_offsets[n]); next_transitions[n] += (uint16_t)len; // check for overflow and make sure next nearest step is greater than @@ -827,11 +829,16 @@ void MCLActions::calc_next_slot_transition(uint8_t n, (next_transitions[n] < MidiClock.div16th_counter)) { next_transitions[n] += (uint16_t)len; } + + DEBUG_PRINT("slot "); + DEBUG_PRINT(n); + DEBUG_PRINT(" "); + DEBUG_PRINTLN(next_transitions[n]); } void MCLActions::calc_next_transition() { next_transition = (uint16_t)-1; - DEBUG_PRINT_FN(); + // DEBUG_PRINT_FN(); int8_t slot = -1; for (uint8_t n = 0; n < NUM_SLOTS; n++) { if (grid_page.active_slots[n] != SLOT_DISABLED) { @@ -850,10 +857,10 @@ void MCLActions::calc_next_transition() { nearest_beat = next_transition % 4 + 1; // next_transition = next_transition % 16; - DEBUG_PRINTLN(F("current_step")); - DEBUG_PRINTLN(MidiClock.div16th_counter); - DEBUG_PRINTLN(F("nearest step")); - DEBUG_PRINTLN(next_transition); + DEBUG_PRINTLN(F("current_step")); + DEBUG_PRINTLN(MidiClock.div16th_counter); + DEBUG_PRINTLN(F("nearest step")); + DEBUG_PRINTLN(next_transition); } void MCLActions::calc_latency() { @@ -881,8 +888,8 @@ void MCLActions::calc_latency() { uint8_t num_devices = 0; - DEBUG_PRINTLN("calc latency"); - DEBUG_CHECK_STACK(); + // DEBUG_PRINTLN("calc latency"); + // DEBUG_CHECK_STACK(); for (uint8_t n = 0; n < NUM_SLOTS; n++) { if ((grid_page.active_slots[n] == SLOT_DISABLED)) continue; @@ -892,9 +899,10 @@ void MCLActions::calc_latency() { continue; } if (send_machine[n] == 0) { - // uint16_t old_clock = clock; - auto *ptrack = - empty_track.load_from_mem(gdt->mem_slot_idx, gdt->track_type); + // Optimised, assume we dont need to read the entire object to calculate + // latency. + auto *ptrack = empty_track.load_from_mem( + gdt->mem_slot_idx, gdt->track_type, sizeof(GridTrack)); // uint16_t diff = clock_diff(old_clock, clock); if (ptrack == nullptr || !ptrack->is_active() || gdt->track_type != ptrack->active) { @@ -928,10 +936,11 @@ void MCLActions::calc_latency() { float latency_in_seconds = (float)dev_latency[a].latency / bytes_per_second_uart1; // 25ms minimum. if (num_devices == 1) { - latency_in_seconds += .23; + latency_in_seconds += .10; } else if (a == 1) { - latency_in_seconds += .23; + latency_in_seconds += .20; } + // latency_in_seconds += (float) dev_latency[a].load_latency * .0002; dev_latency[a].div32th_latency = @@ -951,9 +960,9 @@ void MCLActions::calc_latency() { div192th_total_latency += dev_latency[a].div192th_latency; } } - DEBUG_PRINTLN("total latency"); - DEBUG_PRINTLN(div32th_total_latency); - DEBUG_PRINTLN(div192th_total_latency); + // DEBUG_PRINTLN("total latency"); + // DEBUG_PRINTLN(div32th_total_latency); + // DEBUG_PRINTLN(div192th_total_latency); } MCLActions mcl_actions; diff --git a/avr/cores/megacommand/MCL/MCLActions.h b/avr/cores/megacommand/MCL/MCLActions.h index 50c975b17..5b0dddd95 100644 --- a/avr/cores/megacommand/MCL/MCLActions.h +++ b/avr/cores/megacommand/MCL/MCLActions.h @@ -100,16 +100,16 @@ class MCLActions : public LinkModeData { void save_tracks(int row, uint8_t *slot_select_array, uint8_t merge, uint8_t readpattern = 255); - void load_tracks(int row, uint8_t *slot_select_array, uint8_t *_row_array = nullptr); + void load_tracks(int row, uint8_t *slot_select_array, uint8_t *_row_array = nullptr, uint8_t load_mode = 255); void send_tracks_to_devices(uint8_t *slot_select_array, uint8_t *row_array = nullptr); - void manual_transition(int row, uint8_t *slot_select_array); + void manual_transition(uint8_t *slot_select_array, uint8_t *row_array); void cache_next_tracks(uint8_t *slot_select_array, bool gui_update = false); void calc_next_slot_transition(uint8_t n, bool ignore_chain_settings = false); void calc_next_transition(); void calc_latency(); private: - void collect_tracks(int row, uint8_t *slot_select_array); + void collect_tracks(uint8_t *slot_select_array, uint8_t *row_array); void cache_track(uint8_t n, uint8_t track_idx, uint8_t dev_idx, GridDeviceTrack *gdt); bool load_track(uint8_t track_idx, uint8_t row, uint8_t pos, GridDeviceTrack *gdt, uint8_t *send_masks); }; diff --git a/avr/cores/megacommand/MCL/MCLActionsEvents.cpp b/avr/cores/megacommand/MCL/MCLActionsEvents.cpp index b430de5c3..09e26785a 100644 --- a/avr/cores/megacommand/MCL/MCLActionsEvents.cpp +++ b/avr/cores/megacommand/MCL/MCLActionsEvents.cpp @@ -10,7 +10,9 @@ void MCLActionsMidiEvents::onProgramChangeCallback_Midi2(uint8_t *msg) { (mcl_cfg.uart2_prg_in == MIDI_OMNI_MODE)) { if (mcl_cfg.uart2_prg_mode == 0) { - grid_task.load_row = msg[1]; + uint8_t track_select_array[NUM_SLOTS] = {}; + grid_load_page.track_select_array_from_type_select(track_select_array); + grid_task.load_queue.put(mcl_cfg.load_mode,msg[1],track_select_array); } else { DEBUG_PRINTLN("set row"); grid_task.midi_row_select = msg[1]; @@ -53,7 +55,7 @@ void MCLActionsMidiEvents::onNoteOnCallback_Midi2(uint8_t *msg) { DEBUG_PRINT(slot); DEBUG_PRINT(" "); DEBUG_PRINTLN(grid_task.midi_row_select); - grid_task.midi_track_select[slot] = grid_task.midi_row_select; + grid_task.load_track_select[slot] = grid_task.midi_row_select; } } @@ -72,9 +74,8 @@ void MCLActionsMidiEvents::onNoteOffCallback_Midi2(uint8_t *msg) { CLEAR_BIT32(slot_mask, slot); if (slot_mask == 0) { - grid_task.midi_event_clock = slowclock; - DEBUG_PRINTLN("setting slow clock"); - grid_task.midi_load = true; + grid_task.load_queue.put(mcl_cfg.load_mode,grid_task.load_track_select); + memset(grid_task.load_track_select, 255, sizeof(grid_task.load_track_select)); } } } @@ -157,6 +158,11 @@ void MCLActionsMidiEvents::setup_callbacks() { (midi_callback_ptr_t)&MCLActionsMidiEvents:: onProgramChangeCallback_Midi2); + MidiUSB.addOnNoteOnCallback( + this, (midi_callback_ptr_t)&MCLActionsMidiEvents::onNoteOnCallback_Midi2); + MidiUSB.addOnNoteOffCallback( + this, + (midi_callback_ptr_t)&MCLActionsMidiEvents::onNoteOffCallback_Midi2); Midi2.addOnNoteOnCallback( this, (midi_callback_ptr_t)&MCLActionsMidiEvents::onNoteOnCallback_Midi2); Midi2.addOnNoteOffCallback( @@ -182,7 +188,11 @@ void MCLActionsMidiEvents::remove_callbacks() { Midi2.removeOnProgramChangeCallback( this, (midi_callback_ptr_t)&MCLActionsMidiEvents:: onProgramChangeCallback_Midi2); - + MidiUSB.removeOnNoteOnCallback( + this, (midi_callback_ptr_t)&MCLActionsMidiEvents::onNoteOnCallback_Midi2); + MidiUSB.removeOnNoteOffCallback( + this, + (midi_callback_ptr_t)&MCLActionsMidiEvents::onNoteOffCallback_Midi2); Midi2.removeOnNoteOnCallback( this, (midi_callback_ptr_t)&MCLActionsMidiEvents::onNoteOnCallback_Midi2); Midi2.removeOnNoteOffCallback( diff --git a/avr/cores/megacommand/MCL/MCLClipBoard.cpp b/avr/cores/megacommand/MCL/MCLClipBoard.cpp index 9132aaf83..938b726d1 100644 --- a/avr/cores/megacommand/MCL/MCLClipBoard.cpp +++ b/avr/cores/megacommand/MCL/MCLClipBoard.cpp @@ -35,8 +35,7 @@ bool MCLClipBoard::open() { if (!grids[i].open_file(grid_filename)) { DEBUG_PRINTLN(F("Could not open clipboard")); return false; - } - else { + } else { DEBUG_PRINTLN(F("Opened Clipboard")); } } @@ -59,6 +58,7 @@ bool MCLClipBoard::copy_sequencer(uint8_t offset) { return false; } } + copy_sequencer_track(MDFX_TRACK_NUM + NUM_MD_TRACKS); } else { for (uint8_t n = 0; n < NUM_EXT_TRACKS; n++) { if (!copy_sequencer_track(n + NUM_MD_TRACKS)) { @@ -71,34 +71,29 @@ bool MCLClipBoard::copy_sequencer(uint8_t offset) { bool MCLClipBoard::copy_sequencer_track(uint8_t track) { DEBUG_PRINT_FN(); - bool ret; - EmptyTrack temp_track; - - MDTrack *md_track = (MDTrack *)(&temp_track); - ExtTrack *ext_track = (ExtTrack *)(&temp_track); - uint8_t grid = 0; + bool ret = false; + EmptyTrack empty_track; if (!open()) { DEBUG_PRINTLN(F("error could not open clipboard")); return false; } + uint8_t track_idx, dev_idx; + GridDeviceTrack *gdt = mcl_actions.get_grid_dev_track(track, &track_idx, &dev_idx); + if (gdt == nullptr) { return false; } - if (track < NUM_MD_TRACKS) { - memcpy(md_track->seq_data.data(), mcl_seq.md_tracks[track].data(), - sizeof(md_track->seq_data)); - md_track->get_machine_from_kit(track); - md_track->link.length = mcl_seq.md_tracks[track].length; - md_track->link.speed = mcl_seq.md_tracks[track].speed; - ret = grids[grid].write(&temp_track, sizeof(MDTrack), track, GRID_LENGTH); - } - else { - uint8_t n = track - NUM_MD_TRACKS; - memcpy(ext_track->seq_data.data(), mcl_seq.ext_tracks[n].data(), - sizeof(ext_track->seq_data)); - ext_track->link.length = mcl_seq.ext_tracks[n].length; - ext_track->link.speed = mcl_seq.ext_tracks[n].speed; - ret = grids[grid].write(&temp_track, sizeof(ExtTrack), track, GRID_LENGTH); - } + uint8_t grid_idx = mcl_actions.get_grid_idx(track); + + auto device_track = + ((DeviceTrack *)&empty_track)->init_track_type(gdt->track_type); + if (device_track == nullptr) { goto end; } + + bool merge = false; + bool online = true; + ret = device_track->store_in_grid(track_idx, GRID_LENGTH, gdt->seq_track, merge, + online, &grids[grid_idx]); + + end: close(); if (!ret) { DEBUG_PRINTLN(F("failed write")); @@ -112,6 +107,7 @@ bool MCLClipBoard::paste_sequencer(uint8_t offset) { if (!paste_sequencer_track(n, n)) { return false; } + paste_sequencer_track(MDFX_TRACK_NUM + NUM_MD_TRACKS, MDFX_TRACK_NUM + NUM_MD_TRACKS); } } else { for (uint8_t n = 0; n < NUM_EXT_TRACKS; n++) { @@ -131,55 +127,50 @@ bool MCLClipBoard::paste_sequencer_track(uint8_t source_track, uint8_t track) { MDTrack *md_track = (MDTrack *)(&temp_track); ExtTrack *ext_track = (ExtTrack *)(&temp_track); - uint8_t grid = 0; + uint8_t source_track_idx, track_idx, dev_idx; + + GridDeviceTrack *gdt = mcl_actions.get_grid_dev_track(source_track, &source_track_idx, &dev_idx); + if (gdt == nullptr) { return false; } + + gdt = mcl_actions.get_grid_dev_track(track, &track_idx, &dev_idx); + if (gdt == nullptr) { return false; } + + uint8_t grid_idx = mcl_actions.get_grid_idx(track); + if (!open()) { DEBUG_PRINTLN(F("error could not open clipboard")); return false; } - if (source_track < NUM_MD_TRACKS) { - ret = grids[grid].read(&temp_track, sizeof(MDTrack), source_track, GRID_LENGTH); - } else { - ret = grids[grid].read(&temp_track, sizeof(ExtTrack), source_track, - GRID_LENGTH); - } - if (!ret) { - DEBUG_PRINTLN(F("failed read")); + + auto *device_track = + temp_track.load_from_grid_512(source_track_idx, GRID_LENGTH, &grids[grid_idx]); + + if (device_track == nullptr) { close(); return false; } - if (source_track < NUM_MD_TRACKS) { - DEBUG_PRINTLN(F("loading seq track")); - memcpy(mcl_seq.md_tracks[track].data(), md_track->seq_data.data(), - sizeof(md_track->seq_data)); - mcl_seq.md_tracks[track].set_length(md_track->link.length); - mcl_seq.md_tracks[track].set_speed(md_track->link.speed, md_track->link.speed, false); + DEBUG_PRINTLN("getting ready to paste"); + device_track->paste_track(source_track_idx, track_idx, gdt->seq_track); - if (md_track->machine.trigGroup == source_track) { - md_track->machine.trigGroup = 255; - } - if (md_track->machine.muteGroup == source_track) { - md_track->machine.muteGroup = 255; + MidiDevice *devs[2] = { + midi_active_peering.get_device(UART1_PORT), + midi_active_peering.get_device(UART2_PORT), + }; + if (devs[0] == &MD && track_idx == last_md_track) { + if (GUI.currentPage() == &seq_step_page) { + seq_step_page.config(); } - if (md_track->machine.lfo.destinationTrack == source_track) { - md_track->machine.lfo.destinationTrack = track; + if (GUI.currentPage() == &seq_ptc_page) { + seq_ptc_page.config(); } - DEBUG_PRINTLN(F("sending seq track")); - bool send_machine = true; - bool send_level = true; - MD.sendMachine(track, &(md_track->machine), send_level, send_machine); - } - else { - memcpy(mcl_seq.ext_tracks[track - NUM_MD_TRACKS].data(), ext_track->seq_data.data(), - sizeof(ext_track->seq_data)); - mcl_seq.ext_tracks[track - NUM_MD_TRACKS].length = ext_track->link.length; - mcl_seq.ext_tracks[track - NUM_MD_TRACKS].speed = ext_track->link.speed; } close(); return true; } -bool MCLClipBoard::copy(uint16_t col, uint16_t row, uint16_t w, uint16_t h, uint8_t grid) { +bool MCLClipBoard::copy(uint16_t col, uint16_t row, uint16_t w, uint16_t h, + uint8_t grid) { DEBUG_PRINT_FN(); uint8_t old_grid = proj.get_grid(); t_col = col; @@ -200,12 +191,15 @@ bool MCLClipBoard::copy(uint16_t col, uint16_t row, uint16_t w, uint16_t h, uint proj.read_grid_row_header(&header, y + row); ret = grids[grid].write_row_header(&header, y + row); DEBUG_PRINTLN(header.name); - if (h > 8) { mcl_gui.draw_progress("", y, h); } + if (h > 8) { + mcl_gui.draw_progress("", y, h); + } for (int x = 0; x < w; x++) { ret = proj.read_grid(&temp_track, sizeof(temp_track), x + col, y + row); DEBUG_DUMP(temp_track.active); if (ret) { - ret = grids[grid].write(&temp_track, sizeof(temp_track), x + col, y + row); + ret = grids[grid].write(&temp_track, sizeof(temp_track), x + col, + y + row); } } } @@ -219,7 +213,7 @@ bool MCLClipBoard::paste(uint16_t col, uint16_t row, uint8_t grid) { DEBUG_PRINTLN(F("error could not open clipboard")); return false; } - DEBUG_PRINTLN("paste here"); + DEBUG_PRINTLN("paste here"); bool destination_same = (col == t_col); if (t_w == 1) { destination_same = true; @@ -240,13 +234,14 @@ bool MCLClipBoard::paste(uint16_t col, uint16_t row, uint8_t grid) { grids[grid].read_row_header(&header_copy, y + t_row); header.active = true; if (header_copy.active) { - strncpy(&(header.name[0]), &(header_copy.name[0]), sizeof(header.name)); - } - else { - header.name[0] = '\0'; + strncpy(&(header.name[0]), &(header_copy.name[0]), sizeof(header.name)); + } else { + header.name[0] = '\0'; } } - if (t_h > 8) { mcl_gui.draw_progress("", y, t_h); } + if (t_h > 8) { + mcl_gui.draw_progress("", y, t_h); + } for (int x = 0; x < t_w && x + col < GRID_WIDTH; x++) { // track now has full data and correct type @@ -257,15 +252,18 @@ bool MCLClipBoard::paste(uint16_t col, uint16_t row, uint8_t grid) { grids[grid].read(&empty_track, sizeof(EmptyTrack), s_col, y + t_row); - DeviceTrack *ptrack = ((DeviceTrack*) &empty_track)->init_track_type(empty_track.active); + DeviceTrack *ptrack = + ((DeviceTrack *)&empty_track)->init_track_type(empty_track.active); DEBUG_DUMP(ptrack->active); - GridDeviceTrack *gdt = mcl_actions.get_grid_dev_track(slot_n, &track_idx, &dev_idx); + GridDeviceTrack *gdt = + mcl_actions.get_grid_dev_track(slot_n, &track_idx, &dev_idx); - if ((gdt == nullptr || gdt->track_type != ptrack->active) && (ptrack->active != EMPTY_TRACK_TYPE)) { - DEBUG_PRINTLN("track not supported"); - //Don't allow paste in to unsupported slots - continue; + if ((gdt == nullptr || gdt->track_type != ptrack->active) && + (ptrack->active != EMPTY_TRACK_TYPE)) { + DEBUG_PRINTLN("track not supported"); + // Don't allow paste in to unsupported slots + continue; } int16_t link_row_offset = ptrack->link.row - t_row; @@ -276,8 +274,7 @@ bool MCLClipBoard::paste(uint16_t col, uint16_t row, uint8_t grid) { new_link_row = y + row; } ptrack->link.row = new_link_row; - header.update_model(d_col, ptrack->get_model(), - ptrack->active); + header.update_model(d_col, ptrack->get_model(), ptrack->active); ptrack->on_copy(s_col, d_col, destination_same); ptrack->store_in_grid(d_col, y + row); } diff --git a/avr/cores/megacommand/MCL/MCLSd.cpp b/avr/cores/megacommand/MCL/MCLSd.cpp index 51e35d924..e705f9326 100644 --- a/avr/cores/megacommand/MCL/MCLSd.cpp +++ b/avr/cores/megacommand/MCL/MCLSd.cpp @@ -3,7 +3,7 @@ /* Function for initialising the SD Card */ -SdFat SD; +SdFat SD __attribute__((section(".sdcard"))); bool MCLSd::sd_init() { bool ret = false; @@ -12,7 +12,7 @@ bool MCLSd::sd_init() { // File file("/test.mcl",O_WRITE); /*Configuration file used to store settings when Minicommand is turned off*/ for (uint8_t n = 0; n < SD_MAX_RETRIES && ret == false; n++) { - ret = SD.begin(SD_CS, SPI_FULL_SPEED); + ret = SD.begin(SdSpiConfig(SD_CS, DEDICATED_SPI, SD_SCK_MHZ(50))); if (!ret) { delay(50); } @@ -93,7 +93,7 @@ bool MCLSd::load_init() { return false; } -bool MCLSd::seek(uint32_t pos, FatFile *filep) { +bool MCLSd::seek(uint32_t pos, File *filep) { bool pass = false; bool ret; for (uint8_t n = 0; n < SD_MAX_RETRIES; n++) { @@ -101,7 +101,7 @@ bool MCLSd::seek(uint32_t pos, FatFile *filep) { if (!filep) { DEBUG_PRINTLN(F("huh")); } ret = filep->seekSet(pos); if (!ret) { - SD.cardBegin(SD_CS, SPI_FULL_SPEED); + //SD.cardBegin(SD_CS, SPI_FULL_SPEED); //oled_display.textbox("SEEK RETRY", ""); //oled_display.display(); DEBUG_PRINTLN("seek retry"); @@ -115,7 +115,7 @@ bool MCLSd::seek(uint32_t pos, FatFile *filep) { return pass; } -bool MCLSd::write_data(void *data, size_t len, FatFile *filep) { +bool MCLSd::write_data(void *data, size_t len, File *filep) { size_t b; bool pass = false; @@ -125,7 +125,7 @@ bool MCLSd::write_data(void *data, size_t len, FatFile *filep) { for (uint8_t n = 0; n < SD_MAX_RETRIES; n++) { if (n > 0) { DEBUG_PRINTLN("write retry"); - SD.cardBegin(SD_CS, SPI_FULL_SPEED); + //SD.cardBegin(SD_CS, SPI_FULL_SPEED); //oled_display.textbox("WRITE RETRY", ""); //oled_display.display(); delay(20); @@ -149,7 +149,7 @@ bool MCLSd::write_data(void *data, size_t len, FatFile *filep) { /* Function for reading from the project file */ -bool MCLSd::read_data(void *data, size_t len, FatFile *filep) { +bool MCLSd::read_data(void *data, size_t len, File *filep) { size_t b; bool ret; @@ -159,7 +159,7 @@ bool MCLSd::read_data(void *data, size_t len, FatFile *filep) { for (uint8_t n = 0; n < SD_MAX_RETRIES; n++) { if (n > 0) { DEBUG_PRINTLN("read retry"); - SD.cardBegin(SD_CS, SPI_FULL_SPEED); + //SD.cardBegin(SD_CS, SPI_FULL_SPEED); //oled_display.textbox("READ RETRY", ""); //oled_display.display(); delay(20); diff --git a/avr/cores/megacommand/MCL/MCLSd.h b/avr/cores/megacommand/MCL/MCLSd.h index 9fef72a1f..fb5a101e4 100644 --- a/avr/cores/megacommand/MCL/MCLSd.h +++ b/avr/cores/megacommand/MCL/MCLSd.h @@ -16,24 +16,24 @@ class MCLSd { bool sd_state = false; bool sd_init(); bool load_init(); - bool seek(uint32_t pos, FatFile *filep); - bool read_data(void *data, size_t len, FatFile *filep); - bool write_data(void *data, size_t len, FatFile *filep); + bool seek(uint32_t pos, File *filep); + bool read_data(void *data, size_t len, File *filep); + bool write_data(void *data, size_t len, File *filep); /// read data from SD card and repair vtable - template bool read_data_v(T *data, FatFile *filep) { + template bool read_data_v(T *data, File *filep) { auto ret = read_data(data, sizeof(T), filep); ::new(data)T; return ret; } /// Specialization for ElektronPattern... - template bool read_data_v_noinit(T *data, FatFile *filep) { + template bool read_data_v_noinit(T *data, File *filep) { auto ret = read_data(data, sizeof(T), filep); ::new(data)T(false); return ret; } /// save data to SD card, including the vtable - template bool write_data_v(T *data, FatFile *filep) { + template bool write_data_v(T *data, File *filep) { return write_data(data, sizeof(T), filep); } diff --git a/avr/cores/megacommand/MCL/MCLSysConfig.cpp b/avr/cores/megacommand/MCL/MCLSysConfig.cpp index 0893ebd90..742f275e4 100644 --- a/avr/cores/megacommand/MCL/MCLSysConfig.cpp +++ b/avr/cores/megacommand/MCL/MCLSysConfig.cpp @@ -1,5 +1,12 @@ #include "MCL_impl.h" +void usb_wait() { + oled_display.clearDisplay(); + oled_display.textbox("PLEASE WAIT", ""); + oled_display.display(); + delay(4000); +} + bool megacmd_check() { if (!IS_MEGACMD()) { oled_display.textbox("MODE ", "N/A"); @@ -10,15 +17,17 @@ bool megacmd_check() { } void usb_os_update() { + usb_wait(); + change_usb_mode(USB_SERIAL); oled_display.clearDisplay(); oled_display.textbox("OS UPDATE", ""); oled_display.display(); - change_usb_mode(USB_SERIAL); while (1) ; } void usb_dfu_mode() { + usb_wait(); oled_display.clearDisplay(); oled_display.textbox("DFU ", "MODE"); oled_display.display(); @@ -32,7 +41,7 @@ void usb_disk_mode() { if (!megacmd_check()) { return; } - + usb_wait(); oled_display.clearDisplay(); oled_display.textbox("USB ", "DISK"); diff --git a/avr/cores/megacommand/MCL/MDFXTrack.cpp b/avr/cores/megacommand/MCL/MDFXTrack.cpp index b8dce88c6..ad234cf96 100644 --- a/avr/cores/megacommand/MCL/MDFXTrack.cpp +++ b/avr/cores/megacommand/MCL/MDFXTrack.cpp @@ -1,5 +1,11 @@ #include "MCL_impl.h" +void MDFXTrack::paste_track(uint8_t src_track, uint8_t dest_track, + SeqTrack *seq_track) { + load_link_data(seq_track); + send_fx(true); +} + void MDFXTrack::transition_send(uint8_t tracknumber, uint8_t slotnumber) { send_fx(); } @@ -76,7 +82,7 @@ void MDFXTrack::get_fx_from_kit() { } bool MDFXTrack::store_in_grid(uint8_t column, uint16_t row, SeqTrack *seq_track, - uint8_t merge, bool online) { + uint8_t merge, bool online, Grid *grid) { active = MDFX_TRACK_TYPE; bool ret; int b = 0; @@ -93,8 +99,7 @@ bool MDFXTrack::store_in_grid(uint8_t column, uint16_t row, SeqTrack *seq_track, len = sizeof(MDFXTrack); DEBUG_PRINTLN(len); - - ret = proj.write_grid((uint8_t *)(this), len, column, row); + ret = write_grid((uint8_t *)(this), len, column, row, grid); if (!ret) { DEBUG_PRINTLN(F("write failed")); diff --git a/avr/cores/megacommand/MCL/MDFXTrack.h b/avr/cores/megacommand/MCL/MDFXTrack.h index 7d25b2b18..4b044c912 100644 --- a/avr/cores/megacommand/MCL/MDFXTrack.h +++ b/avr/cores/megacommand/MCL/MDFXTrack.h @@ -41,9 +41,9 @@ class MDFXTrack : public AUXTrack, public MDFXData { uint16_t calc_latency(uint8_t tracknumber); uint16_t send_fx(bool send = true); void transition_send(uint8_t tracknumber, uint8_t slotnumber); - + void paste_track(uint8_t src_track, uint8_t dest_track, SeqTrack *seq_track); bool store_in_grid(uint8_t column, uint16_t row, SeqTrack *seq_track = nullptr, - uint8_t merge = 0, bool online = false); + uint8_t merge = 0, bool online = false, Grid *grid = nullptr); void load_immediate(uint8_t tracknumber, SeqTrack *seq_track); diff --git a/avr/cores/megacommand/MCL/MDLFOTrack.cpp b/avr/cores/megacommand/MCL/MDLFOTrack.cpp index 632633787..8b83d4872 100644 --- a/avr/cores/megacommand/MCL/MDLFOTrack.cpp +++ b/avr/cores/megacommand/MCL/MDLFOTrack.cpp @@ -6,15 +6,16 @@ void MDLFOTrack::transition_load(uint8_t tracknumber, SeqTrack *seq_track, GridTrack::transition_load(tracknumber, seq_track, slotnumber); memcpy(mcl_seq.lfo_tracks[0].data(), lfo_data.data(), sizeof(LFOSeqTrackData)); + load_link_data(seq_track); } uint16_t MDLFOTrack::calc_latency(uint8_t tracknumber) { return 0; } void MDLFOTrack::load_immediate(uint8_t tracknumber, SeqTrack *seq_track) { - load_link_data(seq_track); memcpy(mcl_seq.lfo_tracks[0].data(), lfo_data.data(), sizeof(LFOSeqTrackData)); - + DEBUG_PRINTLN("MD LFO LOAD IMMI"); + load_link_data(seq_track); DEBUG_PRINTLN("Load speed"); DEBUG_PRINTLN(lfo_data.speed); } @@ -28,7 +29,7 @@ void MDLFOTrack::get_lfos() { bool MDLFOTrack::store_in_grid(uint8_t column, uint16_t row, SeqTrack *seq_track, uint8_t merge, - bool online) { + bool online, Grid *grid) { active = MDLFO_TRACK_TYPE; bool ret; int b = 0; @@ -42,8 +43,7 @@ bool MDLFOTrack::store_in_grid(uint8_t column, uint16_t row, len = sizeof(MDLFOTrack); DEBUG_PRINTLN(len); - - ret = proj.write_grid((uint8_t *)(this), len, column, row); + ret = write_grid((uint8_t *)(this), len, column, row, grid); if (!ret) { DEBUG_PRINTLN(F("write failed")); diff --git a/avr/cores/megacommand/MCL/MDLFOTrack.h b/avr/cores/megacommand/MCL/MDLFOTrack.h index 0e66f1778..ccb3e8d9c 100644 --- a/avr/cores/megacommand/MCL/MDLFOTrack.h +++ b/avr/cores/megacommand/MCL/MDLFOTrack.h @@ -26,7 +26,7 @@ class MDLFOTrack : public AUXTrack { uint8_t slotnumber); bool store_in_grid(uint8_t column, uint16_t row, SeqTrack *seq_track = nullptr, uint8_t merge = 0, - bool online = false); + bool online = false, Grid *grid = nullptr); void load_immediate(uint8_t tracknumber, SeqTrack *seq_track); diff --git a/avr/cores/megacommand/MCL/MDRouteTrack.cpp b/avr/cores/megacommand/MCL/MDRouteTrack.cpp index e6414e794..6797a46a7 100644 --- a/avr/cores/megacommand/MCL/MDRouteTrack.cpp +++ b/avr/cores/megacommand/MCL/MDRouteTrack.cpp @@ -41,7 +41,7 @@ void MDRouteTrack::get_routes() { bool MDRouteTrack::store_in_grid(uint8_t column, uint16_t row, SeqTrack *seq_track, uint8_t merge, - bool online) { + bool online, Grid *grid) { active = MDROUTE_TRACK_TYPE; bool ret; int b = 0; @@ -58,8 +58,7 @@ bool MDRouteTrack::store_in_grid(uint8_t column, uint16_t row, len = sizeof(MDRouteTrack); DEBUG_PRINTLN(len); - - ret = proj.write_grid((uint8_t *)(this), len, column, row); + ret = write_grid((uint8_t *)(this), len, column, row, grid); if (!ret) { DEBUG_PRINTLN(F("write failed")); diff --git a/avr/cores/megacommand/MCL/MDRouteTrack.h b/avr/cores/megacommand/MCL/MDRouteTrack.h index efa2d2e7f..6b0cfeb53 100644 --- a/avr/cores/megacommand/MCL/MDRouteTrack.h +++ b/avr/cores/megacommand/MCL/MDRouteTrack.h @@ -30,7 +30,7 @@ class MDRouteTrack : public AUXTrack, public RouteData { uint8_t slotnumber); bool store_in_grid(uint8_t column, uint16_t row, SeqTrack *seq_track = nullptr, uint8_t merge = 0, - bool online = false); + bool online = false, Grid *grid = nullptr); void load_immediate(uint8_t tracknumber, SeqTrack *seq_track); void load_routes(); diff --git a/avr/cores/megacommand/MCL/MDSeqTrack.cpp b/avr/cores/megacommand/MCL/MDSeqTrack.cpp index ce510a7cb..3b0867139 100644 --- a/avr/cores/megacommand/MCL/MDSeqTrack.cpp +++ b/avr/cores/megacommand/MCL/MDSeqTrack.cpp @@ -74,19 +74,17 @@ void MDSeqTrack::re_sync() { } void MDSeqTrack::load_cache() { - MDTrackChunk t; - +/* MDTrackChunk t; + DEBUG_PRINTLN("lc"); for (uint8_t n = 0; n < t.get_chunk_count(); n++) { t.load_from_mem_chunk(track_number, n); t.load_chunk(data(), n); - } + }*/ - t.load_link_from_mem(track_number); - t.load_link_data((SeqTrack *)this); - - oneshot_mask = 0; -// set_length(length); - update_params(); + MDTrack t; + t.load_from_mem(track_number, MD_TRACK_TYPE); + MD.insertMachineInKit(track_number, &(t.machine),false); + t.load_seq_data((SeqTrack *)this); SET_BIT16(load_machine_cache, track_number); } @@ -226,6 +224,7 @@ void MDSeqTrack::reset_params() { } } if (re_assign) { + DEBUG_PRINTLN("re-assign machine"); MD.assignMachineBulk(track_number, &md_track.machine, 255, 1, true); } } diff --git a/avr/cores/megacommand/MCL/MDTempoTrack.cpp b/avr/cores/megacommand/MCL/MDTempoTrack.cpp index fe7738190..cd14a438d 100644 --- a/avr/cores/megacommand/MCL/MDTempoTrack.cpp +++ b/avr/cores/megacommand/MCL/MDTempoTrack.cpp @@ -33,7 +33,7 @@ void MDTempoTrack::get_tempo() { bool MDTempoTrack::store_in_grid(uint8_t column, uint16_t row, SeqTrack *seq_track, uint8_t merge, - bool online) { + bool online, Grid *grid) { active = MDTEMPO_TRACK_TYPE; bool ret; int b = 0; @@ -51,7 +51,7 @@ bool MDTempoTrack::store_in_grid(uint8_t column, uint16_t row, len = sizeof(MDTempoTrack); DEBUG_PRINTLN(len); - ret = proj.write_grid((uint8_t *)(this), len, column, row); + ret = write_grid((uint8_t *)(this), len, column, row, grid); if (!ret) { DEBUG_PRINTLN(F("write failed")); diff --git a/avr/cores/megacommand/MCL/MDTempoTrack.h b/avr/cores/megacommand/MCL/MDTempoTrack.h index da04cb4a8..b97487909 100644 --- a/avr/cores/megacommand/MCL/MDTempoTrack.h +++ b/avr/cores/megacommand/MCL/MDTempoTrack.h @@ -24,7 +24,7 @@ class MDTempoTrack : public AUXTrack, public TempoData { void transition_send(uint8_t tracknumber, uint8_t slotnumber); bool store_in_grid(uint8_t column, uint16_t row, SeqTrack *seq_track = nullptr, uint8_t merge = 0, - bool online = false); + bool online = false, Grid *grid = nullptr); void load_immediate(uint8_t tracknumber, SeqTrack *seq_track); diff --git a/avr/cores/megacommand/MCL/MDTrack.cpp b/avr/cores/megacommand/MCL/MDTrack.cpp index 6a188d832..054e935c7 100644 --- a/avr/cores/megacommand/MCL/MDTrack.cpp +++ b/avr/cores/megacommand/MCL/MDTrack.cpp @@ -1,5 +1,23 @@ #include "MCL_impl.h" +void MDTrack::paste_track(uint8_t src_track, uint8_t dest_track, + SeqTrack *seq_track) { + DEBUG_PRINTLN(F("paste seq track")); + if (machine.trigGroup == src_track) { + machine.trigGroup = 255; + } + if (machine.muteGroup == src_track) { + machine.muteGroup = 255; + } + if (machine.lfo.destinationTrack == src_track) { + machine.lfo.destinationTrack = dest_track; + } + load_immediate(dest_track, seq_track); + bool send_machine = true; + bool send_level = true; + MD.sendMachine(dest_track, &(machine), send_level, send_machine); +} + uint16_t MDTrack::calc_latency(uint8_t tracknumber) { uint8_t n = tracknumber; uint16_t md_latency = 0; @@ -13,7 +31,7 @@ uint16_t MDTrack::calc_latency(uint8_t tracknumber) { return md_latency; } -void MDTrack::transition_cache(uint8_t tracknumber, uint8_t slotnumber) { +bool MDTrack::transition_cache(uint8_t tracknumber, uint8_t slotnumber) { uint8_t n = slotnumber; bool send_level = false; bool send = true; @@ -25,12 +43,13 @@ void MDTrack::transition_cache(uint8_t tracknumber, uint8_t slotnumber) { break; } MD.sendMachineCache(tracknumber, &(machine), send_level, send); + return true; } void MDTrack::transition_send(uint8_t tracknumber, uint8_t slotnumber) { uint8_t n = slotnumber; bool send_level = false; - DEBUG_DUMP(n); + DEBUG_PRINTLN("transition send"); switch (mcl_actions.transition_level[n]) { case TRANSITION_UNMUTE: DEBUG_PRINTLN(F("unmuting")); @@ -45,19 +64,18 @@ void MDTrack::transition_send(uint8_t tracknumber, uint8_t slotnumber) { default: break; } - } void MDTrack::transition_load(uint8_t tracknumber, SeqTrack *seq_track, uint8_t slotnumber) { GridTrack::transition_load(tracknumber, seq_track, slotnumber); - //load_seq_data(seq_track); + // load_seq_data(seq_track); } void MDTrack::load_immediate(uint8_t tracknumber, SeqTrack *seq_track) { + DEBUG_PRINTLN("load immediate"); MD.insertMachineInKit(tracknumber, &(machine)); load_seq_data(seq_track); - store_in_mem(tracknumber); } void MDTrack::get_machine_from_kit(uint8_t tracknumber) { @@ -66,7 +84,8 @@ void MDTrack::get_machine_from_kit(uint8_t tracknumber) { machine.track = tracknumber; machine.level = MD.kit.levels[tracknumber]; - machine.model = MD.kit.models[tracknumber]; //get_raw_model including tonal bit + machine.model = + MD.kit.models[tracknumber]; // get_raw_model including tonal bit /*Check to see if LFO is modulating host track*/ /*IF it is then we need to make sure that the LFO destination is updated to @@ -82,7 +101,6 @@ void MDTrack::get_machine_from_kit(uint8_t tracknumber) { machine.trigGroup = MD.kit.trigGroups[tracknumber]; machine.muteGroup = MD.kit.muteGroups[tracknumber]; - } void MDTrack::init() { @@ -131,7 +149,7 @@ void MDTrack::normalize() { } bool MDTrack::store_in_grid(uint8_t column, uint16_t row, SeqTrack *seq_track, - uint8_t merge, bool online) { + uint8_t merge, bool online, Grid *grid) { active = MD_TRACK_TYPE; bool ret; @@ -183,7 +201,7 @@ bool MDTrack::store_in_grid(uint8_t column, uint16_t row, SeqTrack *seq_track, len = sizeof(MDTrack); DEBUG_PRINTLN(len); - ret = proj.write_grid((uint8_t *)(this), len, column, row); + ret = write_grid((uint8_t *)(this), len, column, row, grid); if (!ret) { DEBUG_PRINTLN(F("write failed")); diff --git a/avr/cores/megacommand/MCL/MDTrack.h b/avr/cores/megacommand/MCL/MDTrack.h index 10aeeaae1..ca72ceb31 100644 --- a/avr/cores/megacommand/MCL/MDTrack.h +++ b/avr/cores/megacommand/MCL/MDTrack.h @@ -77,10 +77,9 @@ class MDTrack : public DeviceTrack { static_assert(sizeof(MDTrack) <= GRID1_TRACK_LEN); } void init(); - void clear_track(); uint16_t calc_latency(uint8_t tracknumber); - void transition_cache(uint8_t tracknumber, uint8_t slotnumber); + bool transition_cache(uint8_t tracknumber, uint8_t slotnumber); void transition_send(uint8_t tracknumber, uint8_t slotnumber); void transition_load(uint8_t tracknumber, SeqTrack *seq_track, uint8_t slotnumber); @@ -90,9 +89,9 @@ class MDTrack : public DeviceTrack { bool store_in_grid(uint8_t column, uint16_t row, SeqTrack *seq_track = nullptr, uint8_t merge = 0, - bool online = false); + bool online = false, Grid *grid = nullptr); void load_immediate(uint8_t tracknumber, SeqTrack *seq_track); - + void paste_track(uint8_t src_track, uint8_t dest_track, SeqTrack *seq_track); // scale machine track vol by percentage void scale_vol(float scale); diff --git a/avr/cores/megacommand/MCL/MNMTrack.cpp b/avr/cores/megacommand/MCL/MNMTrack.cpp index b3a11121e..6b6ebb28e 100644 --- a/avr/cores/megacommand/MCL/MNMTrack.cpp +++ b/avr/cores/megacommand/MCL/MNMTrack.cpp @@ -27,7 +27,6 @@ void MNMTrack::load_immediate(uint8_t tracknumber, SeqTrack *seq_track) { DEBUG_PRINT_FN(); MNM.insertMachineInKit(tracknumber, &(machine)); load_seq_data(seq_track); - store_in_mem(tracknumber); } void MNMTrack::get_machine_from_kit(uint8_t tracknumber) { diff --git a/avr/cores/megacommand/MCL/PageSelectPage.cpp b/avr/cores/megacommand/MCL/PageSelectPage.cpp index d9eab056a..7c9a2d702 100644 --- a/avr/cores/megacommand/MCL/PageSelectPage.cpp +++ b/avr/cores/megacommand/MCL/PageSelectPage.cpp @@ -84,21 +84,13 @@ void PageSelectPage::init() { R.use_icons_page(); R.use_page_entries(); R.restore_page_entry_deps(); - - oled_display.clearDisplay(); oled_display.fillRect(0, 0, 128, 7, WHITE); oled_display.setFont(&TomThumb); oled_display.setTextColor(BLACK); oled_display.setCursor(47, 6); oled_display.print("PAGE SELECT"); oled_display.setTextColor(WHITE); - char str[16]; - uint8_t label_pos[4] = {30, 57, 81, 104}; - for (uint8_t i = 0; i < 4; ++i) { - get_category_name_by_idx(i, str); - oled_display.setCursor(label_pos[i], 31); - oled_display.print(str); - } + loop_init = true; // md_exploit.on(switch_tracks); // clear trigled so it's always sent on first run @@ -229,6 +221,18 @@ void PageSelectPage::display() { uint8_t pageidx; uint8_t catidx; + oled_display.fillRect(0, 7, 128, 25, BLACK); + oled_display.setFont(&TomThumb); + oled_display.setTextColor(BLACK); + oled_display.setCursor(47, 6); + oled_display.print("PAGE SELECT"); + oled_display.setTextColor(WHITE); + uint8_t label_pos[4] = {30, 57, 81, 104}; + for (uint8_t i = 0; i < 4; ++i) { + get_category_name_by_idx(i, str); + oled_display.setCursor(label_pos[i], 31); + oled_display.print(str); + } pageidx = get_pageidx(page_select); get_page_icon(pageidx, icon, iconw, iconh); get_page(pageidx, str); @@ -239,8 +243,8 @@ void PageSelectPage::display() { catidx = 0xFF; } - oled_display.fillRect(28, 7, 100, 16, BLACK); - oled_display.fillRect(0, 7, 28, 25, BLACK); +// oled_display.fillRect(28, 7, 100, 16, BLACK); +// oled_display.fillRect(0, 7, 28, 25, BLACK); // 4x trig groups uint8_t group_x = 28; diff --git a/avr/cores/megacommand/MCL/SeqStepPage.cpp b/avr/cores/megacommand/MCL/SeqStepPage.cpp index 55388c29b..f4e4fd116 100644 --- a/avr/cores/megacommand/MCL/SeqStepPage.cpp +++ b/avr/cores/megacommand/MCL/SeqStepPage.cpp @@ -481,7 +481,6 @@ bool SeqStepPage::handleEvent(gui_event_t *event) { // Note copy if (step != 255) { opt_copy_step_handler(255); - note_interface.ignoreNextEvent(step); disable_md_micro(); } else if (trig_interface.is_key_down(MDX_KEY_SCALE)) { opt_copy_page_handler_cb(); @@ -498,7 +497,6 @@ bool SeqStepPage::handleEvent(gui_event_t *event) { reset_undo(); if (step != 255) { opt_paste_step_handler(); - note_interface.ignoreNextEvent(step); disable_md_micro(); send_locks(step); } else if (trig_interface.is_key_down(MDX_KEY_SCALE)) { @@ -520,7 +518,6 @@ bool SeqStepPage::handleEvent(gui_event_t *event) { opt_clear_step = 1; opt_clear_step_handler(); disable_md_micro(); - note_interface.ignoreNextEvent(step); last_step = step; } else if (trig_interface.is_key_down(MDX_KEY_SCALE)) { opt_clear_page_handler(); diff --git a/avr/cores/megacommand/MCL/SeqTrack.h b/avr/cores/megacommand/MCL/SeqTrack.h index 90253b9a2..662ea0312 100644 --- a/avr/cores/megacommand/MCL/SeqTrack.h +++ b/avr/cores/megacommand/MCL/SeqTrack.h @@ -205,6 +205,13 @@ class SeqTrack : public SeqTrackBase { ALWAYS_INLINE() void seq() { uint8_t timing_mid = get_timing_mid(); mod12_counter++; + if (count_down) { + count_down--; + if (count_down == 0) { + reset(); + mod12_counter = 0; + } + } if (mod12_counter == timing_mid) { count_down = 0; mod12_counter = 0; diff --git a/avr/cores/megacommand/MD/MD.cpp b/avr/cores/megacommand/MD/MD.cpp index d4514ceb2..8d2f9ab01 100644 --- a/avr/cores/megacommand/MD/MD.cpp +++ b/avr/cores/megacommand/MD/MD.cpp @@ -652,6 +652,7 @@ void MDClass::setMachine(uint8_t track, MDMachine *machine) { uint8_t MDClass::assignMachineBulk(uint8_t track, MDMachine *machine, uint8_t level, uint8_t mode, bool send) { + DEBUG_PRINT("assign machine bulk: "); DEBUG_PRINTLN(track); uint8_t data[43] = {0x70, 0x5b}; uint8_t i = 2; data[i++] = track; @@ -693,16 +694,13 @@ uint8_t MDClass::assignMachineBulk(uint8_t track, MDMachine *machine, data[i++] = level; set_level = true; } - if (send) { - if (track > 15) { track -= 16; } - insertMachineInKit(track, machine, set_level); - } end: return sendRequest(data, i, send); } void MDClass::loadMachinesCache(uint16_t track_mask) { + DEBUG_PRINTLN("load machine cache"); uint8_t a = track_mask & 0x7F; uint8_t b = (track_mask >> 7) & 0x7F; uint8_t c = (track_mask >> 14) & 0x7F; @@ -717,6 +715,9 @@ void MDClass::setOrigParams(uint8_t track, MDMachine *machine) { void MDClass::insertMachineInKit(uint8_t track, MDMachine *machine, bool set_level) { + + DEBUG_PRINT("insert machine in kit "); DEBUG_PRINTLN(track); + MDKit *kit_ = &kit; memcpy(kit_->params[track], machine->params, 24); @@ -758,7 +759,11 @@ uint8_t MDClass::sendMachine(uint8_t track, MDMachine *machine, bool send_level, MDKit *kit_ = &kit; uint8_t level = 255; - if ((send_level) && (kit_->levels[track] != machine->level)) { + + uint8_t track_ = track; + if (track_ > 15) { track_ -= 16; } + + if ((send_level) && (kit_->levels[track_] != machine->level)) { level = machine->level; } diff --git a/avr/cores/megacommand/Makefile b/avr/cores/megacommand/Makefile index 7c9719f3e..09e12a7b9 100644 --- a/avr/cores/megacommand/Makefile +++ b/avr/cores/megacommand/Makefile @@ -38,7 +38,7 @@ OPTIM_FLAGS = -Os WARN_FLAGS = -Wall -Wextra COMPILE_FLAGS = -c $(OPTIM_FLAGS) $(WARN_FLAGS) -MMD FEATURE_FLAGS = -ffunction-sections -fdata-sections -flto=jobserver -LINK_FLAGS = -lm -Wl,--gc-sections,--defsym=__stack=0x8021ff,--section-start,.data=0x802200,--defsym=__heap_end=0x80ffff,--relax +LINK_FLAGS = -lm -Wl,--gc-sections,--defsym=__stack=0x801FA1,--section-start,.sdcard=0x801FA2,--section-start,.data=0x802200,--defsym=__heap_end=0x80ffff,--relax ARCH_FLAGS = -mmcu=atmega2560 -DF_CPU=16000000L -DARDUINO=10803 -DARDUINO_AVR_MEGA2560 -DARDUINO_ARCH_AVR CFLAGS = -std=gnu11 $(COMPILE_FLAGS) $(FEATURE_FLAGS) $(LINK_FLAGS) $(ARCH_FLAGS) @@ -47,7 +47,7 @@ CXXFLAGS += -fpermissive -fno-exceptions -fno-threadsafe-statics -fno-rtti LDFLAGS = $(OPTIM_FLAGS) $(WARN_FLAGS) -flto -fuse-linker-plugin -mrelax $(LINK_FLAGS) $(ARCH_FLAGS) -DIRS = Elektron A4 Wire Wire/utility Midi MidiTools Adafruit-GFX-Library Adafruit-GFX-Library/Fonts SdFat SdFat/SdCard SdFat/FatLib SdFat/SpiDriver SdFat/SpiDriver/boards MCL SPI Adafruit_SSD1305_Library CommonTools GUI MD MNM resources +DIRS = Elektron A4 Wire Wire/utility Midi MidiTools Adafruit-GFX-Library Adafruit-GFX-Library/Fonts SdFat SdFat/ExFatLib SdFat/FsLib SdFat/common SdFat/SdCard SdFat/FatLib SdFat/SpiDriver SdFat/SpiDriver/boards MCL SPI Adafruit_SSD1305_Library CommonTools GUI MD MNM resources LDIRS = $(foreach dir,$(DIRS),$(CORE_DIR)/$(dir)) ${CORE_DIR} ${VARIENTS_DIR} diff --git a/avr/cores/megacommand/Midi/MidiSysexFile.cpp b/avr/cores/megacommand/Midi/MidiSysexFile.cpp index b738b5d4b..ded5b4e70 100644 --- a/avr/cores/megacommand/Midi/MidiSysexFile.cpp +++ b/avr/cores/megacommand/Midi/MidiSysexFile.cpp @@ -3,7 +3,7 @@ int MidiSysexFile::readPacket(uint8_t* buf, size_t szbuf) { uint8_t *p = buf; do { - *p = FatFile::read(); + *p = read(); } while (*p++ != 0xF7 && --szbuf > 0); if(buf[0] == 0xF0 && p[-1] == 0xF7) { return p - buf; diff --git a/avr/cores/megacommand/MidiUart.cpp b/avr/cores/megacommand/MidiUart.cpp index 2450d8590..e71527bb7 100644 --- a/avr/cores/megacommand/MidiUart.cpp +++ b/avr/cores/megacommand/MidiUart.cpp @@ -48,6 +48,7 @@ void MidiUartClass::set_speed(uint32_t speed_) { // UBRR1L = (cpu & 0xFF); while (!txRb.isEmpty()) ; + while (!(check_empty_tx())); uint32_t cpu = (F_CPU / 16); cpu /= speed_; diff --git a/avr/cores/megacommand/MidiUart.h b/avr/cores/megacommand/MidiUart.h index 8ee88008e..7c332cee0 100644 --- a/avr/cores/megacommand/MidiUart.h +++ b/avr/cores/megacommand/MidiUart.h @@ -159,16 +159,16 @@ class MidiUartClass : public MidiUartParent { uint8_t read_char() { return *udr; } bool check_empty_tx() { volatile uint8_t *ptr = ucsra(); - return IS_BIT_SET(*ptr, UDRE1); + return *ptr & (1 << UDRE0); } void set_tx() { volatile uint8_t *ptr = ucsrb(); - SET_BIT(*ptr, UDRIE0); + *ptr |= (1 << UDRIE0); } void clear_tx() { volatile uint8_t *ptr = ucsrb(); - CLEAR_BIT(*ptr, UDRIE0); + *ptr &= ~(1 << UDRIE0); } void set_speed(uint32_t speed); diff --git a/avr/cores/megacommand/SPI/SPI.h b/avr/cores/megacommand/SPI/SPI.h index 5206a0918..a0c841ade 100644 --- a/avr/cores/megacommand/SPI/SPI.h +++ b/avr/cores/megacommand/SPI/SPI.h @@ -15,6 +15,8 @@ #define _SPI_H_INCLUDED #include +#include "mididuino_private.h" +#define nop asm volatile ("nop\n\t") // SPI_HAS_TRANSACTION means SPI has beginTransaction(), endTransaction(), // usingInterrupt(), and SPISetting(clock, bitOrder, dataMode) @@ -56,17 +58,17 @@ #define SPI_MODE2 0x08 #define SPI_MODE3 0x0C -#define SPI_MODE_MASK 0x0C // CPOL = bit 3, CPHA = bit 2 on SPCR -#define SPI_CLOCK_MASK 0x03 // SPR1 = bit 1, SPR0 = bit 0 on SPCR -#define SPI_2XCLOCK_MASK 0x01 // SPI2X = bit 0 on SPSR +#define SPI_MODE_MASK 0x0C // CPOL = bit 3, CPHA = bit 2 on SPCR +#define SPI_CLOCK_MASK 0x03 // SPR1 = bit 1, SPR0 = bit 0 on SPCR +#define SPI_2XCLOCK_MASK 0x01 // SPI2X = bit 0 on SPSR // define SPI_AVR_EIMSK for AVR boards with external interrupt pins #if defined(EIMSK) - #define SPI_AVR_EIMSK EIMSK +#define SPI_AVR_EIMSK EIMSK #elif defined(GICR) - #define SPI_AVR_EIMSK GICR +#define SPI_AVR_EIMSK GICR #elif defined(GIMSK) - #define SPI_AVR_EIMSK GIMSK +#define SPI_AVR_EIMSK GIMSK #endif class SPISettings { @@ -78,15 +80,14 @@ class SPISettings { init_MightInline(clock, bitOrder, dataMode); } } - SPISettings() { - init_AlwaysInline(4000000, MSBFIRST, SPI_MODE0); - } + SPISettings() { init_AlwaysInline(4000000, MSBFIRST, SPI_MODE0); } + private: void init_MightInline(uint32_t clock, uint8_t bitOrder, uint8_t dataMode) { init_AlwaysInline(clock, bitOrder, dataMode); } void init_AlwaysInline(uint32_t clock, uint8_t bitOrder, uint8_t dataMode) - __attribute__((__always_inline__)) { + __attribute__((__always_inline__)) { // Clock settings are defined as follows. Note that this shows SPI2X // inverted, so the bits form increasing numbers. Also note that // fosc/64 appears twice @@ -137,14 +138,14 @@ class SPISettings { // Compensate for the duplicate fosc/64 if (clockDiv == 6) - clockDiv = 7; + clockDiv = 7; // Invert the SPI2X bit clockDiv ^= 0x1; // Pack into the SPISettings class spcr = _BV(SPE) | _BV(MSTR) | ((bitOrder == LSBFIRST) ? _BV(DORD) : 0) | - (dataMode & SPI_MODE_MASK) | ((clockDiv >> 1) & SPI_CLOCK_MASK); + (dataMode & SPI_MODE_MASK) | ((clockDiv >> 1) & SPI_CLOCK_MASK); spsr = clockDiv & SPI_2XCLOCK_MASK; } uint8_t spcr; @@ -152,7 +153,6 @@ class SPISettings { friend class SPIClass; }; - class SPIClass { public: // Initialize the SPI library @@ -180,25 +180,25 @@ class SPIClass { uint8_t sreg = SREG; noInterrupts(); - #ifdef SPI_AVR_EIMSK +#ifdef SPI_AVR_EIMSK if (interruptMode == 1) { interruptSave = SPI_AVR_EIMSK; SPI_AVR_EIMSK &= ~interruptMask; SREG = sreg; } else - #endif +#endif { interruptSave = sreg; } } - #ifdef SPI_TRANSACTION_MISMATCH_LED +#ifdef SPI_TRANSACTION_MISMATCH_LED if (inTransactionFlag) { pinMode(SPI_TRANSACTION_MISMATCH_LED, OUTPUT); digitalWrite(SPI_TRANSACTION_MISMATCH_LED, HIGH); } inTransactionFlag = 1; - #endif +#endif SPCR = settings.spcr; SPSR = settings.spsr; @@ -214,69 +214,81 @@ class SPIClass { * speeds it is unnoticed. */ asm volatile("nop"); - while (!(SPSR & _BV(SPIF))) ; // wait + while (!(SPSR & _BV(SPIF))) + ; // wait return SPDR; } inline static uint16_t transfer16(uint16_t data) { - union { uint16_t val; struct { uint8_t lsb; uint8_t msb; }; } in, out; + union { + uint16_t val; + struct { + uint8_t lsb; + uint8_t msb; + }; + } in, out; in.val = data; if (!(SPCR & _BV(DORD))) { SPDR = in.msb; asm volatile("nop"); // See transfer(uint8_t) function - while (!(SPSR & _BV(SPIF))) ; + while (!(SPSR & _BV(SPIF))) + ; out.msb = SPDR; SPDR = in.lsb; asm volatile("nop"); - while (!(SPSR & _BV(SPIF))) ; + while (!(SPSR & _BV(SPIF))) + ; out.lsb = SPDR; } else { SPDR = in.lsb; asm volatile("nop"); - while (!(SPSR & _BV(SPIF))) ; + while (!(SPSR & _BV(SPIF))) + ; out.lsb = SPDR; SPDR = in.msb; asm volatile("nop"); - while (!(SPSR & _BV(SPIF))) ; + while (!(SPSR & _BV(SPIF))) + ; out.msb = SPDR; } return out.val; } - inline static void transfer(void *buf, size_t count) { + inline static void transfer(uint8_t *buf, size_t count) { if (count == 0) return; - uint8_t *p = (uint8_t *)buf; - SPDR = *p; - while (--count > 0) { - uint8_t out = *(p + 1); - while (!(SPSR & _BV(SPIF))) ; - uint8_t in = SPDR; - SPDR = out; - *p++ = in; + SPDR = *buf++; + while (--count) { + uint8_t b = *buf++; + // nops optimize loop for 16MHz CPU 8 MHz SPI + nop; + nop; + while (!(SPSR & (1 << SPIF))) { + } + SPDR = b; + } + while (!(SPSR & (1 << SPIF))) { } - while (!(SPSR & _BV(SPIF))) ; - *p = SPDR; } // After performing a group of transfers and releasing the chip select // signal, this function allows others to access the SPI bus inline static void endTransaction(void) { - #ifdef SPI_TRANSACTION_MISMATCH_LED +#ifdef SPI_TRANSACTION_MISMATCH_LED if (!inTransactionFlag) { pinMode(SPI_TRANSACTION_MISMATCH_LED, OUTPUT); digitalWrite(SPI_TRANSACTION_MISMATCH_LED, HIGH); } inTransactionFlag = 0; - #endif +#endif if (interruptMode > 0) { - #ifdef SPI_AVR_EIMSK +#ifdef SPI_AVR_EIMSK uint8_t sreg = SREG; - #endif +#endif noInterrupts(); - #ifdef SPI_AVR_EIMSK +#ifdef SPI_AVR_EIMSK if (interruptMode == 1) { SPI_AVR_EIMSK = interruptSave; SREG = sreg; } else - #endif +#endif { SREG = interruptSave; } @@ -289,8 +301,10 @@ class SPIClass { // This function is deprecated. New applications should use // beginTransaction() to configure SPI settings. inline static void setBitOrder(uint8_t bitOrder) { - if (bitOrder == LSBFIRST) SPCR |= _BV(DORD); - else SPCR &= ~(_BV(DORD)); + if (bitOrder == LSBFIRST) + SPCR |= _BV(DORD); + else + SPCR &= ~(_BV(DORD)); } // This function is deprecated. New applications should use // beginTransaction() to configure SPI settings. @@ -314,9 +328,9 @@ class SPIClass { static uint8_t interruptMode; // 0=none, 1=mask, 2=global static uint8_t interruptMask; // which interrupts to mask static uint8_t interruptSave; // temp storage, to restore state - #ifdef SPI_TRANSACTION_MISMATCH_LED +#ifdef SPI_TRANSACTION_MISMATCH_LED static uint8_t inTransactionFlag; - #endif +#endif }; extern SPIClass SPI; diff --git a/avr/cores/megacommand/SdFat/BufferedPrint.h b/avr/cores/megacommand/SdFat/BufferedPrint.h new file mode 100644 index 000000000..c16f14149 --- /dev/null +++ b/avr/cores/megacommand/SdFat/BufferedPrint.h @@ -0,0 +1,269 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef BufferedPrint_h +#define BufferedPrint_h +/** + * \file + * \brief Fast buffered print. + */ +#include "common/FmtNumber.h" +/** + * \class BufferedPrint + * \brief Fast buffered print template. + */ +template +class BufferedPrint { + public: + BufferedPrint() : m_wr(nullptr), m_in(0) {} + /** BufferedPrint constructor. + * \param[in] wr Print destination. + */ + explicit BufferedPrint(WriteClass* wr) : m_wr(wr), m_in(0) {} + /** Initialize the BuffedPrint class. + * \param[in] wr Print destination. + */ + void begin(WriteClass* wr) { + m_wr = wr; + m_in = 0; + } + /** Flush the buffer - same as sync() with no status return. */ + void flush() {sync();} + /** Print a character followed by a field terminator. + * \param[in] c character to print. + * \param[in] term The field terminator. Use '\\n' for CR LF. + * \return true for success or false if an error occurs. + */ + size_t printField(char c, char term) { + char buf[3]; + char* str = buf + sizeof(buf); + if (term) { + *--str = term; + if (term == '\n') { + *--str = '\r'; + } + } + *--str = c; + return write(str, buf + sizeof(buf) - str); + } + /** Print a string stored in AVR flash followed by a field terminator. + * \param[in] fsh string to print. + * \param[in] term The field terminator. Use '\\n' for CR LF. + * \return true for success or false if an error occurs. + */ + size_t printField(const __FlashStringHelper *fsh, char term) { +#ifdef __AVR__ + size_t rtn = 0; + PGM_P p = reinterpret_cast(fsh); + char c; + while ((c = pgm_read_byte(p++))) { + if (!write(&c, 1)) { + return 0; + } + rtn++; + } + if (term) { + char buf[2]; + char* str = buf + sizeof(buf); + *--str = term; + if (term == '\n') { + *--str = '\r'; + } + rtn += write(str, buf + sizeof(buf) - str); + } + return rtn; +#else // __AVR__ + return printField(reinterpret_cast(fsh), term); +#endif // __AVR__ + } + /** Print a string followed by a field terminator. + * \param[in] str string to print. + * \param[in] term The field terminator. Use '\\n' for CR LF. + * \return true for success or false if an error occurs. + */ + size_t printField(const char* str, char term) { + size_t rtn = write(str, strlen(str)); + if (term) { + char buf[2]; + char* ptr = buf + sizeof(buf); + *--ptr = term; + if (term == '\n') { + *--ptr = '\r'; + } + rtn += write(ptr, buf + sizeof(buf) - ptr); + } + return rtn; + } + /** Print a double followed by a field terminator. + * \param[in] d The number to be printed. + * \param[in] term The field terminator. Use '\\n' for CR LF. + * \param[in] prec Number of digits after decimal point. + * \return true for success or false if an error occurs. + */ + size_t printField(double d, char term, uint8_t prec = 2) { + char buf[24]; + char* str = buf + sizeof(buf); + if (term) { + *--str = term; + if (term == '\n') { + *--str = '\r'; + } + } + str = fmtDouble(str, d, prec, false); + return write(str, buf + sizeof(buf) - str); + } + /** Print a float followed by a field terminator. + * \param[in] f The number to be printed. + * \param[in] term The field terminator. Use '\\n' for CR LF. + * \param[in] prec Number of digits after decimal point. + * \return true for success or false if an error occurs. + */ + size_t printField(float f, char term, uint8_t prec = 2) { + return printField(static_cast(f), term, prec); + } + /** Print an integer value for 8, 16, and 32 bit signed and unsigned types. + * \param[in] n The value to print. + * \param[in] term The field terminator. Use '\\n' for CR LF. + * \return true for success or false if an error occurs. + */ + template + size_t printField(Type n, char term) { + const uint8_t DIM = sizeof(Type) <= 2 ? 8 : 13; + char buf[DIM]; + char* str = buf + sizeof(buf); + + if (term) { + *--str = term; + if (term == '\n') { + *--str = '\r'; + } + } + Type p = n < 0 ? -n : n; + if (sizeof(Type) <= 2) { + str = fmtBase10(str, (uint16_t)p); + } else { + str = fmtBase10(str, (uint32_t)p); + } + if (n < 0) { + *--str = '-'; + } + return write(str, buf + sizeof(buf) - str); + } + /** Print CR LF. + * \return true for success or false if an error occurs. + */ + size_t println() { + char buf[2]; + buf[0] = '\r'; + buf[1] = '\n'; + return write(buf, 2); + } + /** Print a double. + * \param[in] d The number to be printed. + * \param[in] prec Number of digits after decimal point. + * \return true for success or false if an error occurs. + */ + size_t print(double d, uint8_t prec = 2) { + return printField(d, 0, prec); + } + /** Print a double followed by CR LF. + * \param[in] d The number to be printed. + * \param[in] prec Number of digits after decimal point. + * \return true for success or false if an error occurs. + */ + size_t println(double d, uint8_t prec = 2) { + return printField(d, '\n', prec); + } + /** Print a float. + * \param[in] f The number to be printed. + * \param[in] prec Number of digits after decimal point. + * \return true for success or false if an error occurs. + */ + size_t print(float f, uint8_t prec = 2) { + return printField(static_cast(f), 0, prec); + } + /** Print a float followed by CR LF. + * \param[in] f The number to be printed. + * \param[in] prec Number of digits after decimal point. + * \return true for success or false if an error occurs. + */ + size_t println(float f, uint8_t prec) { + return printField(static_cast(f), '\n', prec); + } + /** Print character, string, or number. + * \param[in] v item to print. + * \return true for success or false if an error occurs. + */ + template + size_t print(Type v) { + return printField(v, 0); + } + /** Print character, string, or number followed by CR LF. + * \param[in] v item to print. + * \return true for success or false if an error occurs. + */ + template + size_t println(Type v) { + return printField(v, '\n'); + } + + /** Flush the buffer. + * \return true for success or false if an error occurs. + */ + bool sync() { + if (!m_wr || m_wr->write(m_buf, m_in) != m_in) { + return false; + } + m_in = 0; + return true; + } + /** Write data to an open file. + * \param[in] src Pointer to the location of the data to be written. + * + * \param[in] n Number of bytes to write. + * + * \return For success write() returns the number of bytes written, always + * \a n. + */ + size_t write(const void* src, size_t n) { + if ((m_in + n) > sizeof(m_buf)) { + if (!sync()) { + return 0; + } + if (n >= sizeof(m_buf)) { + return n == m_wr->write((const uint8_t*)src, n) ? n : 0; + } + } + memcpy(m_buf + m_in, src, n); + m_in += n; + return n; + } + + private: + WriteClass* m_wr; + uint8_t m_in; + // Insure room for double. + uint8_t m_buf[BUF_DIM < 24 ? 24 : BUF_DIM]; // NOLINT +}; +#endif // BufferedPrint_h diff --git a/avr/cores/megacommand/SdFat/SpiDriver/DigitalPin.h b/avr/cores/megacommand/SdFat/DigitalIO/DigitalPin.h similarity index 90% rename from avr/cores/megacommand/SdFat/SpiDriver/DigitalPin.h rename to avr/cores/megacommand/SdFat/DigitalIO/DigitalPin.h index 83f3d26bb..d44bb5752 100644 --- a/avr/cores/megacommand/SdFat/SpiDriver/DigitalPin.h +++ b/avr/cores/megacommand/SdFat/DigitalIO/DigitalPin.h @@ -1,26 +1,21 @@ -/** - * Copyright (c) 20011-2017 Bill Greiman - * This file is part of the SdFat library for SD memory cards. +/* Arduino DigitalIO Library + * Copyright (C) 2013 by William Greiman * - * MIT License + * This file is part of the Arduino DigitalIO Library * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. + * You should have received a copy of the GNU General Public License + * along with the Arduino DigitalIO Library. If not, see + * . */ /** * @file diff --git a/avr/cores/megacommand/SdFat/SpiDriver/SoftSPI.h b/avr/cores/megacommand/SdFat/DigitalIO/SoftSPI.h similarity index 79% rename from avr/cores/megacommand/SdFat/SpiDriver/SoftSPI.h rename to avr/cores/megacommand/SdFat/DigitalIO/SoftSPI.h index 28ffca815..6dd58dffb 100644 --- a/avr/cores/megacommand/SdFat/SpiDriver/SoftSPI.h +++ b/avr/cores/megacommand/SdFat/DigitalIO/SoftSPI.h @@ -1,36 +1,30 @@ -/** - * Copyright (c) 20011-2017 Bill Greiman - * This file is part of the SdFat library for SD memory cards. +/* Arduino DigitalIO Library + * Copyright (C) 2013 by William Greiman * - * MIT License + * This file is part of the Arduino DigitalIO Library * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. + * You should have received a copy of the GNU General Public License + * along with the Arduino DigitalIO Library. If not, see + * . */ /** - * @file + * @file * @brief Software SPI. * * @defgroup softSPI Software SPI * @details Software SPI Template Class. * @{ */ - #ifndef SoftSPI_h #define SoftSPI_h #include "DigitalPin.h" diff --git a/avr/cores/megacommand/SdFat/SpiDriver/boards/AvrDevelopersGpioPinMap.h b/avr/cores/megacommand/SdFat/DigitalIO/boards/AvrDevelopersGpioPinMap.h similarity index 100% rename from avr/cores/megacommand/SdFat/SpiDriver/boards/AvrDevelopersGpioPinMap.h rename to avr/cores/megacommand/SdFat/DigitalIO/boards/AvrDevelopersGpioPinMap.h diff --git a/avr/cores/megacommand/SdFat/SpiDriver/boards/BobuinoGpioPinMap.h b/avr/cores/megacommand/SdFat/DigitalIO/boards/BobuinoGpioPinMap.h similarity index 100% rename from avr/cores/megacommand/SdFat/SpiDriver/boards/BobuinoGpioPinMap.h rename to avr/cores/megacommand/SdFat/DigitalIO/boards/BobuinoGpioPinMap.h diff --git a/avr/cores/megacommand/SdFat/SpiDriver/boards/GpioPinMap.h b/avr/cores/megacommand/SdFat/DigitalIO/boards/GpioPinMap.h similarity index 55% rename from avr/cores/megacommand/SdFat/SpiDriver/boards/GpioPinMap.h rename to avr/cores/megacommand/SdFat/DigitalIO/boards/GpioPinMap.h index 326b7621c..901ad3e78 100644 --- a/avr/cores/megacommand/SdFat/SpiDriver/boards/GpioPinMap.h +++ b/avr/cores/megacommand/SdFat/DigitalIO/boards/GpioPinMap.h @@ -1,3 +1,22 @@ +/* Arduino DigitalIO Library + * Copyright (C) 2013 by William Greiman + * + * This file is part of the Arduino DigitalIO Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Arduino DigitalIO Library. If not, see + * . + */ #ifndef GpioPinMap_h #define GpioPinMap_h #if defined(__AVR_ATmega168__)\ @@ -30,15 +49,15 @@ || defined(__AVR_ATmega16__) #ifdef ARDUINO_1284P_AVR_DEVELOPERS #include "AvrDevelopersGpioPinMap.h" -#elif defined(BOBUINO_PINOUT) || defined(ARDUINO_1284P_BOBUINO) +#elif defined(ARDUINO_1284P_BOBUINO) #include "BobuinoGpioPinMap.h" #elif defined(ARDUINO_1284P_SLEEPINGBEAUTY) #include "SleepingBeautyGpioPinMap.h" -#elif defined(STANDARD_PINOUT) || defined(ARDUINO_1284P_STANDARD) +#elif defined(ARDUINO_1284P_STANDARD) #include "Standard1284GpioPinMap.h" -#else // ARDUINO_1284P_AVR_DEVELOPERS +#else // ARDUINO_1284P_SLEEPINGBEAUTY #error Undefined variant 1284, 644, 324 -#endif // ARDUINO_1284P_AVR_DEVELOPERS +#endif // ARDUINO_1284P_SLEEPINGBEAUTY #else // 1284P, 1284, 644 #error Unknown board type. #endif // end all boards diff --git a/avr/cores/megacommand/SdFat/SpiDriver/boards/LeonardoGpioPinMap.h b/avr/cores/megacommand/SdFat/DigitalIO/boards/LeonardoGpioPinMap.h similarity index 100% rename from avr/cores/megacommand/SdFat/SpiDriver/boards/LeonardoGpioPinMap.h rename to avr/cores/megacommand/SdFat/DigitalIO/boards/LeonardoGpioPinMap.h diff --git a/avr/cores/megacommand/SdFat/SpiDriver/boards/MegaGpioPinMap.h b/avr/cores/megacommand/SdFat/DigitalIO/boards/MegaGpioPinMap.h similarity index 100% rename from avr/cores/megacommand/SdFat/SpiDriver/boards/MegaGpioPinMap.h rename to avr/cores/megacommand/SdFat/DigitalIO/boards/MegaGpioPinMap.h diff --git a/avr/cores/megacommand/SdFat/SpiDriver/boards/SleepingBeautyGpioPinMap.h b/avr/cores/megacommand/SdFat/DigitalIO/boards/SleepingBeautyGpioPinMap.h similarity index 100% rename from avr/cores/megacommand/SdFat/SpiDriver/boards/SleepingBeautyGpioPinMap.h rename to avr/cores/megacommand/SdFat/DigitalIO/boards/SleepingBeautyGpioPinMap.h diff --git a/avr/cores/megacommand/SdFat/SpiDriver/boards/Standard1284GpioPinMap.h b/avr/cores/megacommand/SdFat/DigitalIO/boards/Standard1284GpioPinMap.h similarity index 100% rename from avr/cores/megacommand/SdFat/SpiDriver/boards/Standard1284GpioPinMap.h rename to avr/cores/megacommand/SdFat/DigitalIO/boards/Standard1284GpioPinMap.h diff --git a/avr/cores/megacommand/SdFat/SpiDriver/boards/Teensy2GpioPinMap.h b/avr/cores/megacommand/SdFat/DigitalIO/boards/Teensy2GpioPinMap.h similarity index 100% rename from avr/cores/megacommand/SdFat/SpiDriver/boards/Teensy2GpioPinMap.h rename to avr/cores/megacommand/SdFat/DigitalIO/boards/Teensy2GpioPinMap.h diff --git a/avr/cores/megacommand/SdFat/SpiDriver/boards/Teensy2ppGpioPinMap.h b/avr/cores/megacommand/SdFat/DigitalIO/boards/Teensy2ppGpioPinMap.h similarity index 100% rename from avr/cores/megacommand/SdFat/SpiDriver/boards/Teensy2ppGpioPinMap.h rename to avr/cores/megacommand/SdFat/DigitalIO/boards/Teensy2ppGpioPinMap.h diff --git a/avr/cores/megacommand/SdFat/SpiDriver/boards/UnoGpioPinMap.h b/avr/cores/megacommand/SdFat/DigitalIO/boards/UnoGpioPinMap.h similarity index 100% rename from avr/cores/megacommand/SdFat/SpiDriver/boards/UnoGpioPinMap.h rename to avr/cores/megacommand/SdFat/DigitalIO/boards/UnoGpioPinMap.h diff --git a/avr/cores/megacommand/SdFat/DigitalIO/readme.txt b/avr/cores/megacommand/SdFat/DigitalIO/readme.txt new file mode 100644 index 000000000..8976c6500 --- /dev/null +++ b/avr/cores/megacommand/SdFat/DigitalIO/readme.txt @@ -0,0 +1,3 @@ +Selected files from the DigitalIO library. + +https://github.com/greiman/DigitalIO \ No newline at end of file diff --git a/avr/cores/megacommand/SdFat/ExFatLib/ExFatConfig.h b/avr/cores/megacommand/SdFat/ExFatLib/ExFatConfig.h new file mode 100644 index 000000000..a512314cc --- /dev/null +++ b/avr/cores/megacommand/SdFat/ExFatLib/ExFatConfig.h @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef ExFatConfig_h +#define ExFatConfig_h +#include "SdFatConfig.h" + +#ifndef EXFAT_READ_ONLY +#define EXFAT_READ_ONLY 0 +#endif // EXFAT_READ_ONLY + +#endif // ExFatConfig_h diff --git a/avr/cores/megacommand/SdFat/ExFatLib/ExFatDbg.cpp b/avr/cores/megacommand/SdFat/ExFatLib/ExFatDbg.cpp new file mode 100644 index 000000000..c5f038209 --- /dev/null +++ b/avr/cores/megacommand/SdFat/ExFatLib/ExFatDbg.cpp @@ -0,0 +1,621 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "ExFatVolume.h" +#include "../common/upcase.h" +#include "ExFatLib.h" +#ifndef DOXYGEN_SHOULD_SKIP_THIS +//------------------------------------------------------------------------------ +static void printHex(print_t* pr, uint8_t h); +static void printHex(print_t* pr, uint16_t val); +static void printHex(print_t* pr, uint32_t val); +static void printHex64(print_t* pr, uint64_t n); +static void println64(print_t* pr, uint64_t n); +//------------------------------------------------------------------------------ +static void dmpDirData(print_t* pr, DirGeneric_t* dir) { + for (uint8_t k = 0; k < 31; k++) { + if (k) { + pr->write(' '); + } + printHex(pr, dir->data[k]); + } + pr->println(); +} +//------------------------------------------------------------------------------ +static uint16_t exFatDirChecksum(const void* dir, uint16_t checksum) { + const uint8_t* data = reinterpret_cast(dir); + bool skip = data[0] == EXFAT_TYPE_FILE; + for (size_t i = 0; i < 32; i += (i == 1 && skip ? 3 : 1)) { + checksum = ((checksum << 15) | (checksum >> 1)) + data[i]; + } + return checksum; +} + +//------------------------------------------------------------------------------ +static uint16_t hashDir(DirName_t* dir, uint16_t hash) { + for (uint8_t i = 0; i < 30; i += 2) { + uint16_t u = getLe16(dir->unicode + i); + if (!u) { + break; + } + uint16_t c = toUpcase(u); + hash = ((hash << 15) | (hash >> 1)) + (c & 0XFF); + hash = ((hash << 15) | (hash >> 1)) + (c >> 8); + } + return hash; +} +//------------------------------------------------------------------------------ +static void printDateTime(print_t* pr, + uint32_t timeDate, uint8_t ms, int8_t tz) { + fsPrintDateTime(pr, timeDate, ms, tz); + pr->println(); +} +//------------------------------------------------------------------------------ +static void printDirBitmap(print_t* pr, DirBitmap_t* dir) { + pr->print(F("dirBitmap: 0x")); + pr->println(dir->type, HEX); + pr->print(F("flags: 0x")); + pr->println(dir->flags, HEX); + pr->print(F("firstCluster: ")); + pr->println(getLe32(dir->firstCluster)); + pr->print(F("size: ")); + println64(pr, getLe64(dir->size)); +} +//------------------------------------------------------------------------------ +static void printDirFile(print_t* pr, DirFile_t* dir) { + pr->print(F("dirFile: 0x")); + pr->println(dir->type, HEX); + pr->print(F("setCount: ")); + pr->println(dir->setCount); + pr->print(F("setChecksum: 0x")); + pr->println(getLe16(dir->setChecksum), HEX); + pr->print(F("attributes: 0x")); + pr->println(getLe16(dir->attributes), HEX); + pr->print(F("createTime: ")); + printDateTime(pr, getLe32(dir->createTime), + dir->createTimeMs, dir->createTimezone); + pr->print(F("modifyTime: ")); + printDateTime(pr, getLe32(dir->modifyTime), + dir->modifyTimeMs, dir->modifyTimezone); + pr->print(F("accessTime: ")); + printDateTime(pr, getLe32(dir->accessTime), 0, dir->accessTimezone); +} +//------------------------------------------------------------------------------ +static void printDirLabel(print_t* pr, DirLabel_t* dir) { + pr->print(F("dirLabel: 0x")); + pr->println(dir->type, HEX); + pr->print(F("labelLength: ")); + pr->println(dir->labelLength); + pr->print(F("unicode: ")); + for (size_t i = 0; i < dir->labelLength; i++) { + pr->write(dir->unicode[2*i]); + } + pr->println(); +} +//------------------------------------------------------------------------------ +static void printDirName(print_t* pr, DirName_t* dir) { + pr->print(F("dirName: 0x")); + pr->println(dir->type, HEX); + pr->print(F("unicode: ")); + for (size_t i = 0; i < 30; i += 2) { + uint16_t c = getLe16(dir->unicode + i); + if (c == 0) break; + if (c < 128) { + pr->print(static_cast(c)); + } else { + pr->print("0x"); + pr->print(c, HEX); + } + pr->print(' '); + } + pr->println(); +} +//------------------------------------------------------------------------------ +static void printDirStream(print_t* pr, DirStream_t* dir) { + pr->print(F("dirStream: 0x")); + pr->println(dir->type, HEX); + pr->print(F("flags: 0x")); + pr->println(dir->flags, HEX); + pr->print(F("nameLength: ")); + pr->println(dir->nameLength); + pr->print(F("nameHash: 0x")); + pr->println(getLe16(dir->nameHash), HEX); + pr->print(F("validLength: ")); + println64(pr, getLe64(dir->validLength)); + pr->print(F("firstCluster: ")); + pr->println(getLe32(dir->firstCluster)); + pr->print(F("dataLength: ")); + println64(pr, getLe64(dir->dataLength)); +} +//------------------------------------------------------------------------------ +static void printDirUpcase(print_t* pr, DirUpcase_t* dir) { + pr->print(F("dirUpcase: 0x")); + pr->println(dir->type, HEX); + pr->print(F("checksum: 0x")); + pr->println(getLe32(dir->checksum), HEX); + pr->print(F("firstCluster: ")); + pr->println(getLe32(dir->firstCluster)); + pr->print(F("size: ")); + println64(pr, getLe64(dir->size)); +} +//------------------------------------------------------------------------------ +static void printExFatBoot(print_t* pr, pbs_t* pbs) { + BpbExFat_t* ebs = reinterpret_cast(pbs->bpb); + pr->print(F("bpbSig: 0x")); + pr->println(getLe16(pbs->signature), HEX); + pr->print(F("FileSystemName: ")); + pr->write(reinterpret_cast(pbs->oemName), 8); + pr->println(); + for (size_t i = 0; i < sizeof(ebs->mustBeZero); i++) { + if (ebs->mustBeZero[i]) { + pr->println(F("mustBeZero error")); + break; + } + } + pr->print(F("PartitionOffset: 0x")); + printHex64(pr, getLe64(ebs->partitionOffset)); + pr->print(F("VolumeLength: ")); + println64(pr, getLe64(ebs->volumeLength)); + pr->print(F("FatOffset: 0x")); + pr->println(getLe32(ebs->fatOffset), HEX); + pr->print(F("FatLength: ")); + pr->println(getLe32(ebs->fatLength)); + pr->print(F("ClusterHeapOffset: 0x")); + pr->println(getLe32(ebs->clusterHeapOffset), HEX); + pr->print(F("ClusterCount: ")); + pr->println(getLe32(ebs->clusterCount)); + pr->print(F("RootDirectoryCluster: ")); + pr->println(getLe32(ebs->rootDirectoryCluster)); + pr->print(F("VolumeSerialNumber: 0x")); + pr->println(getLe32(ebs->volumeSerialNumber), HEX); + pr->print(F("FileSystemRevision: 0x")); + pr->println(getLe32(ebs->fileSystemRevision), HEX); + pr->print(F("VolumeFlags: 0x")); + pr->println(getLe16(ebs->volumeFlags) , HEX); + pr->print(F("BytesPerSectorShift: ")); + pr->println(ebs->bytesPerSectorShift); + pr->print(F("SectorsPerClusterShift: ")); + pr->println(ebs->sectorsPerClusterShift); + pr->print(F("NumberOfFats: ")); + pr->println(ebs->numberOfFats); + pr->print(F("DriveSelect: 0x")); + pr->println(ebs->driveSelect, HEX); + pr->print(F("PercentInUse: ")); + pr->println(ebs->percentInUse); +} +//------------------------------------------------------------------------------ +static void printHex(print_t* pr, uint8_t h) { + if (h < 16) { + pr->write('0'); + } + pr->print(h, HEX); +} +//------------------------------------------------------------------------------ +static void printHex(print_t* pr, uint16_t val) { + bool space = true; + for (uint8_t i = 0; i < 4; i++) { + uint8_t h = (val >> (12 - 4*i)) & 15; + if (h || i == 3) { + space = false; + } + if (space) { + pr->write(' '); + } else { + pr->print(h, HEX); + } + } +} +//------------------------------------------------------------------------------ +static void printHex(print_t* pr, uint32_t val) { + bool space = true; + for (uint8_t i = 0; i < 8; i++) { + uint8_t h = (val >> (28 - 4*i)) & 15; + if (h || i == 7) { + space = false; + } + if (space) { + pr->write(' '); + } else { + pr->print(h, HEX); + } + } +} +//------------------------------------------------------------------------------ +static void printHex64(print_t* pr, uint64_t n) { + char buf[17]; + char *str = &buf[sizeof(buf) - 1]; + *str = '\0'; + do { + uint8_t h = n & 15; + *--str = h < 10 ? h + '0' : h + 'A' - 10; + n >>= 4; + } while (n); + pr->println(str); +} +//------------------------------------------------------------------------------ +static void println64(print_t* pr, uint64_t n) { + char buf[21]; + char *str = &buf[sizeof(buf) - 1]; + *str = '\0'; + do { + uint64_t m = n; + n /= 10; + *--str = m - 10*n + '0'; + } while (n); + pr->println(str); +} +//------------------------------------------------------------------------------ +static void printMbr(print_t* pr, MbrSector_t* mbr) { + pr->print(F("mbrSig: 0x")); + pr->println(getLe16(mbr->signature), HEX); + for (int i = 0; i < 4; i++) { + printHex(pr, mbr->part[i].boot); + pr->write(' '); + for (int k = 0; k < 3; k++) { + printHex(pr, mbr->part[i].beginCHS[k]); + pr->write(' '); + } + printHex(pr, mbr->part[i].type); + pr->write(' '); + for (int k = 0; k < 3; k++) { + printHex(pr, mbr->part[i].endCHS[k]); + pr->write(' '); + } + pr->print(getLe32(mbr->part[i].relativeSectors), HEX); + pr->print(' '); + pr->println(getLe32(mbr->part[i].totalSectors), HEX); + } +} +//============================================================================== +void ExFatPartition::checkUpcase(print_t* pr) { + bool skip = false; + uint16_t u = 0; + uint8_t* upcase = nullptr; + uint32_t size = 0; + uint32_t sector = clusterStartSector(m_rootDirectoryCluster); + uint8_t* cache = dataCachePrepare(sector, FsCache::CACHE_FOR_READ); + if (!cache) { + pr->println(F("read root failed")); + return; + } + DirUpcase_t* dir = reinterpret_cast(cache); + + pr->println(F("\nChecking upcase table")); + for (size_t i = 0; i < 16; i++) { + if (dir[i].type == EXFAT_TYPE_UPCASE) { + sector = clusterStartSector(getLe32(dir[i].firstCluster)); + size = getLe64(dir[i].size); + break; + } + } + if (!size) { + pr->println(F("upcase not found")); + return; + } + for (size_t i = 0; i < size/2; i++) { + if ((i%256) == 0) { + upcase = dataCachePrepare(sector++, FsCache::CACHE_FOR_READ); + if (!upcase) { + pr->println(F("read upcase failed")); + return; + } + } + uint16_t v = getLe16(&upcase[2*(i & 0XFF)]); + if (skip) { + pr->print("skip "); + pr->print(u); + pr->write(' '); + pr->println(v); + } + if (v == 0XFFFF) { + skip = true; + } else if (skip) { + for (uint16_t k = 0; k < v; k++) { + uint16_t x = toUpcase(u + k); + if (x != (u + k)) { + printHex(pr, (uint16_t)(u+k)); + pr->write(','); + printHex(pr, x); + pr->println("<<<<<<<<<<<<<<<<<<<<"); + } + } + u += v; + skip = false; + } else { + uint16_t x = toUpcase(u); + if (v != x) { + printHex(pr, u); + pr->write(','); + printHex(pr, x); + pr->write(','); + printHex(pr, v); + pr->println(); + } + u++; + } + } + pr->println(F("Done checkUpcase")); +} +//------------------------------------------------------------------------------ +void ExFatPartition::dmpBitmap(print_t* pr) { + pr->println(F("bitmap:")); + dmpSector(pr, m_clusterHeapStartSector); +} +//------------------------------------------------------------------------------ +void ExFatPartition::dmpCluster(print_t* pr, uint32_t cluster, + uint32_t offset, uint32_t count) { + uint32_t sector = clusterStartSector(cluster) + offset; + for (uint32_t i = 0; i < count; i++) { + pr->print(F("\nSector: ")); + pr->println(sector + i, HEX); + dmpSector(pr, sector + i); + } +} +//------------------------------------------------------------------------------ +void ExFatPartition::dmpFat(print_t* pr, uint32_t start, uint32_t count) { + uint32_t sector = m_fatStartSector + start; + uint32_t cluster = 128*start; + pr->println(F("FAT:")); + for (uint32_t i = 0; i < count; i++) { + uint8_t* cache = dataCachePrepare(sector + i, FsCache::CACHE_FOR_READ); + if (!cache) { + pr->println(F("cache read failed")); + return; + } + uint32_t* fat = reinterpret_cast(cache); + for (size_t k = 0; k < 128; k++) { + if (0 == cluster%8) { + if (k) { + pr->println(); + } + printHex(pr, cluster); + } + cluster++; + pr->write(' '); + printHex(pr, fat[k]); + } + pr->println(); + } +} +//------------------------------------------------------------------------------ +void ExFatPartition::dmpSector(print_t* pr, uint32_t sector) { + uint8_t* cache = dataCachePrepare(sector, FsCache::CACHE_FOR_READ); + if (!cache) { + pr->println(F("dmpSector failed")); + return; + } + for (uint16_t i = 0; i < m_bytesPerSector; i++) { + if (i%32 == 0) { + if (i) { + pr->println(); + } + printHex(pr, i); + } + pr->write(' '); + printHex(pr, cache[i]); + } + pr->println(); +} +//------------------------------------------------------------------------------ +bool ExFatPartition::printDir(print_t* pr, ExFatFile* file) { + DirGeneric_t* dir = nullptr; + DirFile_t* dirFile; + DirStream_t* dirStream; + DirName_t* dirName; + uint16_t calcHash = 0; + uint16_t nameHash = 0; + uint16_t setChecksum = 0; + uint16_t calcChecksum = 0; + uint8_t nameLength = 0; + uint8_t setCount = 0; + uint8_t nUnicode; + +#define RAW_ROOT +#ifndef RAW_ROOT + while (1) { + uint8_t buf[FS_DIR_SIZE]; + if (file->read(buf, FS_DIR_SIZE) != FS_DIR_SIZE) { + break; + } + dir = reinterpret_cast(buf); +#else // RAW_ROOT + (void)file; + uint32_t nDir = 1UL << (m_sectorsPerClusterShift + 4); + uint32_t sector = clusterStartSector(m_rootDirectoryCluster); + for (uint32_t iDir = 0; iDir < nDir; iDir++) { + size_t i = iDir%16; + if (i == 0) { + uint8_t* cache = dataCachePrepare(sector++, FsCache::CACHE_FOR_READ); + if (!cache) { + return false; + } + dir = reinterpret_cast(cache); + } else { + dir++; + } +#endif // RAW_ROOT + if (dir->type == EXFAT_TYPE_END_DIR) { + break; + } + pr->println(); + + switch (dir->type) { + case EXFAT_TYPE_BITMAP: + printDirBitmap(pr, reinterpret_cast(dir)); + break; + + case EXFAT_TYPE_UPCASE: + printDirUpcase(pr, reinterpret_cast(dir)); + break; + + case EXFAT_TYPE_LABEL: + printDirLabel(pr, reinterpret_cast(dir)); + break; + + case EXFAT_TYPE_FILE: + dirFile = reinterpret_cast(dir); + printDirFile(pr, dirFile); + setCount = dirFile->setCount; + setChecksum = getLe16(dirFile->setChecksum); + calcChecksum = exFatDirChecksum(dir, 0); + break; + + case EXFAT_TYPE_STREAM: + dirStream = reinterpret_cast(dir); + printDirStream(pr, dirStream); + nameLength = dirStream->nameLength; + nameHash = getLe16(dirStream->nameHash); + calcChecksum = exFatDirChecksum(dir, calcChecksum); + setCount--; + calcHash = 0; + break; + + case EXFAT_TYPE_NAME: + dirName = reinterpret_cast(dir); + printDirName(pr, dirName); + calcChecksum = exFatDirChecksum(dir, calcChecksum); + nUnicode = nameLength > 15 ? 15 : nameLength; + calcHash = hashDir(dirName, calcHash); + nameLength -= nUnicode; + setCount--; + if (nameLength == 0 || setCount == 0) { + pr->print(F("setChecksum: 0x")); + pr->print(setChecksum, HEX); + if (setChecksum != calcChecksum) { + pr->print(F(" != calcChecksum: 0x")); + } else { + pr->print(F(" == calcChecksum: 0x")); + } + pr->println(calcChecksum, HEX); + pr->print(F("nameHash: 0x")); + pr->print(nameHash, HEX); + if (nameHash != calcHash) { + pr->print(F(" != calcHash: 0x")); + } else { + pr->print(F(" == calcHash: 0x")); + } + pr->println(calcHash, HEX); + } + break; + + default: + if (dir->type & EXFAT_TYPE_USED) { + pr->print(F("Unknown dirType: 0x")); + } else { + pr->print(F("Unused dirType: 0x")); + } + pr->println(dir->type, HEX); + dmpDirData(pr, dir); + break; + } + } + pr->println(F("Done")); + return true; +} +//------------------------------------------------------------------------------ +void ExFatPartition::printFat(print_t* pr) { + uint32_t next; + int8_t status; + pr->println(F("FAT:")); + for (uint32_t cluster = 0; cluster < 16; cluster++) { + status = fatGet(cluster, &next); + pr->print(cluster, HEX); + pr->write(' '); + if (status == 0) { + next = EXFAT_EOC; + } + pr->println(next, HEX); + } +} +//------------------------------------------------------------------------------ +void ExFatPartition::printUpcase(print_t* pr) { + uint8_t* upcase = nullptr; + uint32_t sector; + uint32_t size = 0; + uint32_t checksum = 0; + DirUpcase_t* dir; + sector = clusterStartSector(m_rootDirectoryCluster); + upcase = dataCachePrepare(sector, FsCache::CACHE_FOR_READ); + dir = reinterpret_cast(upcase); + if (!dir) { + pr->println(F("read root dir failed")); + return; + } + for (size_t i = 0; i < 16; i++) { + if (dir[i].type == EXFAT_TYPE_UPCASE) { + sector = clusterStartSector(getLe32(dir[i].firstCluster)); + size = getLe64(dir[i].size); + break; + } + } + if (!size) { + pr->println(F("upcase not found")); + return; + } + for (uint16_t i = 0; i < size/2; i++) { + if ((i%256) == 0) { + upcase = dataCachePrepare(sector++, FsCache::CACHE_FOR_READ); + if (!upcase) { + pr->println(F("read upcase failed")); + return; + } + } + if (i%16 == 0) { + pr->println(); + printHex(pr, i); + } + pr->write(' '); + uint16_t uc = getLe16(&upcase[2*(i & 0XFF)]); + printHex(pr, uc); + checksum = upcaseChecksum(uc, checksum); + } + pr->println(); + pr->print(F("checksum: ")); + printHex(pr, checksum); + pr->println(); +} +//------------------------------------------------------------------------------ +bool ExFatPartition::printVolInfo(print_t* pr) { + uint8_t* cache = dataCachePrepare(0, FsCache::CACHE_FOR_READ); + if (!cache) { + pr->println(F("read mbr failed")); + return false; + } + MbrSector_t* mbr = reinterpret_cast(cache); + printMbr(pr, mbr); + uint32_t volStart = getLe32(mbr->part->relativeSectors); + uint32_t volSize = getLe32(mbr->part->totalSectors); + if (volSize == 0) { + pr->print(F("bad partition size")); + return false; + } + cache = dataCachePrepare(volStart, FsCache::CACHE_FOR_READ); + if (!cache) { + pr->println(F("read pbs failed")); + return false; + } + printExFatBoot(pr, reinterpret_cast(cache)); + return true; +} +#endif // DOXYGEN_SHOULD_SKIP_THIS diff --git a/avr/cores/megacommand/SdFat/ExFatLib/ExFatFile.cpp b/avr/cores/megacommand/SdFat/ExFatLib/ExFatFile.cpp new file mode 100644 index 000000000..7c29ec441 --- /dev/null +++ b/avr/cores/megacommand/SdFat/ExFatLib/ExFatFile.cpp @@ -0,0 +1,738 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#define DBG_FILE "ExFatFile.cpp" +#include "../common/DebugMacros.h" +#include "../common/FsUtf.h" +#include "ExFatLib.h" +//------------------------------------------------------------------------------ +/** test for legal character. + * + * \param[in] c character to be tested. + * + * \return true for legal character else false. + */ +inline bool lfnLegalChar(uint8_t c) { +#if USE_UTF8_LONG_NAMES + return !lfnReservedChar(c); +#else // USE_UTF8_LONG_NAMES + return !(lfnReservedChar(c) || c & 0X80); +#endif // USE_UTF8_LONG_NAMES +} +//------------------------------------------------------------------------------ +bool ExFatFile::attrib(uint8_t bits) { + if (!isFileOrSubDir() || (bits & FS_ATTRIB_USER_SETTABLE) != bits) { + DBG_FAIL_MACRO; + goto fail; + } + // Don't allow read-only to be set if the file is open for write. + if ((bits & FS_ATTRIB_READ_ONLY) && isWritable()) { + DBG_FAIL_MACRO; + goto fail; + } + m_attributes = (m_attributes & ~FS_ATTRIB_USER_SETTABLE) | bits; + // insure sync() will update dir entry + m_flags |= FILE_FLAG_DIR_DIRTY; + if (!sync()) { + DBG_FAIL_MACRO; + goto fail; + } + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +uint8_t* ExFatFile::dirCache(uint8_t set, uint8_t options) { + DirPos_t pos = m_dirPos; + if (m_vol->dirSeek(&pos, FS_DIR_SIZE*set) != 1) { + return nullptr; + } + return m_vol->dirCache(&pos, options); +} +//------------------------------------------------------------------------------ +bool ExFatFile::close() { + bool rtn = sync(); + m_attributes = FILE_ATTR_CLOSED; + m_flags = 0; + return rtn; +} +//------------------------------------------------------------------------------ +bool ExFatFile::contiguousRange(uint32_t* bgnSector, uint32_t* endSector) { + if (!isContiguous()) { + return false; + } + if (bgnSector) { + *bgnSector = firstSector(); + } + if (endSector) { + *endSector = firstSector() + + ((m_validLength - 1) >> m_vol->bytesPerSectorShift()); + } + return true; +} +//------------------------------------------------------------------------------ +void ExFatFile::fgetpos(fspos_t* pos) const { + pos->position = m_curPosition; + pos->cluster = m_curCluster; +} +//------------------------------------------------------------------------------ +int ExFatFile::fgets(char* str, int num, char* delim) { + char ch; + int n = 0; + int r = -1; + while ((n + 1) < num && (r = read(&ch, 1)) == 1) { + // delete CR + if (ch == '\r') { + continue; + } + str[n++] = ch; + if (!delim) { + if (ch == '\n') { + break; + } + } else { + if (strchr(delim, ch)) { + break; + } + } + } + if (r < 0) { + // read error + return -1; + } + str[n] = '\0'; + return n; +} +//------------------------------------------------------------------------------ +uint32_t ExFatFile::firstSector() const { + return m_firstCluster ? m_vol->clusterStartSector(m_firstCluster) : 0; +} +//------------------------------------------------------------------------------ +void ExFatFile::fsetpos(const fspos_t* pos) { + m_curPosition = pos->position; + m_curCluster = pos->cluster; +} +//------------------------------------------------------------------------------ +bool ExFatFile::getAccessDateTime(uint16_t* pdate, uint16_t* ptime) { + DirFile_t* df = reinterpret_cast + (m_vol->dirCache(&m_dirPos, FsCache::CACHE_FOR_READ)); + if (!df) { + DBG_FAIL_MACRO; + goto fail; + } + *pdate = getLe16(df->accessDate); + *ptime = getLe16(df->accessTime); + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +bool ExFatFile::getCreateDateTime(uint16_t* pdate, uint16_t* ptime) { + DirFile_t* df = reinterpret_cast + (m_vol->dirCache(&m_dirPos, FsCache::CACHE_FOR_READ)); + if (!df) { + DBG_FAIL_MACRO; + goto fail; + } + *pdate = getLe16(df->createDate); + *ptime = getLe16(df->createTime); + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +bool ExFatFile::getModifyDateTime(uint16_t* pdate, uint16_t* ptime) { + DirFile_t* df = reinterpret_cast + (m_vol->dirCache(&m_dirPos, FsCache::CACHE_FOR_READ)); + if (!df) { + DBG_FAIL_MACRO; + goto fail; + } + *pdate = getLe16(df->modifyDate); + *ptime = getLe16(df->modifyTime); + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +bool ExFatFile::isBusy() { + return m_vol->isBusy(); +} +//------------------------------------------------------------------------------ +bool ExFatFile::open(const char* path, oflag_t oflag) { + return open(ExFatVolume::cwv(), path, oflag); +} +//------------------------------------------------------------------------------ +bool ExFatFile::open(ExFatVolume* vol, const char* path, oflag_t oflag) { + return vol && open(vol->vwd(), path, oflag); +} +//------------------------------------------------------------------------------ +bool ExFatFile::open(ExFatFile* dirFile, const char* path, oflag_t oflag) { + ExFatFile tmpDir; + ExName_t fname; + // error if already open + if (isOpen() || !dirFile->isDir()) { + DBG_FAIL_MACRO; + goto fail; + } + if (isDirSeparator(*path)) { + while (isDirSeparator(*path)) { + path++; + } + if (*path == 0) { + return openRoot(dirFile->m_vol); + } + if (!tmpDir.openRoot(dirFile->m_vol)) { + DBG_FAIL_MACRO; + goto fail; + } + dirFile = &tmpDir; + } + while (1) { + if (!parsePathName(path, &fname, &path)) { + DBG_FAIL_MACRO; + goto fail; + } + if (*path == 0) { + break; + } + if (!openPrivate(dirFile, &fname, O_RDONLY)) { + DBG_WARN_MACRO; + goto fail; + } + tmpDir = *this; + dirFile = &tmpDir; + close(); + } + return openPrivate(dirFile, &fname, oflag); + + fail: + return false; +} +//------------------------------------------------------------------------------ +bool ExFatFile::open(uint32_t index, oflag_t oflag) { + ExFatVolume* vol = ExFatVolume::cwv(); + return vol ? open(vol->vwd(), index, oflag) : false; +} +//------------------------------------------------------------------------------ +bool ExFatFile::open(ExFatFile* dirFile, uint32_t index, oflag_t oflag) { + if (dirFile->seekSet(FS_DIR_SIZE*index) && openNext(dirFile, oflag)) { + if (dirIndex() == index) { + return true; + } + close(); + DBG_FAIL_MACRO; + } + return false; +} +//------------------------------------------------------------------------------ +bool ExFatFile::openCwd() { + if (isOpen() || !ExFatVolume::cwv()) { + DBG_FAIL_MACRO; + goto fail; + } + *this = *ExFatVolume::cwv()->vwd(); + rewind(); + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +bool ExFatFile::openNext(ExFatFile* dir, oflag_t oflag) { + if (isOpen() || !dir->isDir() || (dir->curPosition() & 0X1F)) { + DBG_FAIL_MACRO; + goto fail; + } + return openPrivate(dir, nullptr, oflag); + + fail: + return false; +} +//------------------------------------------------------------------------------ +bool ExFatFile::openPrivate(ExFatFile* dir, ExName_t* fname, oflag_t oflag) { + int n; + uint8_t modeFlags; + uint32_t curCluster __attribute__((unused)); + uint8_t* cache __attribute__((unused)); + DirPos_t freePos __attribute__((unused)); + + DirFile_t* dirFile; + DirStream_t* dirStream; + DirName_t* dirName; + uint8_t buf[FS_DIR_SIZE]; + uint8_t freeCount = 0; + uint8_t freeNeed = 3; + bool inSet = false; + + // error if already open, no access mode, or no directory. + if (isOpen() || !dir->isDir()) { + DBG_FAIL_MACRO; + goto fail; + } + + switch (oflag & O_ACCMODE) { + case O_RDONLY: + modeFlags = FILE_FLAG_READ; + break; + case O_WRONLY: + modeFlags = FILE_FLAG_WRITE; + break; + case O_RDWR: + modeFlags = FILE_FLAG_READ | FILE_FLAG_WRITE; + break; + default: + DBG_FAIL_MACRO; + goto fail; + } + modeFlags |= oflag & O_APPEND ? FILE_FLAG_APPEND : 0; + + if (fname) { + freeNeed = 2 + (fname->nameLength + 14)/15; + dir->rewind(); + } + + while (1) { + n = dir->read(buf, FS_DIR_SIZE); + if (n == 0) { + goto create; + } + if (n != FS_DIR_SIZE) { + DBG_FAIL_MACRO; + goto fail; + } + if (!(buf[0] & EXFAT_TYPE_USED)) { + // Unused entry. + if (freeCount == 0) { + freePos.position = dir->curPosition() - FS_DIR_SIZE; + freePos.cluster = dir->curCluster(); + } + if (freeCount < freeNeed) { + freeCount++; + } + if (buf[0] == EXFAT_TYPE_END_DIR) { + if (fname) { + goto create; + } + // Likely openNext call. + DBG_WARN_MACRO; + goto fail; + } + inSet = false; + } else if (!inSet) { + if (freeCount < freeNeed) { + freeCount = 0; + } + if (buf[0] != EXFAT_TYPE_FILE) { + continue; + } + inSet = true; + memset(this, 0, sizeof(ExFatFile)); + dirFile = reinterpret_cast(buf); + m_setCount = dirFile->setCount; + m_attributes = getLe16(dirFile->attributes) & FS_ATTRIB_COPY; + if (!(m_attributes & FS_ATTRIB_DIRECTORY)) { + m_attributes |= FILE_ATTR_FILE; + } + m_vol = dir->volume(); + m_dirPos.cluster = dir->curCluster(); + m_dirPos.position = dir->curPosition() - FS_DIR_SIZE; + m_dirPos.isContiguous = dir->isContiguous(); + } else if (buf[0] == EXFAT_TYPE_STREAM) { + dirStream = reinterpret_cast(buf); + m_flags = modeFlags; + if (dirStream->flags & EXFAT_FLAG_CONTIGUOUS) { + m_flags |= FILE_FLAG_CONTIGUOUS; + } + m_validLength = getLe64(dirStream->validLength); + m_firstCluster = getLe32(dirStream->firstCluster); + m_dataLength = getLe64(dirStream->dataLength); + if (!fname) { + goto found; + } + fname->reset(); + if (fname->nameLength != dirStream->nameLength || + fname->nameHash != getLe16(dirStream->nameHash)) { + inSet = false; + } + } else if (buf[0] == EXFAT_TYPE_NAME) { + dirName = reinterpret_cast(buf); + if (!cmpName(dirName, fname)) { + inSet = false; + continue; + } + if (fname->atEnd()) { + goto found; + } + } else { + inSet = false; + } + } + + found: + // Don't open if create only. + if (oflag & O_EXCL) { + DBG_FAIL_MACRO; + goto fail; + } + // Write, truncate, or at end is an error for a directory or read-only file. + if ((oflag & (O_TRUNC | O_AT_END)) || (m_flags & FILE_FLAG_WRITE)) { + if (isSubDir() || isReadOnly() || EXFAT_READ_ONLY) { + DBG_FAIL_MACRO; + goto fail; + } + } + +#if !EXFAT_READ_ONLY + if (oflag & O_TRUNC) { + if (!(m_flags & FILE_FLAG_WRITE)) { + DBG_FAIL_MACRO; + goto fail; + } + if (!truncate(0)) { + DBG_FAIL_MACRO; + goto fail; + } + } else if ((oflag & O_AT_END) && !seekSet(fileSize())) { + DBG_FAIL_MACRO; + goto fail; + } + if (isWritable()) { + m_attributes |= FS_ATTRIB_ARCHIVE; + } +#endif // !EXFAT_READ_ONLY + return true; + + create: +#if EXFAT_READ_ONLY + DBG_FAIL_MACRO; + goto fail; +#else // EXFAT_READ_ONLY + // don't create unless O_CREAT and write + if (!(oflag & O_CREAT) || !(modeFlags & FILE_FLAG_WRITE) || !fname) { + DBG_WARN_MACRO; + goto fail; + } + while (freeCount < freeNeed) { + n = dir->read(buf, FS_DIR_SIZE); + if (n == 0) { + curCluster = dir->m_curCluster; + if (!dir->addDirCluster()) { + DBG_FAIL_MACRO; + goto fail; + } + dir->m_curCluster = curCluster; + continue; + } + if (n != FS_DIR_SIZE) { + DBG_FAIL_MACRO; + goto fail; + } + if (freeCount == 0) { + freePos.position = dir->curPosition() - FS_DIR_SIZE; + freePos.cluster = dir->curCluster(); + } + freeCount++; + } + freePos.isContiguous = dir->isContiguous(); + memset(this, 0, sizeof(ExFatFile)); + m_vol = dir->volume(); + m_attributes = FILE_ATTR_FILE | FS_ATTRIB_ARCHIVE; + m_dirPos = freePos; + fname->reset(); + for (uint8_t i = 0; i < freeNeed; i++) { + cache = dirCache(i, FsCache::CACHE_FOR_WRITE); + if (!cache || (cache[0] & 0x80)) { + DBG_FAIL_MACRO; + goto fail; + } + memset(cache, 0 , FS_DIR_SIZE); + if (i == 0) { + dirFile = reinterpret_cast(cache); + dirFile->type = EXFAT_TYPE_FILE; + m_setCount = freeNeed - 1; + dirFile->setCount = m_setCount; + + if (FsDateTime::callback) { + uint16_t date, time; + uint8_t ms10; + FsDateTime::callback(&date, &time, &ms10); + setLe16(dirFile->createDate, date); + setLe16(dirFile->createTime, time); + dirFile->createTimeMs = ms10; + } else { + setLe16(dirFile->createDate, FS_DEFAULT_DATE); + setLe16(dirFile->modifyDate, FS_DEFAULT_DATE); + setLe16(dirFile->accessDate, FS_DEFAULT_DATE); + if (FS_DEFAULT_TIME) { + setLe16(dirFile->createTime, FS_DEFAULT_TIME); + setLe16(dirFile->modifyTime, FS_DEFAULT_TIME); + setLe16(dirFile->accessTime, FS_DEFAULT_TIME); + } + } + } else if (i == 1) { + dirStream = reinterpret_cast(cache); + dirStream->type = EXFAT_TYPE_STREAM; + dirStream->flags = EXFAT_FLAG_ALWAYS1; + m_flags = modeFlags | FILE_FLAG_DIR_DIRTY; + dirStream->nameLength = fname->nameLength; + setLe16(dirStream->nameHash, fname->nameHash); + } else { + dirName = reinterpret_cast(cache); + dirName->type = EXFAT_TYPE_NAME; + for (size_t k = 0; k < 15; k++) { + if (fname->atEnd()) { + break; + } + uint16_t u = fname->get16(); + setLe16(dirName->unicode + 2*k, u); + } + } + } + return sync(); +#endif // EXFAT_READ_ONLY + + fail: + // close file + m_attributes = FILE_ATTR_CLOSED; + m_flags = 0; + return false; +} +//------------------------------------------------------------------------------ +bool ExFatFile::openRoot(ExFatVolume* vol) { + if (isOpen()) { + DBG_FAIL_MACRO; + goto fail; + } + memset(this, 0, sizeof(ExFatFile)); + m_attributes = FILE_ATTR_ROOT; + m_vol = vol; + m_flags = FILE_FLAG_READ; + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +bool ExFatFile::parsePathName(const char* path, + ExName_t* fname, const char** ptr) { + // Skip leading spaces. + while (*path == ' ') { + path++; + } + fname->begin = path; + fname->end = path; + while (*path && !isDirSeparator(*path)) { + uint8_t c = *path++; + if (!lfnLegalChar(c)) { + DBG_FAIL_MACRO; + goto fail; + } + if (c != '.' && c != ' ') { + // Need to trim trailing dots spaces. + fname->end = path; + } + } + // Advance to next path component. + for (; *path == ' ' || isDirSeparator(*path); path++) {} + *ptr = path; + return hashName(fname); + + fail: + return false; +} +//------------------------------------------------------------------------------ +int ExFatFile::peek() { + uint64_t curPosition = m_curPosition; + uint32_t curCluster = m_curCluster; + int c = read(); + m_curPosition = curPosition; + m_curCluster = curCluster; + return c; +} +//------------------------------------------------------------------------------ +int ExFatFile::read(void* buf, size_t count) { + uint8_t* dst = reinterpret_cast(buf); + int8_t fg; + size_t toRead = count; + size_t n; + uint8_t* cache; + uint16_t sectorOffset; + uint32_t sector; + uint32_t clusterOffset; + + if (!isReadable()) { + DBG_FAIL_MACRO; + goto fail; + } + if (isContiguous() || isFile()) { + if ((m_curPosition + count) > m_validLength) { + count = toRead = m_validLength - m_curPosition; + } + } + while (toRead) { + clusterOffset = m_curPosition & m_vol->clusterMask(); + sectorOffset = clusterOffset & m_vol->sectorMask(); + if (clusterOffset == 0) { + if (m_curPosition == 0) { + m_curCluster = isRoot() + ? m_vol->rootDirectoryCluster() : m_firstCluster; + } else if (isContiguous()) { + m_curCluster++; + } else { + fg = m_vol->fatGet(m_curCluster, &m_curCluster); + if (fg < 0) { + DBG_FAIL_MACRO; + goto fail; + } + if (fg == 0) { + // EOF if directory. + if (isDir()) { + break; + } + DBG_FAIL_MACRO; + goto fail; + } + } + } + sector = m_vol->clusterStartSector(m_curCluster) + + (clusterOffset >> m_vol->bytesPerSectorShift()); + if (sectorOffset != 0 || toRead < m_vol->bytesPerSector() + || sector == m_vol->dataCacheSector()) { + n = m_vol->bytesPerSector() - sectorOffset; + if (n > toRead) { + n = toRead; + } + // read sector to cache and copy data to caller + cache = m_vol->dataCachePrepare(sector, FsCache::CACHE_FOR_READ); + if (!cache) { + DBG_FAIL_MACRO; + goto fail; + } + uint8_t* src = cache + sectorOffset; + memcpy(dst, src, n); +#if USE_MULTI_SECTOR_IO + } else if (toRead >= 2*m_vol->bytesPerSector()) { + uint32_t ns = toRead >> m_vol->bytesPerSectorShift(); + // Limit reads to current cluster. + uint32_t maxNs = m_vol->sectorsPerCluster() + - (clusterOffset >> m_vol->bytesPerSectorShift()); + if (ns > maxNs) { + ns = maxNs; + } + n = ns << m_vol->bytesPerSectorShift(); + if (!m_vol->cacheSafeRead(sector, dst, ns)) { + DBG_FAIL_MACRO; + goto fail; + } +#endif // USE_MULTI_SECTOR_IO + } else { + // read single sector + n = m_vol->bytesPerSector(); + if (!m_vol->cacheSafeRead(sector, dst)) { + DBG_FAIL_MACRO; + goto fail; + } + } + dst += n; + m_curPosition += n; + toRead -= n; + } + return count - toRead; + + fail: + m_error |= READ_ERROR; + return -1; +} +//------------------------------------------------------------------------------ +bool ExFatFile::remove(const char* path) { + ExFatFile file; + if (!file.open(this, path, O_WRONLY)) { + DBG_FAIL_MACRO; + goto fail; + } + return file.remove(); + + fail: + return false; +} +//------------------------------------------------------------------------------ +bool ExFatFile::seekSet(uint64_t pos) { + uint32_t nCur; + uint32_t nNew; + uint32_t tmp = m_curCluster; + // error if file not open + if (!isOpen()) { + DBG_FAIL_MACRO; + goto fail; + } + // Optimize O_APPEND writes. + if (pos == m_curPosition) { + return true; + } + if (pos == 0) { + // set position to start of file + m_curCluster = 0; + goto done; + } + if (isFile()) { + if (pos > m_validLength) { + DBG_FAIL_MACRO; + goto fail; + } + } + // calculate cluster index for new position + nNew = (pos - 1) >> m_vol->bytesPerClusterShift(); + if (isContiguous()) { + m_curCluster = m_firstCluster + nNew; + goto done; + } + // calculate cluster index for current position + nCur = (m_curPosition - 1) >> m_vol->bytesPerClusterShift(); + if (nNew < nCur || m_curPosition == 0) { + // must follow chain from first cluster + m_curCluster = isRoot() ? m_vol->rootDirectoryCluster() : m_firstCluster; + } else { + // advance from curPosition + nNew -= nCur; + } + while (nNew--) { + if (m_vol->fatGet(m_curCluster, &m_curCluster) <= 0) { + DBG_FAIL_MACRO; + goto fail; + } + } + + done: + m_curPosition = pos; + return true; + + fail: + m_curCluster = tmp; + return false; +} diff --git a/avr/cores/megacommand/SdFat/ExFatLib/ExFatFile.h b/avr/cores/megacommand/SdFat/ExFatLib/ExFatFile.h new file mode 100644 index 000000000..b1d4bd4c4 --- /dev/null +++ b/avr/cores/megacommand/SdFat/ExFatLib/ExFatFile.h @@ -0,0 +1,864 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef ExFatFile_h +#define ExFatFile_h +/** + * \file + * \brief ExFatFile class + */ +#include +#include +#include "../common/FsDateTime.h" +#include "../common/FsApiConstants.h" +#include "../common/FmtNumber.h" +#include "../common/FsName.h" +#include "ExFatPartition.h" + +class ExFatVolume; +//------------------------------------------------------------------------------ +/** Expression for path name separator. */ +#define isDirSeparator(c) ((c) == '/') +//------------------------------------------------------------------------------ +/** + * \class ExName_t + * \brief Internal type for file name - do not use in user apps. + */ +class ExName_t : public FsName { + public: + /** Length of UTF-16 name */ + size_t nameLength; + /** Hash for UTF-16 name */ + uint16_t nameHash; +}; +//------------------------------------------------------------------------------ +/** + * \class ExFatFile + * \brief Basic file class. + */ +class ExFatFile { + public: + /** Create an instance. */ + ExFatFile() {} + /** Create a file object and open it in the current working directory. + * + * \param[in] path A path for a file to be opened. + * + * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive + * OR of open flags. see FatFile::open(FatFile*, const char*, uint8_t). + */ + ExFatFile(const char* path, oflag_t oflag) { + open(path, oflag); + } + +#if DESTRUCTOR_CLOSES_FILE + ~ExFatFile() { + if (isOpen()) { + close(); + } + } +#endif // DESTRUCTOR_CLOSES_FILE + + /** The parenthesis operator. + * + * \return true if a file is open. + */ + operator bool() { + return isOpen(); + } + /** + * \return user settable file attributes for success else -1. + */ + int attrib() { + return isFileOrSubDir() ? m_attributes & FS_ATTRIB_COPY : -1; + } + /** Set file attributes + * + * \param[in] bits bit-wise or of selected attributes: FS_ATTRIB_READ_ONLY, + * FS_ATTRIB_HIDDEN, FS_ATTRIB_SYSTEM, FS_ATTRIB_ARCHIVE. + * + * \note attrib() will fail for set read-only if the file is open for write. + * \return true for success or false for failure. + */ + bool attrib(uint8_t bits); + /** \return The number of bytes available from the current position + * to EOF for normal files. INT_MAX is returned for very large files. + * + * available64() is recommended for very large files. + * + * Zero is returned for directory files. + * + */ + int available() { + uint64_t n = available64(); + return n > INT_MAX ? INT_MAX : n; + } + /** \return The number of bytes available from the current position + * to EOF for normal files. Zero is returned for directory files. + */ + uint64_t available64() { + return isFile() ? fileSize() - curPosition() : 0; + } + /** Clear all error bits. */ + void clearError() { + m_error = 0; + } + /** Clear writeError. */ + void clearWriteError() { + m_error &= ~WRITE_ERROR; + } + /** Close a file and force cached data and directory information + * to be written to the storage device. + * + * \return true for success or false for failure. + */ + bool close(); + /** Check for contiguous file and return its raw sector range. + * + * \param[out] bgnSector the first sector address for the file. + * \param[out] endSector the last sector address for the file. + * + * Parameters may be nullptr. + * + * \return true for success or false for failure. + */ + bool contiguousRange(uint32_t* bgnSector, uint32_t* endSector); + /** \return The current cluster number for a file or directory. */ + uint32_t curCluster() const {return m_curCluster;} + /** \return The current position for a file or directory. */ + uint64_t curPosition() const {return m_curPosition;} + /** \return Total data length for file. */ + uint64_t dataLength() const {return m_dataLength;} + /** \return Directory entry index. */ + uint32_t dirIndex() const {return m_dirPos.position/FS_DIR_SIZE;} + /** Test for the existence of a file in a directory + * + * \param[in] path Path of the file to be tested for. + * + * The calling instance must be an open directory file. + * + * dirFile.exists("TOFIND.TXT") searches for "TOFIND.TXT" in the directory + * dirFile. + * + * \return true if the file exists else false. + */ + bool exists(const char* path) { + ExFatFile file; + return file.open(this, path, O_RDONLY); + } + /** get position for streams + * \param[out] pos struct to receive position + */ + void fgetpos(fspos_t* pos) const; + /** + * Get a string from a file. + * + * fgets() reads bytes from a file into the array pointed to by \a str, until + * \a num - 1 bytes are read, or a delimiter is read and transferred to + * \a str, or end-of-file is encountered. The string is then terminated + * with a null byte. + * + * fgets() deletes CR, '\\r', from the string. This insures only a '\\n' + * terminates the string for Windows text files which use CRLF for newline. + * + * \param[out] str Pointer to the array where the string is stored. + * \param[in] num Maximum number of characters to be read + * (including the final null byte). Usually the length + * of the array \a str is used. + * \param[in] delim Optional set of delimiters. The default is "\n". + * + * \return For success fgets() returns the length of the string in \a str. + * If no data is read, fgets() returns zero for EOF or -1 if an error + * occurred. + */ + int fgets(char* str, int num, char* delim = nullptr); + /** \return The total number of bytes in a file. */ + uint64_t fileSize() const {return m_validLength;} + /** \return Address of first sector or zero for empty file. */ + uint32_t firstSector() const; + /** Set position for streams + * \param[in] pos struct with value for new position + */ + void fsetpos(const fspos_t* pos); + /** Arduino name for sync() */ + void flush() {sync();} + /** Get a file's access date and time. + * + * \param[out] pdate Packed date for directory entry. + * \param[out] ptime Packed time for directory entry. + * + * \return true for success or false for failure. + */ + bool getAccessDateTime(uint16_t* pdate, uint16_t* ptime); + /** Get a file's create date and time. + * + * \param[out] pdate Packed date for directory entry. + * \param[out] ptime Packed time for directory entry. + * + * \return true for success or false for failure. + */ + bool getCreateDateTime(uint16_t* pdate, uint16_t* ptime); + /** \return All error bits. */ + uint8_t getError() const { + return isOpen() ? m_error : 0XFF; + } + /** Get a file's modify date and time. + * + * \param[out] pdate Packed date for directory entry. + * \param[out] ptime Packed time for directory entry. + * + * \return true for success or false for failure. + */ + bool getModifyDateTime(uint16_t* pdate, uint16_t* ptime); + /** + * Get a file's name followed by a zero. + * + * \param[out] name An array of characters for the file's name. + * \param[in] size The size of the array in characters. + * \return the name length. + */ + size_t getName(char* name, size_t size) { +#if USE_UTF8_LONG_NAMES + return getName8(name, size); +#else // USE_UTF8_LONG_NAMES + return getName7(name, size); +#endif // USE_UTF8_LONG_NAMES + } + /** + * Get a file's ASCII name followed by a zero. + * + * \param[out] name An array of characters for the file's name. + * \param[in] size The size of the array in characters. + * \return the name length. + */ + size_t getName7(char* name, size_t size); + /** + * Get a file's UTF-8 name followed by a zero. + * + * \param[out] name An array of characters for the file's name. + * \param[in] size The size of the array in characters. + * \return the name length. + */ + size_t getName8(char* name, size_t size); + /** \return value of writeError */ + bool getWriteError() const { + return isOpen() ? m_error & WRITE_ERROR : true; + } + /** + * Check for FsBlockDevice busy. + * + * \return true if busy else false. + */ + bool isBusy(); + /** \return True if the file is contiguous. */ + bool isContiguous() const {return m_flags & FILE_FLAG_CONTIGUOUS;} + /** \return True if this is a directory. */ + bool isDir() const {return m_attributes & FILE_ATTR_DIR;} + /** \return True if this is a normal file. */ + bool isFile() const {return m_attributes & FILE_ATTR_FILE;} + /** \return True if this is a normal file or sub-directory. */ + bool isFileOrSubDir() const {return isFile() || isSubDir();} + /** \return True if this is a hidden. */ + bool isHidden() const {return m_attributes & FS_ATTRIB_HIDDEN;} + /** \return true if the file is open. */ + bool isOpen() const {return m_attributes;} + /** \return True if file is read-only */ + bool isReadOnly() const {return m_attributes & FS_ATTRIB_READ_ONLY;} + /** \return True if this is the root directory. */ + bool isRoot() const {return m_attributes & FILE_ATTR_ROOT;} + /** \return True file is readable. */ + bool isReadable() const {return m_flags & FILE_FLAG_READ;} + /** \return True if this is a sub-directory. */ + bool isSubDir() const {return m_attributes & FILE_ATTR_SUBDIR;} + /** \return True if this is a system file. */ + bool isSystem() const {return m_attributes & FS_ATTRIB_SYSTEM;} + /** \return True file is writable. */ + bool isWritable() const {return m_flags & FILE_FLAG_WRITE;} + /** List directory contents. + * + * \param[in] pr Print stream for list. + * \return true for success or false for failure. + */ + bool ls(print_t* pr); + /** List directory contents. + * + * \param[in] pr Print stream for list. + * + * \param[in] flags The inclusive OR of + * + * LS_DATE - %Print file modification date + * + * LS_SIZE - %Print file size. + * + * LS_R - Recursive list of sub-directories. + * + * \param[in] indent Amount of space before file name. Used for recursive + * list to indicate sub-directory level. + * + * \return true for success or false for failure. + */ + bool ls(print_t* pr, uint8_t flags, uint8_t indent = 0); + /** Make a new directory. + * + * \param[in] parent An open directory file that will + * contain the new directory. + * + * \param[in] path A path with a valid name for the new directory. + * + * \param[in] pFlag Create missing parent directories if true. + * + * \return true for success or false for failure. + */ + bool mkdir(ExFatFile* parent, const char* path, bool pFlag = true); + /** Open a file or directory by name. + * + * \param[in] dirFile An open directory containing the file to be opened. + * + * \param[in] path The path for a file to be opened. + * + * \param[in] oflag Values for \a oflag are constructed by a + * bitwise-inclusive OR of flags from the following list. + * Only one of O_RDONLY, O_READ, O_WRONLY, O_WRITE, or + * O_RDWR is allowed. + * + * O_RDONLY - Open for reading. + * + * O_READ - Same as O_RDONLY. + * + * O_WRONLY - Open for writing. + * + * O_WRITE - Same as O_WRONLY. + * + * O_RDWR - Open for reading and writing. + * + * O_APPEND - If set, the file offset shall be set to the end of the + * file prior to each write. + * + * O_AT_END - Set the initial position at the end of the file. + * + * O_CREAT - If the file exists, this flag has no effect except as noted + * under O_EXCL below. Otherwise, the file shall be created + * + * O_EXCL - If O_CREAT and O_EXCL are set, open() shall fail if the file + * exists. + * + * O_TRUNC - If the file exists and is a regular file, and the file is + * successfully opened and is not read only, its length shall be truncated + * to 0. + * + * WARNING: A given file must not be opened by more than one file object + * or file corruption may occur. + * + * \note Directory files must be opened read only. Write and truncation is + * not allowed for directory files. + * + * \return true for success or false for failure. + */ + bool open(ExFatFile* dirFile, const char* path, oflag_t oflag = O_RDONLY); + /** Open a file in the volume working directory. + * + * \param[in] vol Volume where the file is located. + * + * \param[in] path with a valid name for a file to be opened. + * + * \param[in] oflag bitwise-inclusive OR of open flags. + * See see open(ExFatFile*, const char*, uint8_t). + * + * \return true for success or false for failure. + */ + bool open(ExFatVolume* vol, const char* path, oflag_t oflag = O_RDONLY); + /** Open a file by index. + * + * \param[in] dirFile An open ExFatFile instance for the directory. + * + * \param[in] index The \a index of the directory entry for the file to be + * opened. The value for \a index is (directory file position)/32. + * + * \param[in] oflag bitwise-inclusive OR of open flags. + * See see ExFatFile::open(ExFatFile*, const char*, uint8_t). + * + * See open() by path for definition of flags. + * \return true for success or false for failure. + */ + bool open(ExFatFile* dirFile, uint32_t index, oflag_t oflag = O_RDONLY); + /** Open a file by index in the current working directory. + * + * \param[in] index The \a index of the directory entry for the file to be + * opened. The value for \a index is (directory file position)/32. + * + * \param[in] oflag bitwise-inclusive OR of open flags. + * See see FatFile::open(FatFile*, const char*, uint8_t). + * + * See open() by path for definition of flags. + * \return true for success or false for failure. + */ + bool open(uint32_t index, oflag_t oflag = O_RDONLY); + /** Open a file in the current working directory. + * + * \param[in] path A path with a valid name for a file to be opened. + * + * \param[in] oflag bitwise-inclusive OR of open flags. + * See see ExFatFile::open(ExFatFile*, const char*, uint8_t). + * + * \return true for success or false for failure. + */ + bool open(const char* path, oflag_t oflag = O_RDONLY); + /** Open the current working directory. + * + * \return true for success or false for failure. + */ + bool openCwd(); + /** Open the next file or subdirectory in a directory. + * + * \param[in] dirFile An open instance for the directory + * containing the file to be opened. + * + * \param[in] oflag bitwise-inclusive OR of open flags. + * See see open(ExFatFile*, const char*, uint8_t). + * + * \return true for success or false for failure. + */ + bool openNext(ExFatFile* dirFile, oflag_t oflag = O_RDONLY); + /** Open a volume's root directory. + * + * \param[in] vol The FAT volume containing the root directory to be opened. + * + * \return true for success or false for failure. + */ + bool openRoot(ExFatVolume* vol); + /** Return the next available byte without consuming it. + * + * \return The byte if no error and not at eof else -1; + */ + int peek(); + /** Allocate contiguous clusters to an empty file. + * + * The file must be empty with no clusters allocated. + * + * The file will have zero validLength and dataLength + * will equal the requested length. + * + * \param[in] length size of allocated space in bytes. + * \return true for success or false for failure. + */ + bool preAllocate(uint64_t length); + /** Print a file's access date and time + * + * \param[in] pr Print stream for output. + * + * \return true for success or false for failure. + */ + size_t printAccessDateTime(print_t* pr); + /** Print a file's creation date and time + * + * \param[in] pr Print stream for output. + * + * \return true for success or false for failure. + */ + size_t printCreateDateTime(print_t* pr); + /** Print a number followed by a field terminator. + * \param[in] value The number to be printed. + * \param[in] term The field terminator. Use '\\n' for CR LF. + * \param[in] prec Number of digits after decimal point. + * \return The number of bytes written or -1 if an error occurs. + */ + size_t printField(double value, char term, uint8_t prec = 2) { + char buf[24]; + char* str = buf + sizeof(buf); + if (term) { + *--str = term; + if (term == '\n') { + *--str = '\r'; + } + } + str = fmtDouble(str, value, prec, false); + return write(str, buf + sizeof(buf) - str); + } + /** Print a number followed by a field terminator. + * \param[in] value The number to be printed. + * \param[in] term The field terminator. Use '\\n' for CR LF. + * \param[in] prec Number of digits after decimal point. + * \return The number of bytes written or -1 if an error occurs. + */ + size_t printField(float value, char term, uint8_t prec = 2) { + return printField(static_cast(value), term, prec); + } + /** Print a number followed by a field terminator. + * \param[in] value The number to be printed. + * \param[in] term The field terminator. Use '\\n' for CR LF. + * \return The number of bytes written or -1 if an error occurs. + */ + template + size_t printField(Type value, char term) { + char sign = 0; + char buf[3*sizeof(Type) + 3]; + char* str = buf + sizeof(buf); + + if (term) { + *--str = term; + if (term == '\n') { + *--str = '\r'; + } + } + if (value < 0) { + value = -value; + sign = '-'; + } + if (sizeof(Type) < 4) { + str = fmtBase10(str, (uint16_t)value); + } else { + str = fmtBase10(str, (uint32_t)value); + } + if (sign) { + *--str = sign; + } + return write(str, &buf[sizeof(buf)] - str); + } + /** Print a file's size in bytes. + * \param[in] pr Prtin stream for the output. + * \return The number of bytes printed. + */ + size_t printFileSize(print_t* pr); + /** Print a file's modify date and time + * + * \param[in] pr Print stream for output. + * + * \return true for success or false for failure. + */ + size_t printModifyDateTime(print_t* pr); + /** Print a file's name + * + * \param[in] pr Print stream for output. + * + * \return length for success or zero for failure. + */ + size_t printName(print_t* pr) { +#if USE_UTF8_LONG_NAMES + return printName8(pr); +#else // USE_UTF8_LONG_NAMES + return printName7(pr); +#endif // USE_UTF8_LONG_NAMES + } + /** Print a file's ASCII name + * + * \param[in] pr Print stream for output. + * + * \return true for success or false for failure. + */ + size_t printName7(print_t* pr); + /** Print a file's UTF-8 name + * + * \param[in] pr Print stream for output. + * + * \return true for success or false for failure. + */ + size_t printName8(print_t* pr); + /** 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. + */ + int read() { + uint8_t b; + return read(&b, 1) == 1 ? b : -1; + } + /** Read data from a file starting at the current position. + * + * \param[out] buf Pointer to the location that will receive the data. + * + * \param[in] count Maximum number of bytes to read. + * + * \return For success read() returns the number of bytes read. + * A value less than \a nbyte, including zero, will be returned + * if end of file is reached. + * If an error occurs, read() returns -1. + */ + int read(void* buf, size_t count); + /** Remove a file. + * + * The directory entry and all data for the file are deleted. + * + * \note This function should not be used to delete the 8.3 version of a + * file that has a long name. For example if a file has the long name + * "New Text Document.txt" you should not delete the 8.3 name "NEWTEX~1.TXT". + * + * \return true for success or false for failure. + */ + bool remove(); + /** Remove a file. + * + * The directory entry and all data for the file are deleted. + * + * \param[in] path Path for the file to be removed. + * + * Example use: dirFile.remove(filenameToRemove); + * + * \note This function should not be used to delete the 8.3 version of a + * file that has a long name. For example if a file has the long name + * "New Text Document.txt" you should not delete the 8.3 name "NEWTEX~1.TXT". + * + * \return true for success or false for failure. + */ + bool remove(const char* path); + /** Rename a file or subdirectory. + * + * \param[in] newPath New path name for the file/directory. + * + * \return true for success or false for failure. + */ + bool rename(const char* newPath); + /** Rename a file or subdirectory. + * + * \param[in] dirFile Directory for the new path. + * \param[in] newPath New path name for the file/directory. + * + * \return true for success or false for failure. + */ + bool rename(ExFatFile* dirFile, const char* newPath); + /** Set the file's current position to zero. */ + void rewind() { + seekSet(0); + } + /** Remove a directory file. + * + * The directory file will be removed only if it is empty and is not the + * root directory. rmdir() follows DOS and Windows and ignores the + * read-only attribute for the directory. + * + * \note This function should not be used to delete the 8.3 version of a + * directory that has a long name. For example if a directory has the + * long name "New folder" you should not delete the 8.3 name "NEWFOL~1". + * + * \return true for success or false for failure. + */ + bool rmdir(); + /** 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(int64_t offset) { + return seekSet(m_curPosition + offset); + } + /** Set the files position to end-of-file + \a offset. See seekSet(). + * Can't be used for directory files since file size is not defined. + * \param[in] offset The new position in bytes from end-of-file. + * \return true for success or false for failure. + */ + bool seekEnd(int64_t offset = 0) { + return isFile() ? seekSet(m_validLength + offset) : false; + } + /** Sets a file's position. + * + * \param[in] pos The new position in bytes from the beginning of the file. + * + * \return true for success or false for failure. + */ + bool seekSet(uint64_t pos); + /** \return directory set count */ + uint8_t setCount() const {return m_setCount;} + /** The sync() call causes all modified data and directory fields + * to be written to the storage device. + * + * \return true for success or false for failure. + */ + bool sync(); + /** Truncate a file at the current file position. + * + * \return true for success or false for failure. + */ + /** Set a file's timestamps in its directory entry. + * + * \param[in] flags Values for \a flags are constructed by a + * bitwise-inclusive OR of flags from the following list + * + * T_ACCESS - Set the file's last access date and time. + * + * T_CREATE - Set the file's creation date and time. + * + * T_WRITE - Set the file's last write/modification date and time. + * + * \param[in] year Valid range 1980 - 2107 inclusive. + * + * \param[in] month Valid range 1 - 12 inclusive. + * + * \param[in] day Valid range 1 - 31 inclusive. + * + * \param[in] hour Valid range 0 - 23 inclusive. + * + * \param[in] minute Valid range 0 - 59 inclusive. + * + * \param[in] second Valid range 0 - 59 inclusive + * + * \note It is possible to set an invalid date since there is no check for + * the number of days in a month. + * + * \note + * Modify and access timestamps may be overwritten if a date time callback + * function has been set by dateTimeCallback(). + * + * \return true for success or false for failure. + */ + bool timestamp(uint8_t flags, uint16_t year, uint8_t month, uint8_t day, + uint8_t hour, uint8_t minute, uint8_t second); + /** Truncate a file at 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. + * + * \return true for success or false for failure. + */ + bool truncate(); + /** Truncate a file to a specified length. The current file position + * will be set to end of file. + * + * \param[in] length The desired length for the file. + * + * \return true for success or false for failure. + */ + bool truncate(uint64_t length) { + return seekSet(length) && truncate(); + } + + /** \return The valid number of bytes in a file. */ + uint64_t validLength() const {return m_validLength;} + /** Write a string to a file. Used by the Arduino Print class. + * \param[in] str Pointer to the string. + * Use getWriteError to check for errors. + * \return count of characters written for success or -1 for failure. + */ + size_t write(const char* str) { + return write(str, strlen(str)); + } + /** Write a single byte. + * \param[in] b The byte to be written. + * \return +1 for success or zero for failure. + */ + size_t write(uint8_t b) {return write(&b, 1);} + /** Write data to an open file. + * + * \note Data is moved to the cache but may not be written to the + * storage device until sync() is called. + * + * \param[in] buf Pointer to the location of the data to be written. + * + * \param[in] count Number of bytes to write. + * + * \return For success write() returns the number of bytes written, always + * \a count. If an error occurs, write() returns zero and writeError is set. + */ + size_t write(const void* buf, size_t count); +//------------------------------------------------------------------------------ +#if ENABLE_ARDUINO_SERIAL + /** List directory contents. + * + * \param[in] flags The inclusive OR of + * + * LS_DATE - %Print file modification date + * + * LS_SIZE - %Print file size. + * + * LS_R - Recursive list of subdirectories. + * + * \return true for success or false for failure. + */ + bool ls(uint8_t flags = 0) { + return ls(&Serial, flags); + } + /** Print a file's name. + * + * \return length for success or zero for failure. + */ + size_t printName() { + return ExFatFile::printName(&Serial); + } +#endif // ENABLE_ARDUINO_SERIAL + + private: + /** ExFatVolume allowed access to private members. */ + friend class ExFatVolume; + bool addCluster(); + bool addDirCluster(); + bool cmpName(const DirName_t* dirName, ExName_t* fname); + uint8_t* dirCache(uint8_t set, uint8_t options); + bool hashName(ExName_t* fname); + bool mkdir(ExFatFile* parent, ExName_t* fname); + + bool openPrivate(ExFatFile* dir, ExName_t* fname, oflag_t oflag); + bool parsePathName(const char* path, + ExName_t* fname, const char** ptr); + ExFatVolume* volume() const {return m_vol;} + bool syncDir(); + //---------------------------------------------------------------------------- + static const uint8_t WRITE_ERROR = 0X1; + static const uint8_t READ_ERROR = 0X2; + + /** This file has not been opened. */ + static const uint8_t FILE_ATTR_CLOSED = 0; + /** Entry for normal data file */ + static const uint8_t FILE_ATTR_FILE = 0X08; + /** Entry is for a subdirectory */ + static const uint8_t FILE_ATTR_SUBDIR = FS_ATTRIB_DIRECTORY; + /** Root directory */ + static const uint8_t FILE_ATTR_ROOT = 0X40; + /** Directory type bits */ + static const uint8_t FILE_ATTR_DIR = FILE_ATTR_SUBDIR | FILE_ATTR_ROOT; + + static const uint8_t FILE_FLAG_READ = 0X01; + static const uint8_t FILE_FLAG_WRITE = 0X02; + static const uint8_t FILE_FLAG_APPEND = 0X08; + static const uint8_t FILE_FLAG_CONTIGUOUS = 0X40; + static const uint8_t FILE_FLAG_DIR_DIRTY = 0X80; + + + uint64_t m_curPosition; + uint64_t m_dataLength; + uint64_t m_validLength; + uint32_t m_curCluster; + uint32_t m_firstCluster; + ExFatVolume* m_vol; + DirPos_t m_dirPos; + uint8_t m_setCount; + uint8_t m_attributes = FILE_ATTR_CLOSED; + uint8_t m_error = 0; + uint8_t m_flags = 0; +}; + +#include "../common/ArduinoFiles.h" +/** + * \class ExFile + * \brief exFAT file with Arduino Stream. + */ +class ExFile : public StreamFile { + public: + /** Opens the next file or folder in a directory. + * + * \param[in] oflag open flags. + * \return a FatStream object. + */ + ExFile openNextFile(oflag_t oflag = O_RDONLY) { + ExFile tmpFile; + tmpFile.openNext(this, oflag); + return tmpFile; + } +}; +#endif // ExFatFile_h diff --git a/avr/cores/megacommand/SdFat/ExFatLib/ExFatFilePrint.cpp b/avr/cores/megacommand/SdFat/ExFatLib/ExFatFilePrint.cpp new file mode 100644 index 000000000..733df8caf --- /dev/null +++ b/avr/cores/megacommand/SdFat/ExFatLib/ExFatFilePrint.cpp @@ -0,0 +1,228 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#define DBG_FILE "ExFatFilePrint.cpp" +#include "../common/DebugMacros.h" +#include "ExFatLib.h" +#include "../common/FsUtf.h" +//------------------------------------------------------------------------------ +bool ExFatFile::ls(print_t* pr) { + ExFatFile file; + if (!isDir()) { + DBG_FAIL_MACRO; + goto fail; + } + rewind(); + while (file.openNext(this, O_RDONLY)) { + if (!file.isHidden()) { + file.printName(pr); + if (file.isDir()) { + pr->write('/'); + } + pr->write('\r'); + pr->write('\n'); + } + file.close(); + } + if (getError()) { + DBG_FAIL_MACRO; + goto fail; + } + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +bool ExFatFile::ls(print_t* pr, uint8_t flags, uint8_t indent) { + ExFatFile file; + if (!isDir()) { + DBG_FAIL_MACRO; + goto fail; + } + rewind(); + while (file.openNext(this, O_RDONLY)) { + // indent for dir level + if (!file.isHidden() || (flags & LS_A)) { + for (uint8_t i = 0; i < indent; i++) { + pr->write(' '); + } + if (flags & LS_DATE) { + file.printModifyDateTime(pr); + pr->write(' '); + } + if (flags & LS_SIZE) { + file.printFileSize(pr); + pr->write(' '); + } + file.printName(pr); + if (file.isDir()) { + pr->write('/'); + } + pr->write('\r'); + pr->write('\n'); + if ((flags & LS_R) && file.isDir()) { + file.ls(pr, flags, indent + 2); + } + } + file.close(); + } + if (getError()) { + DBG_FAIL_MACRO; + goto fail; + } + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +size_t ExFatFile::printAccessDateTime(print_t* pr) { + uint16_t date; + uint16_t time; + if (getAccessDateTime(&date, &time)) { + return fsPrintDateTime(pr, date, time); + } + return 0; +} +//------------------------------------------------------------------------------ +size_t ExFatFile::printCreateDateTime(print_t* pr) { + uint16_t date; + uint16_t time; + if (getCreateDateTime(&date, &time)) { + return fsPrintDateTime(pr, date, time); + } + return 0; +} +//------------------------------------------------------------------------------ +size_t ExFatFile::printFileSize(print_t* pr) { + uint64_t n = m_validLength; + char buf[21]; + char *str = &buf[sizeof(buf) - 1]; + char *bgn = str - 12; + *str = '\0'; + do { + uint64_t m = n; + n /= 10; + *--str = m - 10*n + '0'; + } while (n); + while (str > bgn) { + *--str = ' '; + } + return pr->write(str); +} +//------------------------------------------------------------------------------ +size_t ExFatFile::printModifyDateTime(print_t* pr) { + uint16_t date; + uint16_t time; + if (getModifyDateTime(&date, &time)) { + return fsPrintDateTime(pr, date, time); + } + return 0; +} +//------------------------------------------------------------------------------ +size_t ExFatFile::printName7(print_t* pr) { + DirName_t* dn; + size_t n = 0; + uint8_t in; + uint8_t buf[15]; + if (!isOpen()) { + DBG_FAIL_MACRO; + goto fail; + } + for (uint8_t is = 2; is <= m_setCount; is++) { + dn = reinterpret_cast + (dirCache(is, FsCache::CACHE_FOR_READ)); + if (!dn || dn->type != EXFAT_TYPE_NAME) { + DBG_FAIL_MACRO; + goto fail; + } + for (in = 0; in < 15; in++) { + uint16_t c = getLe16(dn->unicode + 2*in); + if (!c) { + break; + } + buf[in] = c < 0X7F ? c : '?'; + n++; + } + pr->write(buf, in); + } + return n; + + fail: + return 0; +} +//------------------------------------------------------------------------------ +size_t ExFatFile::printName8(print_t *pr) { + DirName_t* dn; + uint16_t hs = 0; + uint32_t cp; + size_t n = 0; + uint8_t in; + char buf[5]; + if (!isOpen()) { + DBG_FAIL_MACRO; + goto fail; + } + for (uint8_t is = 2; is <= m_setCount; is++) { + dn = reinterpret_cast + (dirCache(is, FsCache::CACHE_FOR_READ)); + if (!dn || dn->type != EXFAT_TYPE_NAME) { + DBG_FAIL_MACRO; + goto fail; + } + for (in = 0; in < 15; in++) { + uint16_t c = getLe16(dn->unicode + 2*in); + if (hs) { + if (!FsUtf::isLowSurrogate(c)) { + DBG_FAIL_MACRO; + goto fail; + } + cp = FsUtf::u16ToCp(hs, c); + hs = 0; + } else if (!FsUtf::isSurrogate(c)) { + if (c == 0) { + break; + } + cp = c; + } else if (FsUtf::isHighSurrogate(c)) { + hs = c; + continue; + } else { + DBG_FAIL_MACRO; + goto fail; + } + char* str = FsUtf::cpToMb(cp, buf, buf + sizeof(buf)); + if (!str) { + DBG_FAIL_MACRO; + goto fail; + } + n += pr->write(buf, str - buf); + } + } + return n; + + fail: + return 0; +} diff --git a/avr/cores/megacommand/SdFat/ExFatLib/ExFatFileWrite.cpp b/avr/cores/megacommand/SdFat/ExFatLib/ExFatFileWrite.cpp new file mode 100644 index 000000000..398ac0b63 --- /dev/null +++ b/avr/cores/megacommand/SdFat/ExFatLib/ExFatFileWrite.cpp @@ -0,0 +1,756 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#define DBG_FILE "ExFatFileWrite.cpp" +#include "../common/DebugMacros.h" +#include "ExFatLib.h" +//============================================================================== +#if EXFAT_READ_ONLY +bool ExFatFile::mkdir(ExFatFile* parent, const char* path, bool pFlag) { + (void) parent; + (void)path; + (void)pFlag; + return false; +} +bool ExFatFile::preAllocate(uint64_t length) { + (void)length; + return false; +} +bool ExFatFile::rename(const char* newPath) { + (void)newPath; + return false; +} +bool ExFatFile::rename(ExFatFile* dirFile, const char* newPath) { + (void)dirFile; + (void)newPath; + return false; +} +bool ExFatFile::sync() { + return false; +} +bool ExFatFile::truncate() { + return false; +} +size_t ExFatFile::write(const void* buf, size_t nbyte) { + (void)buf; + (void)nbyte; + return false; +} +//============================================================================== +#else // EXFAT_READ_ONLY +//------------------------------------------------------------------------------ +static uint16_t exFatDirChecksum(const uint8_t* data, uint16_t checksum) { + bool skip = data[0] == EXFAT_TYPE_FILE; + for (size_t i = 0; i < 32; i += i == 1 && skip ? 3 : 1) { + checksum = ((checksum << 15) | (checksum >> 1)) + data[i]; + } + return checksum; +} +//------------------------------------------------------------------------------ +bool ExFatFile::addCluster() { + uint32_t find = m_vol->bitmapFind(m_curCluster ? m_curCluster + 1 : 0, 1); + if (find < 2) { + DBG_FAIL_MACRO; + goto fail; + } + if (!m_vol->bitmapModify(find, 1, 1)) { + DBG_FAIL_MACRO; + goto fail; + } + if (m_curCluster == 0) { + m_flags |= FILE_FLAG_CONTIGUOUS; + goto done; + } + if (isContiguous()) { + if (find == (m_curCluster + 1)) { + goto done; + } + // No longer contiguous so make FAT chain. + m_flags &= ~FILE_FLAG_CONTIGUOUS; + + for (uint32_t c = m_firstCluster; c < m_curCluster; c++) { + if (!m_vol->fatPut(c, c + 1)) { + DBG_FAIL_MACRO; + goto fail; + } + } + } + // New cluster is EOC. + if (!m_vol->fatPut(find, EXFAT_EOC)) { + DBG_FAIL_MACRO; + goto fail; + } + // Connect new cluster to existing chain. + if (m_curCluster) { + if (!m_vol->fatPut(m_curCluster, find)) { + DBG_FAIL_MACRO; + goto fail; + } + } + + done: + m_curCluster = find; + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +bool ExFatFile::addDirCluster() { + uint32_t sector; + uint32_t dl = isRoot() ? m_vol->rootLength() : m_dataLength; + uint8_t* cache; + dl += m_vol->bytesPerCluster(); + if (dl >= 0X4000000) { + DBG_FAIL_MACRO; + goto fail; + } + if (!addCluster()) { + DBG_FAIL_MACRO; + goto fail; + } + sector = m_vol->clusterStartSector(m_curCluster); + for (uint32_t i = 0; i < m_vol->sectorsPerCluster(); i++) { + cache = m_vol->dataCachePrepare(sector + i, + FsCache::CACHE_RESERVE_FOR_WRITE); + if (!cache) { + DBG_FAIL_MACRO; + goto fail; + } + memset(cache, 0, m_vol->bytesPerSector()); + } + if (!isRoot()) { + m_flags |= FILE_FLAG_DIR_DIRTY; + m_dataLength += m_vol->bytesPerCluster(); + m_validLength += m_vol->bytesPerCluster(); + } + return sync(); + + fail: + return false; +} +//------------------------------------------------------------------------------ +bool ExFatFile::mkdir(ExFatFile* parent, const char* path, bool pFlag) { + ExName_t fname; + ExFatFile tmpDir; + + if (isOpen() || !parent->isDir()) { + DBG_FAIL_MACRO; + goto fail; + } + if (isDirSeparator(*path)) { + while (isDirSeparator(*path)) { + path++; + } + if (!tmpDir.openRoot(parent->m_vol)) { + DBG_FAIL_MACRO; + goto fail; + } + parent = &tmpDir; + } + while (1) { + if (!parsePathName(path, &fname, &path)) { + DBG_FAIL_MACRO; + goto fail; + } + if (!*path) { + break; + } + if (!openPrivate(parent, &fname, O_RDONLY)) { + if (!pFlag || !mkdir(parent, &fname)) { + DBG_FAIL_MACRO; + goto fail; + } + } + tmpDir = *this; + parent = &tmpDir; + close(); + } + return mkdir(parent, &fname); + + fail: + return false; +} +//------------------------------------------------------------------------------ +bool ExFatFile::mkdir(ExFatFile* parent, ExName_t* fname) { + if (!parent->isDir()) { + DBG_FAIL_MACRO; + goto fail; + } + // create a normal file + if (!openPrivate(parent, fname, O_CREAT | O_EXCL | O_RDWR)) { + DBG_FAIL_MACRO; + goto fail; + } + // convert file to directory + m_attributes = FILE_ATTR_SUBDIR | FS_ATTRIB_ARCHIVE; + + // allocate and zero first cluster + if (!addDirCluster()) { + DBG_FAIL_MACRO; + goto fail; + } + m_firstCluster = m_curCluster; + + // Set to start of dir + rewind(); + m_flags = FILE_FLAG_READ | FILE_FLAG_CONTIGUOUS | FILE_FLAG_DIR_DIRTY; + return sync(); + + fail: + return false; +} +//------------------------------------------------------------------------------ +bool ExFatFile::preAllocate(uint64_t length) { + uint32_t find; + uint32_t need; + if (!length || !isWritable() || m_firstCluster) { + DBG_FAIL_MACRO; + goto fail; + } + need = 1 + ((length - 1) >> m_vol->bytesPerClusterShift()); + find = m_vol->bitmapFind(0, need); + if (find < 2) { + DBG_FAIL_MACRO; + goto fail; + } + if (!m_vol->bitmapModify(find, need, 1)) { + DBG_FAIL_MACRO; + goto fail; + } + m_dataLength = length; + m_firstCluster = find; + m_flags |= FILE_FLAG_DIR_DIRTY | FILE_FLAG_CONTIGUOUS; + if (!sync()) { + DBG_FAIL_MACRO; + goto fail; + } + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +bool ExFatFile::remove() { + uint8_t* cache; + if (!isWritable()) { + DBG_FAIL_MACRO; + goto fail; + } + // Free any clusters. + if (m_firstCluster) { + if (isContiguous()) { + uint32_t nc = 1 + ((m_dataLength - 1) >> m_vol->bytesPerClusterShift()); + if (!m_vol->bitmapModify(m_firstCluster, nc, 0)) { + DBG_FAIL_MACRO; + goto fail; + } + } else { + if (!m_vol->freeChain(m_firstCluster)) { + DBG_FAIL_MACRO; + goto fail; + } + } + } + + for (uint8_t is = 0; is <= m_setCount; is++) { + cache = dirCache(is, FsCache::CACHE_FOR_WRITE); + if (!cache) { + DBG_FAIL_MACRO; + goto fail; + } + // Mark entry not used. + cache[0] &= 0x7F; + } + // Set this file closed. + m_attributes = FILE_ATTR_CLOSED; + m_flags = 0; + + // Write entry to device. + return m_vol->cacheSync(); + + fail: + return false; +} +//------------------------------------------------------------------------------ +bool ExFatFile::rename(const char* newPath) { + return rename(m_vol->vwd(), newPath); +} +//------------------------------------------------------------------------------ +bool ExFatFile::rename(ExFatFile* dirFile, const char* newPath) { + ExFatFile file; + ExFatFile oldFile; + + // Must be an open file or subdirectory. + if (!(isFile() || isSubDir())) { + DBG_FAIL_MACRO; + goto fail; + } + // Can't move file to new volume. + if (m_vol != dirFile->m_vol) { + DBG_FAIL_MACRO; + goto fail; + } + if (!file.open(dirFile, newPath, O_CREAT | O_EXCL | O_WRONLY)) { + DBG_FAIL_MACRO; + goto fail; + } + oldFile = *this; + m_dirPos = file.m_dirPos; + m_setCount = file.m_setCount; + m_flags |= FILE_FLAG_DIR_DIRTY; + if (!sync()) { + DBG_FAIL_MACRO; + goto fail; + } + // Remove old directory entry; + oldFile.m_firstCluster = 0; + oldFile.m_flags = FILE_FLAG_WRITE; + oldFile.m_attributes = FILE_ATTR_FILE; + return oldFile.remove(); + + fail: + return false; +} +//------------------------------------------------------------------------------ +bool ExFatFile::rmdir() { + int n; + uint8_t dir[FS_DIR_SIZE]; + // must be open subdirectory + if (!isSubDir()) { + DBG_FAIL_MACRO; + goto fail; + } + rewind(); + + // make sure directory is empty + while (1) { + n = read(dir, FS_DIR_SIZE); + if (n == 0) { + break; + } + if (n != FS_DIR_SIZE || dir[0] & 0X80) { + DBG_FAIL_MACRO; + goto fail; + } + if (dir[0] == 0) { + break; + } + } + // convert empty directory to normal file for remove + m_attributes = FILE_ATTR_FILE; + m_flags |= FILE_FLAG_WRITE; + return remove(); + + fail: + return false; +} +//------------------------------------------------------------------------------ +bool ExFatFile::sync() { + if (!isOpen()) { + return true; + } + if (m_flags & FILE_FLAG_DIR_DIRTY) { + // clear directory dirty + m_flags &= ~FILE_FLAG_DIR_DIRTY; + return syncDir(); + } + if (!m_vol->cacheSync()) { + DBG_FAIL_MACRO; + goto fail; + } + return true; + + fail: + m_error |= WRITE_ERROR; + return false; +} +//------------------------------------------------------------------------------ +bool ExFatFile::syncDir() { + DirFile_t* df; + DirStream_t* ds; + uint8_t* cache; + uint16_t checksum = 0; + + for (uint8_t is = 0; is <= m_setCount ; is++) { + cache = dirCache(is, FsCache::CACHE_FOR_READ); + if (!cache) { + DBG_FAIL_MACRO; + goto fail; + } + switch (cache[0]) { + case EXFAT_TYPE_FILE: + df = reinterpret_cast(cache); + setLe16(df->attributes, m_attributes & FS_ATTRIB_COPY); + if (FsDateTime::callback) { + uint16_t date, time; + uint8_t ms10; + FsDateTime::callback(&date, &time, &ms10); + df->modifyTimeMs = ms10; + setLe16(df->modifyTime, time); + setLe16(df->modifyDate, date); + setLe16(df->accessTime, time); + setLe16(df->accessDate, date); + } + m_vol->dataCacheDirty(); + break; + + case EXFAT_TYPE_STREAM: + ds = reinterpret_cast(cache); + if (isContiguous()) { + ds->flags |= EXFAT_FLAG_CONTIGUOUS; + } else { + ds->flags &= ~EXFAT_FLAG_CONTIGUOUS; + } + setLe64(ds->validLength, m_validLength); + setLe32(ds->firstCluster, m_firstCluster); + setLe64(ds->dataLength, m_dataLength); + m_vol->dataCacheDirty(); + break; + + case EXFAT_TYPE_NAME: + break; + + default: + DBG_FAIL_MACRO; + goto fail; + break; + } + checksum = exFatDirChecksum(cache, checksum); + } + df = reinterpret_cast + (m_vol->dirCache(&m_dirPos, FsCache::CACHE_FOR_WRITE)); + if (!df) { + DBG_FAIL_MACRO; + goto fail; + } + setLe16(df->setChecksum, checksum); + if (!m_vol->cacheSync()) { + DBG_FAIL_MACRO; + goto fail; + } + return true; + + fail: + m_error |= WRITE_ERROR; + return false; +} +//------------------------------------------------------------------------------ +bool ExFatFile::timestamp(uint8_t flags, uint16_t year, uint8_t month, + uint8_t day, uint8_t hour, uint8_t minute, uint8_t second) { + DirFile_t* df; + uint8_t* cache; + uint16_t checksum = 0; + uint16_t date; + uint16_t time; + uint8_t ms10; + + if (!isFile() + || year < 1980 + || year > 2107 + || month < 1 + || month > 12 + || day < 1 + || day > 31 + || hour > 23 + || minute > 59 + || second > 59) { + DBG_FAIL_MACRO; + goto fail; + } + // update directory entry + if (!sync()) { + DBG_FAIL_MACRO; + goto fail; + } + + date = FS_DATE(year, month, day); + time = FS_TIME(hour, minute, second); + ms10 = second & 1 ? 100 : 0; + + for (uint8_t is = 0; is <= m_setCount; is++) { + cache = dirCache(is, FsCache::CACHE_FOR_READ); + if (!cache) { + DBG_FAIL_MACRO; + goto fail; + } + switch (cache[0]) { + case EXFAT_TYPE_FILE: + df = reinterpret_cast(cache); + setLe16(df->attributes, m_attributes & FS_ATTRIB_COPY); + m_vol->dataCacheDirty(); + if (flags & T_ACCESS) { + setLe16(df->accessTime, time); + setLe16(df->accessDate, date); + } + if (flags & T_CREATE) { + df->createTimeMs = ms10; + setLe16(df->createTime, time); + setLe16(df->createDate, date); + } + if (flags & T_WRITE) { + df->modifyTimeMs = ms10; + setLe16(df->modifyTime, time); + setLe16(df->modifyDate, date); + } + break; + + case EXFAT_TYPE_STREAM: + break; + + case EXFAT_TYPE_NAME: + break; + + default: + DBG_FAIL_MACRO; + goto fail; + break; + } + checksum = exFatDirChecksum(cache, checksum); + } + df = reinterpret_cast + (m_vol->dirCache(&m_dirPos, FsCache::CACHE_FOR_WRITE)); + if (!df) { + DBG_FAIL_MACRO; + goto fail; + } + setLe16(df->setChecksum, checksum); + if (!m_vol->cacheSync()) { + DBG_FAIL_MACRO; + goto fail; + } + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +bool ExFatFile::truncate() { + uint32_t toFree; + // error if not a normal file or read-only + if (!isWritable()) { + DBG_FAIL_MACRO; + goto fail; + } + if (m_firstCluster == 0) { + return true; + } + if (isContiguous()) { + uint32_t nc = 1 + ((m_dataLength - 1) >> m_vol->bytesPerClusterShift()); + if (m_curCluster) { + toFree = m_curCluster + 1; + nc -= 1 + m_curCluster - m_firstCluster; + } else { + toFree = m_firstCluster; + m_firstCluster = 0; + } + if (nc && !m_vol->bitmapModify(toFree, nc, 0)) { + DBG_FAIL_MACRO; + goto fail; + } + } else { + // need to free chain + if (m_curCluster) { + toFree = 0; + int8_t fg = m_vol->fatGet(m_curCluster, &toFree); + if (fg < 0) { + DBG_FAIL_MACRO; + goto fail; + } + if (fg) { + // current cluster is end of chain + if (!m_vol->fatPut(m_curCluster, EXFAT_EOC)) { + DBG_FAIL_MACRO; + goto fail; + } + } + } else { + toFree = m_firstCluster; + m_firstCluster = 0; + } + if (toFree) { + if (!m_vol->freeChain(toFree)) { + DBG_FAIL_MACRO; + goto fail; + } + } + } + m_dataLength = m_curPosition; + m_validLength = m_curPosition; + m_flags |= FILE_FLAG_DIR_DIRTY; + return sync(); + + fail: + return false; +} +//------------------------------------------------------------------------------ +size_t ExFatFile::write(const void* buf, size_t nbyte) { + // convert void* to uint8_t* - must be before goto statements + const uint8_t* src = reinterpret_cast(buf); + uint8_t* cache; + uint8_t cacheOption; + uint16_t sectorOffset; + uint32_t sector; + uint32_t clusterOffset; + + // number of bytes left to write - must be before goto statements + size_t toWrite = nbyte; + size_t n; + // error if not an open file or is read-only + if (!isWritable()) { + DBG_FAIL_MACRO; + goto fail; + } + // seek to end of file if append flag + if ((m_flags & FILE_FLAG_APPEND)) { + if (!seekSet(m_validLength)) { + DBG_FAIL_MACRO; + goto fail; + } + } + while (toWrite) { + clusterOffset = m_curPosition & m_vol->clusterMask(); + sectorOffset = clusterOffset & m_vol->sectorMask(); + if (clusterOffset == 0) { + // start of new cluster + if (m_curCluster != 0) { + int fg; + + if (isContiguous()) { + uint32_t lc = m_firstCluster; + lc += (m_dataLength - 1) >> m_vol->bytesPerClusterShift(); + if (m_curCluster < lc) { + m_curCluster++; + fg = 1; + } else { + fg = 0; + } + } else { + fg = m_vol->fatGet(m_curCluster, &m_curCluster); + if (fg < 0) { + DBG_FAIL_MACRO; + goto fail; + } + } + if (fg == 0) { + // add cluster if at end of chain + if (!addCluster()) { + DBG_FAIL_MACRO; + goto fail; + } + } + } else { + if (m_firstCluster == 0) { + // allocate first cluster of file + if (!addCluster()) { + DBG_FAIL_MACRO; + goto fail; + } + m_firstCluster = m_curCluster; + } else { + m_curCluster = m_firstCluster; + } + } + } + // sector for data write + sector = m_vol->clusterStartSector(m_curCluster) + + (clusterOffset >> m_vol->bytesPerSectorShift()); + + if (sectorOffset != 0 || toWrite < m_vol->bytesPerSector()) { + // partial sector - must use cache + // max space in sector + n = m_vol->bytesPerSector() - sectorOffset; + // lesser of space and amount to write + if (n > toWrite) { + n = toWrite; + } + + if (sectorOffset == 0 && m_curPosition >= m_validLength) { + // start of new sector don't need to read into cache + cacheOption = FsCache::CACHE_RESERVE_FOR_WRITE; + } else { + // rewrite part of sector + cacheOption = FsCache::CACHE_FOR_WRITE; + } + cache = m_vol->dataCachePrepare(sector, cacheOption); + if (!cache) { + DBG_FAIL_MACRO; + goto fail; + } + uint8_t* dst = cache + sectorOffset; + memcpy(dst, src, n); + if (m_vol->bytesPerSector() == (n + sectorOffset)) { + // Force write if sector is full - improves large writes. + if (!m_vol->dataCacheSync()) { + DBG_FAIL_MACRO; + goto fail; + } + } +#if USE_MULTI_SECTOR_IO + } else if (toWrite >= 2*m_vol->bytesPerSector()) { + // use multiple sector write command + uint32_t ns = toWrite >> m_vol->bytesPerSectorShift(); + // Limit writes to current cluster. + uint32_t maxNs = m_vol->sectorsPerCluster() + - (clusterOffset >> m_vol->bytesPerSectorShift()); + if (ns > maxNs) { + ns = maxNs; + } + n = ns << m_vol->bytesPerSectorShift(); + if (!m_vol->cacheSafeWrite(sector, src, ns)) { + DBG_FAIL_MACRO; + goto fail; + } +#endif // USE_MULTI_SECTOR_IO + } else { + n = m_vol->bytesPerSector(); + if (!m_vol->cacheSafeWrite(sector, src)) { + DBG_FAIL_MACRO; + goto fail; + } + } + m_curPosition += n; + src += n; + toWrite -= n; + if (m_curPosition > m_validLength) { + m_flags |= FILE_FLAG_DIR_DIRTY; + m_validLength = m_curPosition; + } + } + if (m_curPosition > m_dataLength) { + m_dataLength = m_curPosition; + // update fileSize and insure sync will update dir entry + m_flags |= FILE_FLAG_DIR_DIRTY; + } else if (FsDateTime::callback) { + // insure sync will update modified date and time + m_flags |= FILE_FLAG_DIR_DIRTY; + } + return nbyte; + + fail: + // return for write error + m_error |= WRITE_ERROR; + return 0; +} +#endif // EXFAT_READ_ONLY diff --git a/avr/cores/megacommand/SdFat/ExFatLib/ExFatFormatter.cpp b/avr/cores/megacommand/SdFat/ExFatLib/ExFatFormatter.cpp new file mode 100644 index 000000000..7ca5c4d02 --- /dev/null +++ b/avr/cores/megacommand/SdFat/ExFatLib/ExFatFormatter.cpp @@ -0,0 +1,363 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#define DBG_FILE "ExFatFormatter.cpp" +#include "../common/DebugMacros.h" +#include "../common/upcase.h" +#include "ExFatLib.h" +//------------------------------------------------------------------------------ +// Formatter assumes 512 byte sectors. +const uint32_t BOOT_BACKUP_OFFSET = 12; +const uint16_t BYTES_PER_SECTOR = 512; +const uint16_t SECTOR_MASK = BYTES_PER_SECTOR - 1; +const uint8_t BYTES_PER_SECTOR_SHIFT = 9; +const uint16_t MINIMUM_UPCASE_SKIP = 512; +const uint32_t BITMAP_CLUSTER = 2; +const uint32_t UPCASE_CLUSTER = 3; +const uint32_t ROOT_CLUSTER = 4; +//------------------------------------------------------------------------------ +#define PRINT_FORMAT_PROGRESS 1 +#if !PRINT_FORMAT_PROGRESS +#define writeMsg(pr, str) +#elif defined(__AVR__) +#define writeMsg(pr, str) if (pr) pr->print(F(str)) +#else // PRINT_FORMAT_PROGRESS +#define writeMsg(pr, str) if (pr) pr->write(str) +#endif // PRINT_FORMAT_PROGRESS +//------------------------------------------------------------------------------ +bool ExFatFormatter::format(FsBlockDevice* dev, uint8_t* secBuf, print_t* pr) { +#if !PRINT_FORMAT_PROGRESS +(void)pr; +#endif // !PRINT_FORMAT_PROGRESS + MbrSector_t* mbr; + ExFatPbs_t* pbs; + DirUpcase_t* dup; + DirBitmap_t* dbm; + DirLabel_t* label; + uint32_t bitmapSize; + uint32_t checksum = 0; + uint32_t clusterCount; + uint32_t clusterHeapOffset; + uint32_t fatLength; + uint32_t fatOffset; + uint32_t m; + uint32_t ns; + uint32_t partitionOffset; + uint32_t sector; + uint32_t sectorsPerCluster; + uint32_t volumeLength; + uint32_t sectorCount; + uint8_t sectorsPerClusterShift; + uint8_t vs; + + m_dev = dev; + m_secBuf = secBuf; + sectorCount = dev->sectorCount(); + // Min size is 512 MB + if (sectorCount < 0X100000) { + writeMsg(pr, "Device is too small\r\n"); + DBG_FAIL_MACRO; + goto fail; + } + // Determine partition layout. + for (m = 1, vs = 0; m && sectorCount > m; m <<= 1, vs++) {} + sectorsPerClusterShift = vs < 29 ? 8 : (vs - 11)/2; + sectorsPerCluster = 1UL << sectorsPerClusterShift; + fatLength = 1UL << (vs < 27 ? 13 : (vs + 1)/2); + fatOffset = fatLength; + partitionOffset = 2*fatLength; + clusterHeapOffset = 2*fatLength; + clusterCount = (sectorCount - 4*fatLength) >> sectorsPerClusterShift; + volumeLength = clusterHeapOffset + (clusterCount << sectorsPerClusterShift); + + // make Master Boot Record. Use fake CHS. + memset(secBuf, 0, BYTES_PER_SECTOR); + mbr = reinterpret_cast(secBuf); + mbr->part->beginCHS[0] = 1; + mbr->part->beginCHS[1] = 1; + mbr->part->beginCHS[2] = 0; + mbr->part->type = 7; + mbr->part->endCHS[0] = 0XFE; + mbr->part->endCHS[1] = 0XFF; + mbr->part->endCHS[2] = 0XFF; + setLe32(mbr->part->relativeSectors, partitionOffset); + setLe32(mbr->part->totalSectors, volumeLength); + setLe16(mbr->signature, MBR_SIGNATURE); + if (!dev->writeSector(0, secBuf)) { + DBG_FAIL_MACRO; + goto fail; + } + // Partition Boot sector. + memset(secBuf, 0, BYTES_PER_SECTOR); + pbs = reinterpret_cast(secBuf); + pbs->jmpInstruction[0] = 0XEB; + pbs->jmpInstruction[1] = 0X76; + pbs->jmpInstruction[2] = 0X90; + pbs->oemName[0] = 'E'; + pbs->oemName[1] = 'X'; + pbs->oemName[2] = 'F'; + pbs->oemName[3] = 'A'; + pbs->oemName[4] = 'T'; + pbs->oemName[5] = ' '; + pbs->oemName[6] = ' '; + pbs->oemName[7] = ' '; + setLe64(pbs->bpb.partitionOffset, partitionOffset); + setLe64(pbs->bpb.volumeLength, volumeLength); + setLe32(pbs->bpb.fatOffset, fatOffset); + setLe32(pbs->bpb.fatLength, fatLength); + setLe32(pbs->bpb.clusterHeapOffset, clusterHeapOffset); + setLe32(pbs->bpb.clusterCount, clusterCount); + setLe32(pbs->bpb.rootDirectoryCluster, ROOT_CLUSTER); + setLe32(pbs->bpb.volumeSerialNumber, sectorCount); + setLe16(pbs->bpb.fileSystemRevision, 0X100); + setLe16(pbs->bpb.volumeFlags, 0); + pbs->bpb.bytesPerSectorShift = BYTES_PER_SECTOR_SHIFT; + pbs->bpb.sectorsPerClusterShift = sectorsPerClusterShift; + pbs->bpb.numberOfFats = 1; + pbs->bpb.driveSelect = 0X80; + pbs->bpb.percentInUse = 0; + + // Fill boot code like official SDFormatter. + for (size_t i = 0; i < sizeof(pbs->bootCode); i++) { + pbs->bootCode[i] = 0XF4; + } + setLe16(pbs->signature, PBR_SIGNATURE); + for (size_t i = 0; i < BYTES_PER_SECTOR; i++) { + if (i == offsetof(ExFatPbs_t, bpb.volumeFlags[0]) || + i == offsetof(ExFatPbs_t, bpb.volumeFlags[1]) || + i == offsetof(ExFatPbs_t, bpb.percentInUse)) { + continue; + } + checksum = exFatChecksum(checksum, secBuf[i]); + } + sector = partitionOffset; + if (!dev->writeSector(sector, secBuf) || + !dev->writeSector(sector + BOOT_BACKUP_OFFSET , secBuf)) { + DBG_FAIL_MACRO; + goto fail; + } + sector++; + // Write eight Extended Boot Sectors. + memset(secBuf, 0, BYTES_PER_SECTOR); + setLe16(pbs->signature, PBR_SIGNATURE); + for (int j = 0; j < 8; j++) { + for (size_t i = 0; i < BYTES_PER_SECTOR; i++) { + checksum = exFatChecksum(checksum, secBuf[i]); + } + if (!dev->writeSector(sector, secBuf) || + !dev->writeSector(sector + BOOT_BACKUP_OFFSET , secBuf)) { + DBG_FAIL_MACRO; + goto fail; + } + sector++; + } + // Write OEM Parameter Sector and reserved sector. + memset(secBuf, 0, BYTES_PER_SECTOR); + for (int j = 0; j < 2; j++) { + for (size_t i = 0; i < BYTES_PER_SECTOR; i++) { + checksum = exFatChecksum(checksum, secBuf[i]); + } + if (!dev->writeSector(sector, secBuf) || + !dev->writeSector(sector + BOOT_BACKUP_OFFSET , secBuf)) { + DBG_FAIL_MACRO; + goto fail; + } + sector++; + } + // Write Boot CheckSum Sector. + for (size_t i = 0; i < BYTES_PER_SECTOR; i += 4) { + setLe32(secBuf + i, checksum); + } + if (!dev->writeSector(sector, secBuf) || + !dev->writeSector(sector + BOOT_BACKUP_OFFSET , secBuf)) { + DBG_FAIL_MACRO; + goto fail; + } + // Initialize FAT. + writeMsg(pr, "Writing FAT "); + sector = partitionOffset + fatOffset; + ns = ((clusterCount + 2)*4 + BYTES_PER_SECTOR - 1)/BYTES_PER_SECTOR; + + memset(secBuf, 0, BYTES_PER_SECTOR); + // Allocate two reserved clusters, bitmap, upcase, and root clusters. + secBuf[0] = 0XF8; + for (size_t i = 1; i < 20; i++) { + secBuf[i] = 0XFF; + } + for (uint32_t i = 0; i < ns; i++) { + if (i%(ns/32) == 0) { + writeMsg(pr, "."); + } + if (!dev->writeSector(sector + i, secBuf)) { + DBG_FAIL_MACRO; + goto fail; + } + if (i == 0) { + memset(secBuf, 0, BYTES_PER_SECTOR); + } + } + writeMsg(pr, "\r\n"); + // Write cluster two, bitmap. + sector = partitionOffset + clusterHeapOffset; + bitmapSize = (clusterCount + 7)/8; + ns = (bitmapSize + BYTES_PER_SECTOR - 1)/BYTES_PER_SECTOR; + if (ns > sectorsPerCluster) { + DBG_FAIL_MACRO; + goto fail; + } + memset(secBuf, 0, BYTES_PER_SECTOR); + // Allocate clusters for bitmap, upcase, and root. + secBuf[0] = 0X7; + for (uint32_t i = 0; i < ns; i++) { + if (!dev->writeSector(sector + i, secBuf)) { + DBG_FAIL_MACRO; + goto fail; + } + if (i == 0) { + secBuf[0] = 0; + } + } + // Write cluster three, upcase table. + writeMsg(pr, "Writing upcase table\r\n"); + if (!writeUpcase(partitionOffset + clusterHeapOffset + sectorsPerCluster)) { + DBG_FAIL_MACRO; + goto fail; + } + if (m_upcaseSize > BYTES_PER_SECTOR*sectorsPerCluster) { + DBG_FAIL_MACRO; + goto fail; + } + // Initialize first sector of root. + writeMsg(pr, "Writing root\r\n"); + ns = sectorsPerCluster; + sector = partitionOffset + clusterHeapOffset + 2*sectorsPerCluster; + memset(secBuf, 0, BYTES_PER_SECTOR); + + // Unused Label entry. + label = reinterpret_cast(secBuf); + label->type = EXFAT_TYPE_LABEL & 0X7F; + + // bitmap directory entry. + dbm = reinterpret_cast(secBuf + 32); + dbm->type = EXFAT_TYPE_BITMAP; + setLe32(dbm->firstCluster, BITMAP_CLUSTER); + setLe64(dbm->size, bitmapSize); + + // upcase directory entry. + dup = reinterpret_cast(secBuf + 64); + dup->type = EXFAT_TYPE_UPCASE; + setLe32(dup->checksum, m_upcaseChecksum); + setLe32(dup->firstCluster, UPCASE_CLUSTER); + setLe64(dup->size, m_upcaseSize); + + // Write root, cluster four. + for (uint32_t i = 0; i < ns; i++) { + if (!dev->writeSector(sector + i, secBuf)) { + DBG_FAIL_MACRO; + goto fail; + } + if (i == 0) { + memset(secBuf, 0, BYTES_PER_SECTOR); + } + } + writeMsg(pr, "Format done\r\n"); + return true; + + fail: + writeMsg(pr, "Format failed\r\n"); + return false; +} +//------------------------------------------------------------------------------ +bool ExFatFormatter::syncUpcase() { + uint16_t index = m_upcaseSize & SECTOR_MASK; + if (!index) { + return true; + } + for (size_t i = index; i < BYTES_PER_SECTOR; i++) { + m_secBuf[i] = 0; + } + return m_dev->writeSector(m_upcaseSector, m_secBuf); +} +//------------------------------------------------------------------------------ +bool ExFatFormatter::writeUpcaseByte(uint8_t b) { + uint16_t index = m_upcaseSize & SECTOR_MASK; + m_secBuf[index] = b; + m_upcaseChecksum = exFatChecksum(m_upcaseChecksum, b); + m_upcaseSize++; + if (index == SECTOR_MASK) { + return m_dev->writeSector(m_upcaseSector++, m_secBuf); + } + return true; +} +//------------------------------------------------------------------------------ +bool ExFatFormatter::writeUpcaseUnicode(uint16_t unicode) { + return writeUpcaseByte(unicode) && writeUpcaseByte(unicode >> 8); +} +//------------------------------------------------------------------------------ +bool ExFatFormatter::writeUpcase(uint32_t sector) { + uint32_t n; + uint32_t ns; + uint32_t ch = 0; + uint16_t uc; + + m_upcaseSize = 0; + m_upcaseChecksum = 0; + m_upcaseSector = sector; + + while (ch < 0X10000) { + uc = toUpcase(ch); + if (uc != ch) { + if (!writeUpcaseUnicode(uc)) { + DBG_FAIL_MACRO; + goto fail; + } + ch++; + } else { + for (n = ch + 1; n < 0X10000 && n == toUpcase(n); n++) {} + ns = n - ch; + if (ns >= MINIMUM_UPCASE_SKIP) { + if (!writeUpcaseUnicode(0XFFFF) || !writeUpcaseUnicode(ns)) { + DBG_FAIL_MACRO; + goto fail; + } + ch = n; + } else { + while (ch < n) { + if (!writeUpcaseUnicode(ch++)) { + DBG_FAIL_MACRO; + goto fail; + } + } + } + } + } + if (!syncUpcase()) { + DBG_FAIL_MACRO; + goto fail; + } + return true; + + fail: + return false; +} diff --git a/avr/cores/megacommand/SdFat/ExFatLib/ExFatFormatter.h b/avr/cores/megacommand/SdFat/ExFatLib/ExFatFormatter.h new file mode 100644 index 000000000..536be7d6b --- /dev/null +++ b/avr/cores/megacommand/SdFat/ExFatLib/ExFatFormatter.h @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef ExFatFormatter_h +#define ExFatFormatter_h +#include "../common/FsBlockDevice.h" +/** + * \class ExFatFormatter + * \brief Format an exFAT volume. + */ +class ExFatFormatter { + public: + /** + * Format an exFAT volume. + * + * \param[in] dev Block device for volume. + * \param[in] secBuf buffer for writing to volume. + * \param[in] pr Print device for progress output. + * + * \return true for success or false for failure. + */ + bool format(FsBlockDevice* dev, uint8_t* secBuf, print_t* pr = nullptr); + private: + bool syncUpcase(); + bool writeUpcase(uint32_t sector); + bool writeUpcaseByte(uint8_t b); + bool writeUpcaseUnicode(uint16_t unicode); + uint32_t m_upcaseSector; + uint32_t m_upcaseChecksum; + uint32_t m_upcaseSize; + FsBlockDevice* m_dev; + uint8_t* m_secBuf; +}; +#endif // ExFatFormatter_h diff --git a/avr/cores/megacommand/SdFat/ExFatLib/ExFatLib.h b/avr/cores/megacommand/SdFat/ExFatLib/ExFatLib.h new file mode 100644 index 000000000..6d9c39dda --- /dev/null +++ b/avr/cores/megacommand/SdFat/ExFatLib/ExFatLib.h @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef ExFatLib_h +#define ExFatLib_h +#include "ExFatVolume.h" +#include "ExFatFormatter.h" +#endif // ExFatLib_h diff --git a/avr/cores/megacommand/SdFat/ExFatLib/ExFatName.cpp b/avr/cores/megacommand/SdFat/ExFatLib/ExFatName.cpp new file mode 100644 index 000000000..fc1a20ed5 --- /dev/null +++ b/avr/cores/megacommand/SdFat/ExFatLib/ExFatName.cpp @@ -0,0 +1,194 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#define DBG_FILE "ExFatName.cpp" +#include "../common/DebugMacros.h" +#include "../common/upcase.h" +#include "../common/FsUtf.h" +#include "ExFatLib.h" +//------------------------------------------------------------------------------ +static char toUpper(char c) { + return 'a' <= c && c <= 'z' ? c - 'a' + 'A' : c; +} +//------------------------------------------------------------------------------ +inline uint16_t exFatHash(char c, uint16_t hash) { + uint8_t u = toUpper(c); + hash = ((hash << 15) | (hash >> 1)) + u; + hash = ((hash << 15) | (hash >> 1)); + return hash; +} +//------------------------------------------------------------------------------ +inline uint16_t exFatHash(uint16_t u, uint16_t hash) { + uint16_t c = toUpcase(u); + hash = ((hash << 15) | (hash >> 1)) + (c & 0XFF); + hash = ((hash << 15) | (hash >> 1)) + (c >> 8); + return hash; +} +//------------------------------------------------------------------------------ +bool ExFatFile::cmpName(const DirName_t* dirName, ExName_t* fname) { + for (uint8_t i = 0; i < 15; i++) { + uint16_t u = getLe16(dirName->unicode + 2*i); + if (fname->atEnd()) { + return u == 0; + } +#if USE_UTF8_LONG_NAMES + uint16_t cp = fname->get16(); + if (toUpcase(cp) != toUpcase(u)) { + return false; + } +#else // USE_UTF8_LONG_NAMES + char c = fname->getch(); + if (u >= 0x7F || toUpper(c) != toUpper(u)) { + return false; + } +#endif // USE_UTF8_LONG_NAMES + } + return true; +} +//------------------------------------------------------------------------------ +size_t ExFatFile::getName7(char* name, size_t count) { + DirName_t* dn; + size_t n = 0; + if (!isOpen()) { + DBG_FAIL_MACRO; + goto fail; + } + for (uint8_t is = 2; is <= m_setCount; is++) { + dn = reinterpret_cast + (dirCache(is, FsCache::CACHE_FOR_READ)); + if (!dn || dn->type != EXFAT_TYPE_NAME) { + DBG_FAIL_MACRO; + goto fail; + } + for (uint8_t in = 0; in < 15; in++) { + uint16_t c = getLe16(dn->unicode + 2*in); + if (c == 0) { + goto done; + } + if ((n + 1) >= count) { + DBG_FAIL_MACRO; + goto fail; + } + name[n++] = c < 0X7F ? c : '?'; + } + } + done: + name[n] = 0; + return n; + + fail: + *name = 0; + return 0; +} +//------------------------------------------------------------------------------ +size_t ExFatFile::getName8(char* name, size_t count) { + char* end = name + count; + char* str = name; + char* ptr; + DirName_t* dn; + uint16_t hs = 0; + uint32_t cp; + if (!isOpen()) { + DBG_FAIL_MACRO; + goto fail; + } + for (uint8_t is = 2; is <= m_setCount; is++) { + dn = reinterpret_cast + (dirCache(is, FsCache::CACHE_FOR_READ)); + if (!dn || dn->type != EXFAT_TYPE_NAME) { + DBG_FAIL_MACRO; + goto fail; + } + for (uint8_t in = 0; in < 15; in++) { + uint16_t c = getLe16(dn->unicode + 2*in); + if (hs) { + if (!FsUtf::isLowSurrogate(c)) { + DBG_FAIL_MACRO; + goto fail; + } + cp = FsUtf::u16ToCp(hs, c); + hs = 0; + } else if (!FsUtf::isSurrogate(c)) { + if (c == 0) { + goto done; + } + cp = c; + } else if (FsUtf::isHighSurrogate(c)) { + hs = c; + continue; + } else { + DBG_FAIL_MACRO; + goto fail; + } + // Save space for zero byte. + ptr = FsUtf::cpToMb(cp, str, end - 1); + if (!ptr) { + DBG_FAIL_MACRO; + goto fail; + } + str = ptr; + } + } + done: + *str = '\0'; + return str - name; + + fail: + *name = 0; + return 0; +} +//------------------------------------------------------------------------------ +bool ExFatFile::hashName(ExName_t* fname) { + uint16_t hash = 0; + fname->reset(); +#if USE_UTF8_LONG_NAMES + fname->nameLength = 0; + while (!fname->atEnd()) { + uint16_t u = fname->get16(); + if (u == 0XFFFF) { + DBG_FAIL_MACRO; + goto fail; + } + hash = exFatHash(u, hash); + fname->nameLength++; + } +#else // USE_UTF8_LONG_NAMES + while (!fname->atEnd()) { + // Convert to byte for smaller exFatHash. + char c = fname->getch(); + hash = exFatHash(c, hash); + } + fname->nameLength = fname->end - fname->begin; +#endif // USE_UTF8_LONG_NAMES + fname->nameHash = hash; + if (!fname->nameLength || fname->nameLength > EXFAT_MAX_NAME_LENGTH) { + DBG_FAIL_MACRO; + goto fail; + } + return true; + + fail: + return false; +} + diff --git a/avr/cores/megacommand/SdFat/ExFatLib/ExFatPartition.cpp b/avr/cores/megacommand/SdFat/ExFatLib/ExFatPartition.cpp new file mode 100644 index 000000000..b45e500bb --- /dev/null +++ b/avr/cores/megacommand/SdFat/ExFatLib/ExFatPartition.cpp @@ -0,0 +1,332 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#define DBG_FILE "ExFatPartition.cpp" +#include "../common/DebugMacros.h" +#include "ExFatLib.h" +//------------------------------------------------------------------------------ +// return 0 if error, 1 if no space, else start cluster. +uint32_t ExFatPartition::bitmapFind(uint32_t cluster, uint32_t count) { + uint32_t start = cluster ? cluster - 2 : m_bitmapStart; + if (start >= m_clusterCount) { + start = 0; + } + uint32_t endAlloc = start; + uint32_t bgnAlloc = start; + uint16_t sectorSize = 1 << m_bytesPerSectorShift; + size_t i = (start >> 3) & (sectorSize - 1); + uint8_t* cache; + uint8_t mask = 1 << (start & 7); + while (true) { + uint32_t sector = m_clusterHeapStartSector + + (endAlloc >> (m_bytesPerSectorShift + 3)); + cache = bitmapCachePrepare(sector, FsCache::CACHE_FOR_READ); + if (!cache) { + return 0; + } + for (; i < sectorSize; i++) { + for (; mask; mask <<= 1) { + endAlloc++; + if (!(mask & cache[i])) { + if ((endAlloc - bgnAlloc) == count) { + if (cluster == 0 && count == 1) { + // Start at found sector. bitmapModify may increase this. + m_bitmapStart = bgnAlloc; + } + return bgnAlloc + 2; + } + } else { + bgnAlloc = endAlloc; + } + if (endAlloc == start) { + return 1; + } + if (endAlloc >= m_clusterCount) { + endAlloc = bgnAlloc = 0; + i = sectorSize; + break; + } + } + mask = 1; + } + i = 0; + } + return 0; +} +//------------------------------------------------------------------------------ +bool ExFatPartition::bitmapModify(uint32_t cluster, + uint32_t count, bool value) { + uint32_t sector; + uint32_t start = cluster - 2; + size_t i; + uint8_t* cache; + uint8_t mask; + cluster -= 2; + if ((start + count) > m_clusterCount) { + DBG_FAIL_MACRO; + goto fail; + } + if (value) { + if (start <= m_bitmapStart && m_bitmapStart < (start + count)) { + m_bitmapStart = (start + count) < m_clusterCount ? start + count : 0; + } + } else { + if (start < m_bitmapStart) { + m_bitmapStart = start; + } + } + mask = 1 << (start & 7); + sector = m_clusterHeapStartSector + + (start >> (m_bytesPerSectorShift + 3)); + i = (start >> 3) & m_sectorMask; + while (true) { + cache = bitmapCachePrepare(sector++, FsCache::CACHE_FOR_WRITE); + if (!cache) { + DBG_FAIL_MACRO; + goto fail; + } + for (; i < m_bytesPerSector; i++) { + for (; mask; mask <<= 1) { + if (value == static_cast(cache[i] & mask)) { + DBG_FAIL_MACRO; + goto fail; + } + cache[i] ^= mask; + if (--count == 0) { + return true; + } + } + mask = 1; + } + i = 0; + } + + fail: + return false; +} +//------------------------------------------------------------------------------ +uint32_t ExFatPartition::chainSize(uint32_t cluster) { + uint32_t n = 0; + int8_t status; + do { + status = fatGet(cluster, & cluster); + if (status < 0) return 0; + n++; + } while (status); + return n; +} +//------------------------------------------------------------------------------ +uint8_t* ExFatPartition::dirCache(DirPos_t* pos, uint8_t options) { + uint32_t sector = clusterStartSector(pos->cluster); + sector += (m_clusterMask & pos->position) >> m_bytesPerSectorShift; + uint8_t* cache = dataCachePrepare(sector, options); + return cache ? cache + (pos->position & m_sectorMask) : nullptr; +} +//------------------------------------------------------------------------------ +// return -1 error, 0 EOC, 1 OK +int8_t ExFatPartition::dirSeek(DirPos_t* pos, uint32_t offset) { + int8_t status; + uint32_t tmp = (m_clusterMask & pos->position) + offset; + pos->position += offset; + tmp >>= bytesPerClusterShift(); + while (tmp--) { + if (pos->isContiguous) { + pos->cluster++; + } else { + status = fatGet(pos->cluster, &pos->cluster); + if (status != 1) { + return status; + } + } + } + return 1; +} +//------------------------------------------------------------------------------ +// return -1 error, 0 EOC, 1 OK +int8_t ExFatPartition::fatGet(uint32_t cluster, uint32_t* value) { + uint8_t* cache; + uint32_t next; + uint32_t sector; + + if (cluster > (m_clusterCount + 1)) { + DBG_FAIL_MACRO; + return -1; + } + sector = m_fatStartSector + (cluster >> (m_bytesPerSectorShift - 2)); + + cache = dataCachePrepare(sector, FsCache::CACHE_FOR_READ); + if (!cache) { + return -1; + } + next = getLe32(cache + ((cluster << 2) & m_sectorMask)); + if (next == EXFAT_EOC) { + return 0; + } + *value = next; + return 1; +} +//------------------------------------------------------------------------------ +bool ExFatPartition::fatPut(uint32_t cluster, uint32_t value) { + uint32_t sector; + uint8_t* cache; + if (cluster < 2 || cluster > (m_clusterCount + 1)) { + DBG_FAIL_MACRO; + goto fail; + } + sector = m_fatStartSector + (cluster >> (m_bytesPerSectorShift - 2)); + cache = dataCachePrepare(sector, FsCache::CACHE_FOR_WRITE); + if (!cache) { + DBG_FAIL_MACRO; + goto fail; + } + setLe32(cache + ((cluster << 2) & m_sectorMask), value); + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +bool ExFatPartition::freeChain(uint32_t cluster) { + uint32_t next; + uint32_t start = cluster; + int8_t status; + do { + status = fatGet(cluster, &next); + if (status < 0) { + DBG_FAIL_MACRO; + goto fail; + } + if (!fatPut(cluster, 0)) { + DBG_FAIL_MACRO; + goto fail; + } + if (status == 0 || (cluster + 1) != next) { + if (!bitmapModify(start, cluster - start + 1, 0)) { + DBG_FAIL_MACRO; + goto fail; + } + start = next; + } + cluster = next; + } while (status); + + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +int32_t ExFatPartition::freeClusterCount() { + uint32_t nc = 0; + uint32_t sector = m_clusterHeapStartSector; + uint32_t usedCount = 0; + uint8_t* cache; + + while (true) { + cache = dataCachePrepare(sector++, FsCache::CACHE_FOR_READ); + if (!cache) { + return -1; + } + for (size_t i = 0; i < m_bytesPerSector; i++) { + if (cache[i] == 0XFF) { + usedCount+= 8; + } else if (cache[i]) { + for (uint8_t mask = 1; mask ; mask <<=1) { + if ((mask & cache[i])) { + usedCount++; + } + } + } + nc += 8; + if (nc >= m_clusterCount) { + return m_clusterCount - usedCount; + } + } + } +} +//------------------------------------------------------------------------------ +bool ExFatPartition::init(FsBlockDevice* dev, uint8_t part, uint32_t volStart) { + pbs_t* pbs; + BpbExFat_t* bpb; + MbrSector_t* mbr; + m_fatType = 0; + m_blockDev = dev; + cacheInit(m_blockDev); + // if part == 0 assume super floppy with FAT boot sector in sector zero + // if part > 0 assume mbr volume with partition table + if (part) { + if (part > 4) { + DBG_FAIL_MACRO; + goto fail; + } + mbr = reinterpret_cast + (dataCachePrepare(0, FsCache::CACHE_FOR_READ)); + if (!mbr) { + DBG_FAIL_MACRO; + goto fail; + } + MbrPart_t* mp = mbr->part + part - 1; + if (mp->type == 0 || (mp->boot != 0 && mp->boot != 0X80)) { + DBG_FAIL_MACRO; + goto fail; + } + volStart = getLe32(mp->relativeSectors); + } + pbs = reinterpret_cast + (dataCachePrepare(volStart, FsCache::CACHE_FOR_READ)); + if (!pbs) { + DBG_FAIL_MACRO; + goto fail; + } + if (strncmp(pbs->oemName, "EXFAT", 5)) { + DBG_FAIL_MACRO; + goto fail; + } + bpb = reinterpret_cast(pbs->bpb); + if (bpb->bytesPerSectorShift != m_bytesPerSectorShift) { + DBG_FAIL_MACRO; + goto fail; + } + m_fatStartSector = volStart + getLe32(bpb->fatOffset); + m_fatLength = getLe32(bpb->fatLength); + m_clusterHeapStartSector = volStart + getLe32(bpb->clusterHeapOffset); + m_clusterCount = getLe32(bpb->clusterCount); + m_rootDirectoryCluster = getLe32(bpb->rootDirectoryCluster); + m_sectorsPerClusterShift = bpb->sectorsPerClusterShift; + m_bytesPerCluster = 1UL << (m_bytesPerSectorShift + m_sectorsPerClusterShift); + m_clusterMask = m_bytesPerCluster - 1; + // Set m_bitmapStart to first free cluster. + m_bitmapStart = 0; + bitmapFind(0, 1); + m_fatType = FAT_TYPE_EXFAT; + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +uint32_t ExFatPartition::rootLength() { + uint32_t nc = chainSize(m_rootDirectoryCluster); + return nc << bytesPerClusterShift(); +} diff --git a/avr/cores/megacommand/SdFat/ExFatLib/ExFatPartition.h b/avr/cores/megacommand/SdFat/ExFatLib/ExFatPartition.h new file mode 100644 index 000000000..4131d44b2 --- /dev/null +++ b/avr/cores/megacommand/SdFat/ExFatLib/ExFatPartition.h @@ -0,0 +1,231 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef ExFatPartition_h +#define ExFatPartition_h +/** + * \file + * \brief ExFatPartition include file. + */ +#include "../common/SysCall.h" +#include "../common/FsBlockDevice.h" +#include "../common/FsCache.h" +#include "../common/FsStructs.h" +/** Set EXFAT_READ_ONLY non-zero for read only */ +#ifndef EXFAT_READ_ONLY +#define EXFAT_READ_ONLY 0 +#endif // EXFAT_READ_ONLY +/** Type for exFAT partition */ +const uint8_t FAT_TYPE_EXFAT = 64; + +class ExFatFile; +//------------------------------------------------------------------------------ +/** + * \struct DirPos_t + * \brief Internal type for position in directory file. + */ +struct DirPos_t { + /** current cluster */ + uint32_t cluster; + /** offset */ + uint32_t position; + /** directory is contiguous */ + bool isContiguous; +}; +//============================================================================== +/** + * \class ExFatPartition + * \brief Access exFat partitions on raw file devices. + */ +class ExFatPartition { + public: + ExFatPartition() {} + /** \return the number of bytes in a cluster. */ + uint32_t bytesPerCluster() const {return m_bytesPerCluster;} + /** \return the power of two for bytesPerCluster. */ + uint8_t bytesPerClusterShift() const { + return m_bytesPerSectorShift + m_sectorsPerClusterShift; + } + /** \return the number of bytes in a sector. */ + uint16_t bytesPerSector() const {return m_bytesPerSector;} + /** \return the power of two for bytesPerSector. */ + uint8_t bytesPerSectorShift() const {return m_bytesPerSectorShift;} + + /** Clear the cache and returns a pointer to the cache. Not for normal apps. + * \return A pointer to the cache buffer or zero if an error occurs. + */ + uint8_t* cacheClear() { + return m_dataCache.clear(); + } + /** \return the cluster count for the partition. */ + uint32_t clusterCount() const {return m_clusterCount;} + /** \return the cluster heap start sector. */ + uint32_t clusterHeapStartSector() const {return m_clusterHeapStartSector;} + /** End access to volume + * \return pointer to sector size buffer for format. + */ + uint8_t* end() { + m_fatType = 0; + return cacheClear(); + } + /** \return the FAT length in sectors */ + uint32_t fatLength() const {return m_fatLength;} + /** \return the FAT start sector number. */ + uint32_t fatStartSector() const {return m_fatStartSector;} + /** \return Type FAT_TYPE_EXFAT for exFAT partition or zero for error. */ + uint8_t fatType() const {return m_fatType;} + /** \return free cluster count or -1 if an error occurs. */ + int32_t freeClusterCount(); + /** Initialize a exFAT partition. + * \param[in] dev The blockDevice for the partition. + * \param[in] part The partition to be used. Legal values for \a part are + * 1-4 to use the corresponding partition on a device formatted with + * a MBR, Master Boot Record, or zero if the device is formatted as + * a super floppy with the FAT boot sector in sector volStart. + * \param[in] volStart location of volume if part is zero. + * + * \return true for success or false for failure. + */ + bool init(FsBlockDevice* dev, uint8_t part, uint32_t volStart = 0); + /** + * Check for device busy. + * + * \return true if busy else false. + */ + bool isBusy() {return m_blockDev->isBusy();} + /** \return the root directory start cluster number. */ + uint32_t rootDirectoryCluster() const {return m_rootDirectoryCluster;} + /** \return the root directory length. */ + uint32_t rootLength(); + /** \return the number of sectors in a cluster. */ + uint32_t sectorsPerCluster() const {return 1UL << m_sectorsPerClusterShift;} +#ifndef DOXYGEN_SHOULD_SKIP_THIS + uint32_t __attribute__((error("use sectorsPerCluster()"))) blocksPerCluster(); +#endif // DOXYGEN_SHOULD_SKIP_THIS + /** \return the power of two for sectors per cluster. */ + uint8_t sectorsPerClusterShift() const {return m_sectorsPerClusterShift;} + //---------------------------------------------------------------------------- +#ifndef DOXYGEN_SHOULD_SKIP_THIS + void checkUpcase(print_t* pr); + bool printDir(print_t* pr, ExFatFile* file); + void dmpBitmap(print_t* pr); + void dmpCluster(print_t* pr, uint32_t cluster, + uint32_t offset, uint32_t count); + void dmpFat(print_t* pr, uint32_t start, uint32_t count); + void dmpSector(print_t* pr, uint32_t sector); + bool printVolInfo(print_t* pr); + void printFat(print_t* pr); + void printUpcase(print_t* pr); +#endif // DOXYGEN_SHOULD_SKIP_THIS + //---------------------------------------------------------------------------- + private: + /** ExFatFile allowed access to private members. */ + friend class ExFatFile; + uint32_t bitmapFind(uint32_t cluster, uint32_t count); + bool bitmapModify(uint32_t cluster, uint32_t count, bool value); + //---------------------------------------------------------------------------- + // Cache functions. + uint8_t* bitmapCachePrepare(uint32_t sector, uint8_t option) { +#if USE_EXFAT_BITMAP_CACHE + return m_bitmapCache.prepare(sector, option); +#else // USE_EXFAT_BITMAP_CACHE + return m_dataCache.prepare(sector, option); +#endif // USE_EXFAT_BITMAP_CACHE + } + void cacheInit(FsBlockDevice* dev) { +#if USE_EXFAT_BITMAP_CACHE + m_bitmapCache.init(dev); +#endif // USE_EXFAT_BITMAP_CACHE + m_dataCache.init(dev); + } + bool cacheSync() { +#if USE_EXFAT_BITMAP_CACHE + return m_bitmapCache.sync() && m_dataCache.sync() && syncDevice(); +#else // USE_EXFAT_BITMAP_CACHE + return m_dataCache.sync() && syncDevice(); +#endif // USE_EXFAT_BITMAP_CACHE + } + void dataCacheDirty() {m_dataCache.dirty();} + void dataCacheInvalidate() {m_dataCache.invalidate();} + uint8_t* dataCachePrepare(uint32_t sector, uint8_t option) { + return m_dataCache.prepare(sector, option); + } + uint32_t dataCacheSector() {return m_dataCache.sector();} + bool dataCacheSync() {return m_dataCache.sync();} + //---------------------------------------------------------------------------- + uint32_t clusterMask() const {return m_clusterMask;} + uint32_t clusterStartSector(uint32_t cluster) { + return m_clusterHeapStartSector + + ((cluster - 2) << m_sectorsPerClusterShift); + } + uint8_t* dirCache(DirPos_t* pos, uint8_t options); + int8_t dirSeek(DirPos_t* pos, uint32_t offset); + int8_t fatGet(uint32_t cluster, uint32_t* value); + bool fatPut(uint32_t cluster, uint32_t value); + uint32_t chainSize(uint32_t cluster); + bool freeChain(uint32_t cluster); + uint16_t sectorMask() const {return m_sectorMask;} + bool syncDevice() { + return m_blockDev->syncDevice(); + } + bool cacheSafeRead(uint32_t sector, uint8_t* dst) { + return m_dataCache.cacheSafeRead(sector, dst); + } + bool cacheSafeWrite(uint32_t sector, const uint8_t* src) { + return m_dataCache.cacheSafeWrite(sector, src); + } + bool cacheSafeRead(uint32_t sector, uint8_t* dst, size_t count) { + return m_dataCache.cacheSafeRead(sector, dst, count); + } + bool cacheSafeWrite(uint32_t sector, const uint8_t* src, size_t count) { + return m_dataCache.cacheSafeWrite(sector, src, count); + } + bool readSector(uint32_t sector, uint8_t* dst) { + return m_blockDev->readSector(sector, dst); + } + bool writeSector(uint32_t sector, const uint8_t* src) { + return m_blockDev->writeSector(sector, src); + } + //---------------------------------------------------------------------------- + static const uint8_t m_bytesPerSectorShift = 9; + static const uint16_t m_bytesPerSector = 1 << m_bytesPerSectorShift; + static const uint16_t m_sectorMask = m_bytesPerSector - 1; + //---------------------------------------------------------------------------- +#if USE_EXFAT_BITMAP_CACHE + FsCache m_bitmapCache; +#endif // USE_EXFAT_BITMAP_CACHE + FsCache m_dataCache; + uint32_t m_bitmapStart; + uint32_t m_fatStartSector; + uint32_t m_fatLength; + uint32_t m_clusterHeapStartSector; + uint32_t m_clusterCount; + uint32_t m_rootDirectoryCluster; + uint32_t m_clusterMask; + uint32_t m_bytesPerCluster; + FsBlockDevice* m_blockDev; + uint8_t m_fatType = 0; + uint8_t m_sectorsPerClusterShift; +}; +#endif // ExFatPartition_h diff --git a/avr/cores/megacommand/SdFat/BlockDriver.h b/avr/cores/megacommand/SdFat/ExFatLib/ExFatVolume.cpp similarity index 71% rename from avr/cores/megacommand/SdFat/BlockDriver.h rename to avr/cores/megacommand/SdFat/ExFatLib/ExFatVolume.cpp index 71acdfd4f..2c4fa47fa 100644 --- a/avr/cores/megacommand/SdFat/BlockDriver.h +++ b/avr/cores/megacommand/SdFat/ExFatLib/ExFatVolume.cpp @@ -1,5 +1,5 @@ /** - * Copyright (c) 20011-2017 Bill Greiman + * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -22,19 +22,24 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ - /** - * \file - * \brief Define block driver. - */ -#ifndef BlockDriver_h -#define BlockDriver_h -#include "FatLib/BaseBlockDriver.h" -#include "SdCard/SdSpiCard.h" +#define DBG_FILE "ExFatVolume.cpp" +#include "../common/DebugMacros.h" +#include "ExFatLib.h" +ExFatVolume* ExFatVolume::m_cwv = nullptr; //----------------------------------------------------------------------------- -/** typedef for BlockDriver */ -#if ENABLE_EXTENDED_TRANSFER_CLASS || ENABLE_SDIO_CLASS -typedef BaseBlockDriver BlockDriver; -#else // ENABLE_EXTENDED_TRANSFER_CLASS || ENABLE_SDIO_CLASS -typedef SdSpiCard BlockDriver; -#endif // ENABLE_EXTENDED_TRANSFER_CLASS || ENABLE_SDIO_CLASS -#endif // BlockDriver_h +bool ExFatVolume::chdir(const char* path) { + ExFatFile dir; + if (!dir.open(vwd(), path, O_RDONLY)) { + DBG_FAIL_MACRO; + goto fail; + } + if (!dir.isDir()) { + DBG_FAIL_MACRO; + goto fail; + } + m_vwd = dir; + return true; + + fail: + return false; +} diff --git a/avr/cores/megacommand/SdFat/ExFatLib/ExFatVolume.h b/avr/cores/megacommand/SdFat/ExFatLib/ExFatVolume.h new file mode 100644 index 000000000..ac5124b64 --- /dev/null +++ b/avr/cores/megacommand/SdFat/ExFatLib/ExFatVolume.h @@ -0,0 +1,357 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef ExFatVolume_h +#define ExFatVolume_h +#include "ExFatFile.h" +//============================================================================== +/** + * \class ExFatVolume + * \brief exFAT volume. + */ +class ExFatVolume : public ExFatPartition { + public: + ExFatVolume() {} + /** Get file's user settable attributes. + * \param[in] path path to file. + * \return user settable file attributes for success else -1. + */ + int attrib(const char* path) { + ExFatFile tmpFile; + return tmpFile.open(this, path, O_RDONLY) ? tmpFile.attrib() : -1; + } + /** Set file's user settable attributes. + * \param[in] path path to file. + * \param[in] bits bit-wise or of selected attributes: FS_ATTRIB_READ_ONLY, + * FS_ATTRIB_HIDDEN, FS_ATTRIB_SYSTEM, FS_ATTRIB_ARCHIVE. + * + * \return true for success or false for failure. + */ + bool attrib(const char* path, uint8_t bits) { + ExFatFile tmpFile; + return tmpFile.open(this, path, O_RDONLY) ? tmpFile.attrib(bits) : false; + } + /** + * Initialize an FatVolume object. + * \param[in] dev Device block driver. + * \param[in] setCwv Set current working volume if true. + * \param[in] part Partition to initialize. + * \param[in] volStart Start sector of volume if part is zero. + * \return true for success or false for failure. + */ + bool begin(FsBlockDevice* dev, bool setCwv = true, + uint8_t part = 1, uint32_t volStart = 0) { + if (!init(dev, part, volStart)) { + return false; + } + if (!chdir()) { + return false; + } + if (setCwv || !m_cwv) { + m_cwv = this; + } + return true; + } + /** + * Set volume working directory to root. + * \return true for success or false for failure. + */ + bool chdir() { + m_vwd.close(); + return m_vwd.openRoot(this); + } + /** + * Set volume working directory. + * \param[in] path Path for volume working directory. + * \return true for success or false for failure. + */ + bool chdir(const char* path); + + /** Change global working volume to this volume. */ + void chvol() {m_cwv = this;} + + /** + * Test for the existence of a file. + * + * \param[in] path Path of the file to be tested for. + * + * \return true if the file exists else false. + */ + bool exists(const char* path) { + ExFatFile tmp; + return tmp.open(this, path, O_RDONLY); + } + //---------------------------------------------------------------------------- + /** List the directory contents of the root directory. + * + * \param[in] pr Print stream for list. + * + * \param[in] flags The inclusive OR of + * + * LS_DATE - %Print file modification date + * + * LS_SIZE - %Print file size. + * + * LS_R - Recursive list of subdirectories. + * + * \return true for success or false for failure. + */ + bool ls(print_t* pr, uint8_t flags = 0) { + return m_vwd.ls(pr, flags); + } + /** List the contents of a directory. + * + * \param[in] pr Print stream for list. + * + * \param[in] path directory to list. + * + * \param[in] flags The inclusive OR of + * + * LS_DATE - %Print file modification date + * + * LS_SIZE - %Print file size. + * + * LS_R - Recursive list of subdirectories. + * + * \return true for success or false for failure. + */ + bool ls(print_t* pr, const char* path, uint8_t flags) { + ExFatFile dir; + return dir.open(this, path, O_RDONLY) && dir.ls(pr, flags); + } + /** Make a subdirectory in the volume root directory. + * + * \param[in] path A path with a valid 8.3 DOS name for the subdirectory. + * + * \param[in] pFlag Create missing parent directories if true. + * + * \return true for success or false for failure. + */ + bool mkdir(const char* path, bool pFlag = true) { + ExFatFile sub; + return sub.mkdir(vwd(), path, pFlag); + } + /** open a file + * + * \param[in] path location of file to be opened. + * \param[in] oflag open flags. + * \return a ExFile object. + */ + ExFile open(const char* path, oflag_t oflag = O_RDONLY) { + ExFile tmpFile; + tmpFile.open(this, path, oflag); + return tmpFile; + } + /** Remove a file from the volume root directory. + * + * \param[in] path A path with a valid 8.3 DOS name for the file. + * + * \return true for success or false for failure. + */ + bool remove(const char* path) { + ExFatFile tmp; + return tmp.open(this, path, O_WRONLY) && tmp.remove(); + } + /** Rename a file or subdirectory. + * + * \param[in] oldPath Path name to the file or subdirectory to be renamed. + * + * \param[in] newPath New path name of the file or subdirectory. + * + * The \a newPath object must not exist before the rename call. + * + * The file to be renamed must not be open. The directory entry may be + * moved and file system corruption could occur if the file is accessed by + * a file object that was opened before the rename() call. + * + * \return true for success or false for failure. + */ + bool rename(const char* oldPath, const char* newPath) { + ExFatFile file; + return file.open(vwd(), oldPath, O_RDONLY) && file.rename(vwd(), newPath); + } + /** Remove a subdirectory from the volume's working directory. + * + * \param[in] path A path with a valid 8.3 DOS name for the subdirectory. + * + * The subdirectory file will be removed only if it is empty. + * + * \return true for success or false for failure. + */ + bool rmdir(const char* path) { + ExFatFile sub; + return sub.open(this, path, O_RDONLY) && sub.rmdir(); + } + /** Truncate a file to a specified length. The current file position + * will be at the new EOF. + * + * \param[in] path A path with a valid 8.3 DOS name for the file. + * \param[in] length The desired length for the file. + * + * \return true for success or false for failure. + */ + bool truncate(const char* path, uint64_t length) { + ExFatFile file; + if (!file.open(this, path, O_WRONLY)) { + return false; + } + return file.truncate(length); + } +#if ENABLE_ARDUINO_SERIAL + /** List the directory contents of the root directory to Serial. + * + * \return true for success or false for failure. + */ + bool ls() { + return ls(&Serial); + } + /** List the directory contents of the volume root to Serial. + * + * \param[in] flags The inclusive OR of + * + * LS_DATE - %Print file modification date + * + * LS_SIZE - %Print file size. + * + * LS_R - Recursive list of subdirectories. + * + * \return true for success or false for failure. + */ + bool ls(uint8_t flags) { + return ls(&Serial, flags); + } + /** List the directory contents of a directory to Serial. + * + * \param[in] path directory to list. + * + * \param[in] flags The inclusive OR of + * + * LS_DATE - %Print file modification date + * + * LS_SIZE - %Print file size. + * + * LS_R - Recursive list of subdirectories. + * + * \return true for success or false for failure. + */ + bool ls(const char* path, uint8_t flags = 0) { + return ls(&Serial, path, flags); + } +#endif // ENABLE_ARDUINO_SERIAL +#if ENABLE_ARDUINO_STRING + /** + * Set volume working directory. + * \param[in] path Path for volume working directory. + * \return true for success or false for failure. + */ + bool chdir(const String& path) { + return chdir(path.c_str()); + } + /** Test for the existence of a file in a directory + * + * \param[in] path Path of the file to be tested for. + * + * \return true if the file exists else false. + */ + bool exists(const String &path) { + return exists(path.c_str()); + } + /** Make a subdirectory in the volume root directory. + * + * \param[in] path A path with a valid 8.3 DOS name for the subdirectory. + * + * \param[in] pFlag Create missing parent directories if true. + * + * \return true for success or false for failure. + */ + bool mkdir(const String &path, bool pFlag = true) { + return mkdir(path.c_str(), pFlag); + } + /** open a file + * + * \param[in] path location of file to be opened. + * \param[in] oflag open oflag flags. + * \return a ExFile object. + */ + ExFile open(const String &path, oflag_t oflag = O_RDONLY) { + return open(path.c_str(), oflag); + } + /** Remove a file from the volume root directory. + * + * \param[in] path A path with a valid name for the file. + * + * \return true for success or false for failure. + */ + bool remove(const String& path) { + return remove(path.c_str()); + } + /** Rename a file or subdirectory. + * + * \param[in] oldPath Path name to the file or subdirectory to be renamed. + * + * \param[in] newPath New path name of the file or subdirectory. + * + * The \a newPath object must not exist before the rename call. + * + * The file to be renamed must not be open. The directory entry may be + * moved and file system corruption could occur if the file is accessed by + * a file object that was opened before the rename() call. + * + * \return true for success or false for failure. + */ + bool rename(const String& oldPath, const String& newPath) { + return rename(oldPath.c_str(), newPath.c_str()); + } + /** Remove a subdirectory from the volume's working directory. + * + * \param[in] path A path with a valid name for the subdirectory. + * + * The subdirectory file will be removed only if it is empty. + * + * \return true for success or false for failure. + */ + bool rmdir(const String& path) { + return rmdir(path.c_str()); + } + /** Truncate a file to a specified length. The current file position + * will be at the new EOF. + * + * \param[in] path A path with a valid name for the file. + * \param[in] length The desired length for the file. + * + * \return true for success or false for failure. + */ + bool truncate(const String& path, uint64_t length) { + return truncate(path.c_str(), length); + } +#endif // ENABLE_ARDUINO_STRING + + private: + friend ExFatFile; + static ExFatVolume* cwv() {return m_cwv;} + ExFatFile* vwd() {return &m_vwd;} + static ExFatVolume* m_cwv; + ExFatFile m_vwd; +}; +#endif // ExFatVolume_h diff --git a/avr/cores/megacommand/SdFat/FatLib/ArduinoFiles.h b/avr/cores/megacommand/SdFat/FatLib/ArduinoFiles.h deleted file mode 100644 index 0775049ed..000000000 --- a/avr/cores/megacommand/SdFat/FatLib/ArduinoFiles.h +++ /dev/null @@ -1,249 +0,0 @@ -/** - * Copyright (c) 20011-2017 Bill Greiman - * This file is part of the SdFat library for SD memory cards. - * - * MIT License - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ -/** - * \file - * \brief PrintFile class - */ -#ifndef ArduinoFiles_h -#define ArduinoFiles_h -#include "FatLibConfig.h" -#if ENABLE_ARDUINO_FEATURES -#include "FatFile.h" -#include -//------------------------------------------------------------------------------ -/** Arduino SD.h style flag for open for read. */ -#define FILE_READ O_READ -/** Arduino SD.h style flag for open at EOF for read/write with create. */ -#define FILE_WRITE (O_RDWR | O_CREAT | O_AT_END) -//============================================================================== -/** - * \class PrintFile - * \brief FatFile with Print. - */ -class PrintFile : public FatFile, public Print { - public: - PrintFile() {} - /** Create a file object and open it in the current working directory. - * - * \param[in] path A path for a file to be opened. - * - * \param[in] oflag Values for \a oflag are constructed by a - * bitwise-inclusive OR of open flags. see - * FatFile::open(FatFile*, const char*, uint8_t). - */ - PrintFile(const char* path, uint8_t oflag) : FatFile(path, oflag) {} -#if DESTRUCTOR_CLOSES_FILE - ~PrintFile() {} -#endif // DESTRUCTOR_CLOSES_FILE - using FatFile::clearWriteError; - using FatFile::getWriteError; - using FatFile::read; - using FatFile::write; - /** \return number of bytes available from the current position to EOF - * or INT_MAX if more than INT_MAX bytes are available. - */ - int available() { - uint32_t n = FatFile::available(); - return n > INT_MAX ? INT_MAX : n; - } - /** Ensure that any bytes written to the file are saved to the SD card. */ - void flush() { - FatFile::sync(); - } - /** Return the next available byte without consuming it. - * - * \return The byte if no error and not at eof else -1; - */ - int peek() { - return FatFile::peek(); - } - /** Read the next byte from a file. - * - * \return For success return the next byte in the file as an int. - * If an error occurs or end of file is reached return -1. - */ -// int read() { -// return FatFile::read(); -// } - /** Write a byte to a file. Required by the Arduino Print class. - * \param[in] b the byte to be written. - * Use getWriteError to check for errors. - * \return 1 for success and 0 for failure. - */ - size_t write(uint8_t b) { - return FatFile::write(b); - } - /** Write data to an open file. Form required by Print. - * - * \note Data is moved to the cache but may not be written to the - * storage device until sync() is called. - * - * \param[in] buf Pointer to the location of the data to be written. - * - * \param[in] size Number of bytes to write. - * - * \return For success write() returns the number of bytes written, always - * \a nbyte. If an error occurs, write() returns -1. Possible errors - * include write() is called before a file has been opened, write is called - * for a read-only file, device is full, a corrupt file system or an - * I/O error. - */ - size_t write(const uint8_t *buf, size_t size) { - return FatFile::write(buf, size); - } -}; -//============================================================================== -/** - * \class File - * \brief Arduino SD.h style File API - */ -class File : public FatFile, public Stream { - public: - File() {} - /** 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 - * FatFile::open(FatFile*, const char*, uint8_t). - */ - File(const char* path, uint8_t oflag) { - open(path, oflag); - } - using FatFile::clearWriteError; - using FatFile::getWriteError; - using FatFile::read; - using FatFile::write; - /** The parenthesis operator. - * - * \return true if a file is open. - */ - operator bool() { - return isOpen(); - } - /** \return number of bytes available from the current position to EOF - * or INT_MAX if more than INT_MAX bytes are available. - */ - int available() { - uint32_t n = FatFile::available(); - return n > INT_MAX ? INT_MAX : n; - } - /** Ensure that any bytes written to the file are saved to the SD card. */ - void flush() { - FatFile::sync(); - } - /** This function reports if the current file is a directory or not. - * \return true if the file is a directory. - */ - bool isDirectory() { - return isDir(); - } - /** No longer implemented due to Long File Names. - * - * Use getName(char* name, size_t size). - * \return a pointer to replacement suggestion. - */ - const char* name() const { - return "use getName()"; - } - /** Return the next available byte without consuming it. - * - * \return The byte if no error and not at eof else -1; - */ - int peek() { - return FatFile::peek(); - } - /** \return the current file position. */ - uint32_t position() { - return curPosition(); - } - /** Opens the next file or folder in a directory. - * - * \param[in] mode open mode flags. - * \return a File object. - */ - File openNextFile(uint8_t mode = O_READ) { - File tmpFile; - tmpFile.openNext(this, mode); - return tmpFile; - } - /** Read the next byte from a file. - * - * \return For success return the next byte in the file as an int. - * If an error occurs or end of file is reached return -1. - */ - int read() { - return FatFile::read(); - } - /** Rewind a file if it is a directory */ - void rewindDirectory() { - if (isDir()) { - rewind(); - } - } - /** - * Seek to a new position in the file, which must be between - * 0 and the size of the file (inclusive). - * - * \param[in] pos the new file position. - * \return true for success else false. - */ - bool seek(uint32_t pos) { - return seekSet(pos); - } - /** \return the file's size. */ - uint32_t size() { - return fileSize(); - } - /** Write a byte to a file. Required by the Arduino Print class. - * \param[in] b the byte to be written. - * Use getWriteError to check for errors. - * \return 1 for success and 0 for failure. - */ - size_t write(uint8_t b) { - return FatFile::write(b); - } - /** Write data to an open file. Form required by Print. - * - * \note Data is moved to the cache but may not be written to the - * storage device until sync() is called. - * - * \param[in] buf Pointer to the location of the data to be written. - * - * \param[in] size Number of bytes to write. - * - * \return For success write() returns the number of bytes written, always - * \a nbyte. If an error occurs, write() returns -1. Possible errors - * include write() is called before a file has been opened, write is called - * for a read-only file, device is full, a corrupt file system or an - * I/O error. - */ - size_t write(const uint8_t *buf, size_t size) { - return FatFile::write(buf, size); - } -}; -#endif // ENABLE_ARDUINO_FEATURES -#endif // ArduinoFiles_h diff --git a/avr/cores/megacommand/SdFat/FatLib/BaseBlockDriver.h b/avr/cores/megacommand/SdFat/FatLib/BaseBlockDriver.h deleted file mode 100644 index 31221efcd..000000000 --- a/avr/cores/megacommand/SdFat/FatLib/BaseBlockDriver.h +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Copyright (c) 20011-2017 Bill Greiman - * This file is part of the SdFat library for SD memory cards. - * - * MIT License - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ -#ifndef BaseBlockDriver_h -#define BaseBlockDriver_h -#include "FatLibConfig.h" -/** - * \class BaseBlockDriver - * \brief Base block driver. - */ -class BaseBlockDriver { - public: - /** - * Read a 512 byte block from an SD card. - * - * \param[in] block Logical block to be read. - * \param[out] dst Pointer to the location that will receive the data. - * \return The value true is returned for success and - * the value false is returned for failure. - */ - virtual bool readBlock(uint32_t block, uint8_t* dst) = 0; - /** End multi-block transfer and go to idle state. - * \return The value true is returned for success and - * the value false is returned for failure. - */ - virtual bool syncBlocks() = 0; - /** - * Writes a 512 byte block to an SD card. - * - * \param[in] block Logical block to be written. - * \param[in] src Pointer to the location of the data to be written. - * \return The value true is returned for success and - * the value false is returned for failure. - */ - virtual bool writeBlock(uint32_t block, const uint8_t* src) = 0; -#if USE_MULTI_BLOCK_IO - /** - * Read multiple 512 byte blocks from an SD card. - * - * \param[in] block Logical block to be read. - * \param[in] nb Number of blocks to be read. - * \param[out] dst Pointer to the location that will receive the data. - * \return The value true is returned for success and - * the value false is returned for failure. - */ - virtual bool readBlocks(uint32_t block, uint8_t* dst, size_t nb) = 0; - /** - * Write multiple 512 byte blocks to an SD card. - * - * \param[in] block Logical block to be written. - * \param[in] nb Number of blocks to be written. - * \param[in] src Pointer to the location of the data to be written. - * \return The value true is returned for success and - * the value false is returned for failure. - */ - virtual bool writeBlocks(uint32_t block, const uint8_t* src, size_t nb) = 0; -#endif // USE_MULTI_BLOCK_IO -}; -#endif // BaseBlockDriver_h diff --git a/avr/cores/megacommand/SdFat/FatLib/FatDbg.cpp b/avr/cores/megacommand/SdFat/FatLib/FatDbg.cpp new file mode 100644 index 000000000..5344d075e --- /dev/null +++ b/avr/cores/megacommand/SdFat/FatLib/FatDbg.cpp @@ -0,0 +1,268 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "FatLib.h" +#ifndef DOXYGEN_SHOULD_SKIP_THIS +//------------------------------------------------------------------------------ +static uint16_t getLfnChar(DirLfn_t* ldir, uint8_t i) { + if (i < 5) { + return getLe16(ldir->unicode1 + 2*i); + } else if (i < 11) { + return getLe16(ldir->unicode2 + 2*i - 10); + } else if (i < 13) { + return getLe16(ldir->unicode3 + 2*i - 22); + } + return 0; +} +//------------------------------------------------------------------------------ +static void printHex(print_t* pr, uint8_t h) { + if (h < 16) { + pr->write('0'); + } + pr->print(h, HEX); +} +//------------------------------------------------------------------------------ +static void printHex(print_t* pr, uint8_t w, uint16_t h) { + char buf[5]; + char* ptr = buf + sizeof(buf); + *--ptr = 0; + for (uint8_t i = 0; i < w; i++) { + char c = h & 0XF; + *--ptr = c < 10 ? c + '0' : c + 'A' - 10; + h >>= 4; + } + pr->write(ptr); +} +//------------------------------------------------------------------------------ +static void printHex(print_t* pr, uint16_t val) { + bool space = true; + for (uint8_t i = 0; i < 4; i++) { + uint8_t h = (val >> (12 - 4*i)) & 15; + if (h || i == 3) { + space = false; + } + if (space) { + pr->write(' '); + } else { + pr->print(h, HEX); + } + } +} +//------------------------------------------------------------------------------ +static void printHex(print_t* pr, uint32_t val) { + bool space = true; + for (uint8_t i = 0; i < 8; i++) { + uint8_t h = (val >> (28 - 4*i)) & 15; + if (h || i == 7) { + space = false; + } + if (space) { + pr->write(' '); + } else { + pr->print(h, HEX); + } + } +} +//------------------------------------------------------------------------------ +template +static void printHexLn(print_t* pr, Uint val) { + printHex(pr, val); + pr->println(); +} +//------------------------------------------------------------------------------ +static bool printFatDir(print_t* pr, DirFat_t* dir) { + DirLfn_t* ldir = reinterpret_cast(dir); + if (!dir->name[0]) { + pr->println(F("Unused")); + return false; + } else if (dir->name[0] == FAT_NAME_DELETED) { + pr->println(F("Deleted")); + } else if (isFatFileOrSubdir(dir)) { + pr->print(F("SFN: ")); + for (uint8_t i = 0; i < 11; i++) { + printHex(pr, dir->name[i]); + pr->write(' '); + } + pr->write(' '); + pr->write(dir->name, 11); + pr->println(); + pr->print(F("attributes: 0X")); + printHexLn(pr, dir->attributes); + pr->print(F("caseFlags: 0X")); + printHexLn(pr, dir->caseFlags); + uint32_t fc = ((uint32_t)getLe16(dir->firstClusterHigh) << 16) + | getLe16(dir->firstClusterLow); + pr->print(F("firstCluster: ")); + pr->println(fc, HEX); + pr->print(F("fileSize: ")); + pr->println(getLe32(dir->fileSize)); + } else if (isFatLongName(dir)) { + pr->print(F("LFN: ")); + for (uint8_t i = 0; i < 13; i++) { + uint16_t c = getLfnChar(ldir, i); + if (15 < c && c < 128) { + pr->print(static_cast(c)); + } else { + pr->print("0X"); + pr->print(c, HEX); + } + pr->print(' '); + } + pr->println(); + pr->print(F("order: 0X")); + pr->println(ldir->order, HEX); + pr->print(F("attributes: 0X")); + pr->println(ldir->attributes, HEX); + pr->print(F("checksum: 0X")); + pr->println(ldir->checksum, HEX); + } else { + pr->println(F("Other")); + } + pr->println(); + return true; +} +//------------------------------------------------------------------------------ +void FatFile::dmpFile(print_t* pr, uint32_t pos, size_t n) { + char text[17]; + text[16] = 0; + if (n >= 0XFFF0) { + n = 0XFFF0; + } + if (!seekSet(pos)) { + return; + } + for (size_t i = 0; i <= n; i++) { + if ((i & 15) == 0) { + if (i) { + pr->write(' '); + pr->write(text); + if (i == n) { + break; + } + } + pr->write('\r'); + pr->write('\n'); + if (i >= n) { + break; + } + printHex(pr, 4, i); + pr->write(' '); + } + int16_t h = read(); + if (h < 0) { + break; + } + pr->write(' '); + printHex(pr, 2, h); + text[i&15] = ' ' <= h && h < 0X7F ? h : '.'; + } + pr->write('\r'); + pr->write('\n'); +} +//------------------------------------------------------------------------------ +bool FatPartition::dmpDirSector(print_t* pr, uint32_t sector) { + DirFat_t dir[16]; + if (!cacheSafeRead(sector, reinterpret_cast(dir))) { + pr->println(F("dmpDir failed")); + return false; + } + for (uint8_t i = 0; i < 16; i++) { + if (!printFatDir(pr, dir + i)) { + return false; + } + } + return true; +} +//------------------------------------------------------------------------------ +bool FatPartition::dmpRootDir(print_t* pr, uint32_t n) { + uint32_t sector; + if (fatType() == 16) { + sector = rootDirStart(); + } else if (fatType() == 32) { + sector = clusterStartSector(rootDirStart()); + } else { + pr->println(F("dmpRootDir failed")); + return false; + } + return dmpDirSector(pr, sector + n); +} +//------------------------------------------------------------------------------ +void FatPartition::dmpSector(print_t* pr, uint32_t sector, uint8_t bits) { + uint8_t data[FatPartition::m_bytesPerSector]; + if (!cacheSafeRead(sector, data)) { + pr->println(F("dmpSector failed")); + return; + } + for (uint16_t i = 0; i < m_bytesPerSector;) { + if (i%32 == 0) { + if (i) { + pr->println(); + } + printHex(pr, i); + } + pr->write(' '); + if (bits == 32) { + printHex(pr, *reinterpret_cast(data + i)); + i += 4; + } else if (bits == 16) { + printHex(pr, *reinterpret_cast(data + i)); + i += 2; + } else { + printHex(pr, data[i++]); + } + } + pr->println(); +} +//------------------------------------------------------------------------------ +void FatPartition::dmpFat(print_t* pr, uint32_t start, uint32_t count) { + uint16_t nf = fatType() == 16 ? 256 : fatType() == 32 ? 128 : 0; + if (nf == 0) { + pr->println(F("Invalid fatType")); + return; + } + pr->println(F("FAT:")); + uint32_t sector = m_fatStartSector + start; + uint32_t cluster = nf*start; + for (uint32_t i = 0; i < count; i++) { + uint8_t* pc = fatCachePrepare(sector + i, FsCache::CACHE_FOR_READ); + if (!pc) { + pr->println(F("cache read failed")); + return; + } + for (size_t k = 0; k < nf; k++) { + if (0 == cluster%8) { + if (k) { + pr->println(); + } + printHex(pr, cluster); + } + cluster++; + pr->write(' '); + uint32_t v = fatType() == 32 ? getLe32(pc + 4*k) : getLe16(pc + 2*k); + printHex(pr, v); + } + pr->println(); + } +} +#endif // DOXYGEN_SHOULD_SKIP_THIS diff --git a/avr/cores/megacommand/SdFat/FatLib/FatFile.cpp b/avr/cores/megacommand/SdFat/FatLib/FatFile.cpp index 1cb1ddf60..0793d9209 100644 --- a/avr/cores/megacommand/SdFat/FatLib/FatFile.cpp +++ b/avr/cores/megacommand/SdFat/FatLib/FatFile.cpp @@ -1,5 +1,5 @@ /** - * Copyright (c) 20011-2017 Bill Greiman + * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -22,25 +22,39 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ -#include "FatFile.h" -#include "FatFileSystem.h" -//------------------------------------------------------------------------------ -// Pointer to cwd directory. -FatFile* FatFile::m_cwd = 0; -// Callback function for date/time. -void (*FatFile::m_dateTime)(uint16_t* date, uint16_t* time) = 0; +#define DBG_FILE "FatFile.cpp" +#include "../common/DebugMacros.h" +#include "FatLib.h" //------------------------------------------------------------------------------ // Add a cluster to a file. bool FatFile::addCluster() { - m_flags |= F_FILE_DIR_DIRTY; +#if USE_FAT_FILE_FLAG_CONTIGUOUS + uint32_t cc = m_curCluster; + if (!m_vol->allocateCluster(m_curCluster, &m_curCluster)) { + DBG_FAIL_MACRO; + goto fail; + } + if (cc == 0) { + m_flags |= FILE_FLAG_CONTIGUOUS; + } else if (m_curCluster != (cc + 1)) { + m_flags &= ~FILE_FLAG_CONTIGUOUS; + } + m_flags |= FILE_FLAG_DIR_DIRTY; + return true; + + fail: + return false; +#else // USE_FAT_FILE_FLAG_CONTIGUOUS + m_flags |= FILE_FLAG_DIR_DIRTY; return m_vol->allocateCluster(m_curCluster, &m_curCluster); +#endif // USE_FAT_FILE_FLAG_CONTIGUOUS } //------------------------------------------------------------------------------ // Add a cluster to a directory file and zero the cluster. -// Return with first block of cluster in the cache. +// Return with first sector of cluster in the cache. bool FatFile::addDirCluster() { - uint32_t block; - cache_t* pc; + uint32_t sector; + uint8_t* pc; if (isRootFixed()) { DBG_FAIL_MACRO; @@ -55,52 +69,71 @@ bool FatFile::addDirCluster() { DBG_FAIL_MACRO; goto fail; } - block = m_vol->clusterFirstBlock(m_curCluster); - pc = m_vol->cacheFetchData(block, FatCache::CACHE_RESERVE_FOR_WRITE); - if (!pc) { - DBG_FAIL_MACRO; - goto fail; - } - memset(pc, 0, 512); - // zero rest of clusters - for (uint8_t i = 1; i < m_vol->blocksPerCluster(); i++) { - if (!m_vol->writeBlock(block + i, pc->data)) { + sector = m_vol->clusterStartSector(m_curCluster); + for (uint8_t i = 0; i < m_vol->sectorsPerCluster(); i++) { + pc = m_vol->dataCachePrepare(sector + i, FsCache::CACHE_RESERVE_FOR_WRITE); + if (!pc) { DBG_FAIL_MACRO; goto fail; } + memset(pc, 0, m_vol->bytesPerSector()); } // Set position to EOF to avoid inconsistent curCluster/curPosition. - m_curPosition += 512UL*m_vol->blocksPerCluster(); + m_curPosition += m_vol->bytesPerCluster(); + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +bool FatFile::attrib(uint8_t bits) { + if (!isFileOrSubDir() || (bits & FS_ATTRIB_USER_SETTABLE) != bits) { + DBG_FAIL_MACRO; + goto fail; + } + // Don't allow read-only to be set if the file is open for write. + if ((bits & FS_ATTRIB_READ_ONLY) && isWritable()) { + DBG_FAIL_MACRO; + goto fail; + } + m_attributes = (m_attributes & ~FS_ATTRIB_USER_SETTABLE) | bits; + // insure sync() will update dir entry + m_flags |= FILE_FLAG_DIR_DIRTY; + if (!sync()) { + DBG_FAIL_MACRO; + goto fail; + } return true; -fail: + fail: return false; } //------------------------------------------------------------------------------ // cache a file's directory entry // return pointer to cached entry or null for failure -dir_t* FatFile::cacheDirEntry(uint8_t action) { - cache_t* pc; - pc = m_vol->cacheFetchData(m_dirBlock, action); - if (!pc) { +DirFat_t* FatFile::cacheDirEntry(uint8_t action) { + uint8_t* pc = m_vol->dataCachePrepare(m_dirSector, action); + DirFat_t* dir = reinterpret_cast(pc); + if (!dir) { DBG_FAIL_MACRO; goto fail; } - return pc->dir + (m_dirIndex & 0XF); + return dir + (m_dirIndex & 0XF); -fail: - return 0; + fail: + return nullptr; } //------------------------------------------------------------------------------ bool FatFile::close() { bool rtn = sync(); - m_attr = FILE_ATTR_CLOSED; + m_attributes = FILE_ATTR_CLOSED; + m_flags = 0; return rtn; } //------------------------------------------------------------------------------ -bool FatFile::contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock) { - // error if no blocks - if (m_firstCluster == 0) { +bool FatFile::contiguousRange(uint32_t* bgnSector, uint32_t* endSector) { + // error if no clusters + if (!isFile() || m_firstCluster == 0) { DBG_FAIL_MACRO; goto fail; } @@ -118,92 +151,71 @@ bool FatFile::contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock) { DBG_FAIL_MACRO; goto fail; } - *bgnBlock = m_vol->clusterFirstBlock(m_firstCluster); - *endBlock = m_vol->clusterFirstBlock(c) - + m_vol->blocksPerCluster() - 1; +#if USE_FAT_FILE_FLAG_CONTIGUOUS + m_flags |= FILE_FLAG_CONTIGUOUS; +#endif // USE_FAT_FILE_FLAG_CONTIGUOUS + if (bgnSector) { + *bgnSector = m_vol->clusterStartSector(m_firstCluster); + } + if (endSector) { + *endSector = m_vol->clusterStartSector(c) + + m_vol->sectorsPerCluster() - 1; + } return true; } } -fail: + fail: return false; } //------------------------------------------------------------------------------ -bool FatFile::createContiguous(FatFile* dirFile, - const char* path, uint32_t size) { - uint32_t count; - - // don't allow zero length file - if (size == 0) { +bool FatFile::createContiguous(const char* path, uint32_t size) { + if (!open(FatVolume::cwv(), path, O_CREAT | O_EXCL | O_RDWR)) { DBG_FAIL_MACRO; goto fail; } + if (preAllocate(size)) { + return true; + } + close(); + fail: + return false; +} +//------------------------------------------------------------------------------ +bool FatFile::createContiguous(FatFile* dirFile, + const char* path, uint32_t size) { if (!open(dirFile, path, O_CREAT | O_EXCL | O_RDWR)) { DBG_FAIL_MACRO; goto fail; } - // calculate number of clusters needed - count = ((size - 1) >> (m_vol->clusterSizeShift() + 9)) + 1; - - // allocate clusters - if (!m_vol->allocContiguous(count, &m_firstCluster)) { - remove(); - DBG_FAIL_MACRO; - goto fail; + if (preAllocate(size)) { + return true; } - m_fileSize = size; - - // insure sync() will update dir entry - m_flags |= F_FILE_DIR_DIRTY; - return sync(); - -fail: + close(); + fail: return false; } //------------------------------------------------------------------------------ -bool FatFile::dirEntry(dir_t* dst) { - dir_t* dir; +bool FatFile::dirEntry(DirFat_t* dst) { + DirFat_t* dir; // Make sure fields on device are correct. if (!sync()) { DBG_FAIL_MACRO; goto fail; } // read entry - dir = cacheDirEntry(FatCache::CACHE_FOR_READ); + dir = cacheDirEntry(FsCache::CACHE_FOR_READ); if (!dir) { DBG_FAIL_MACRO; goto fail; } // copy to caller's struct - memcpy(dst, dir, sizeof(dir_t)); + memcpy(dst, dir, sizeof(DirFat_t)); return true; -fail: + fail: return false; } -//------------------------------------------------------------------------------ -uint8_t FatFile::dirName(const dir_t* dir, char* name) { - uint8_t j = 0; - uint8_t lcBit = DIR_NT_LC_BASE; - for (uint8_t i = 0; i < 11; i++) { - if (dir->name[i] == ' ') { - continue; - } - if (i == 8) { - // Position bit for extension. - lcBit = DIR_NT_LC_EXT; - name[j++] = '.'; - } - char c = dir->name[i]; - if ('A' <= c && c <= 'Z' && (lcBit & dir->reservedNT)) { - c += 'a' - 'A'; - } - name[j++] = c; - } - name[j] = 0; - return j; -} - //------------------------------------------------------------------------------ uint32_t FatFile::dirSize() { int8_t fg; @@ -211,7 +223,7 @@ uint32_t FatFile::dirSize() { return 0; } if (isRootFixed()) { - return 32*m_vol->rootDirEntryCount(); + return FS_DIR_SIZE*m_vol->rootDirEntryCount(); } uint16_t n = 0; uint32_t c = isRoot32() ? m_vol->rootDirStart() : m_firstCluster; @@ -220,15 +232,15 @@ uint32_t FatFile::dirSize() { if (fg < 0 || n > 4095) { return 0; } - n += m_vol->blocksPerCluster(); + n += m_vol->sectorsPerCluster(); } while (fg); return 512UL*n; } //------------------------------------------------------------------------------ -int16_t FatFile::fgets(char* str, int16_t num, char* delim) { +int FatFile::fgets(char* str, int num, char* delim) { char ch; - int16_t n = 0; - int16_t r = -1; + int n = 0; + int r = -1; while ((n + 1) < num && (r = read(&ch, 1)) == 1) { // delete CR if (ch == '\r') { @@ -253,13 +265,67 @@ int16_t FatFile::fgets(char* str, int16_t num, char* delim) { return n; } //------------------------------------------------------------------------------ -void FatFile::getpos(FatPos_t* pos) { +void FatFile::fgetpos(fspos_t* pos) const { pos->position = m_curPosition; pos->cluster = m_curCluster; } //------------------------------------------------------------------------------ +uint32_t FatFile::firstSector() const { + return m_firstCluster ? m_vol->clusterStartSector(m_firstCluster) : 0; +} +//------------------------------------------------------------------------------ +void FatFile::fsetpos(const fspos_t* pos) { + m_curPosition = pos->position; + m_curCluster = pos->cluster; +} +//------------------------------------------------------------------------------ +bool FatFile::getAccessDate(uint16_t* pdate) { + DirFat_t dir; + if (!dirEntry(&dir)) { + DBG_FAIL_MACRO; + goto fail; + } + *pdate = getLe16(dir.accessDate); + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +bool FatFile::getCreateDateTime(uint16_t* pdate, uint16_t* ptime) { + DirFat_t dir; + if (!dirEntry(&dir)) { + DBG_FAIL_MACRO; + goto fail; + } + *pdate = getLe16(dir.createDate); + *ptime = getLe16(dir.createTime); + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +bool FatFile::getModifyDateTime(uint16_t* pdate, uint16_t* ptime) { + DirFat_t dir; + if (!dirEntry(&dir)) { + DBG_FAIL_MACRO; + goto fail; + } + *pdate = getLe16(dir.modifyDate); + *ptime = getLe16(dir.modifyTime); + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +bool FatFile::isBusy() { + return m_vol->isBusy(); +} +//------------------------------------------------------------------------------ bool FatFile::mkdir(FatFile* parent, const char* path, bool pFlag) { - fname_t fname; + FatName_t fname; FatFile tmpDir; if (isOpen() || !parent->isDir()) { @@ -284,7 +350,7 @@ bool FatFile::mkdir(FatFile* parent, const char* path, bool pFlag) { if (!*path) { break; } - if (!open(parent, &fname, O_READ)) { + if (!open(parent, &fname, O_RDONLY)) { if (!pFlag || !mkdir(parent, &fname)) { DBG_FAIL_MACRO; goto fail; @@ -296,15 +362,15 @@ bool FatFile::mkdir(FatFile* parent, const char* path, bool pFlag) { } return mkdir(parent, &fname); -fail: + fail: return false; } //------------------------------------------------------------------------------ -bool FatFile::mkdir(FatFile* parent, fname_t* fname) { - uint32_t block; - dir_t dot; - dir_t* dir; - cache_t* pc; +bool FatFile::mkdir(FatFile* parent, FatName_t* fname) { + uint32_t sector; + DirFat_t dot; + DirFat_t* dir; + uint8_t* pc; if (!parent->isDir()) { DBG_FAIL_MACRO; @@ -316,8 +382,8 @@ bool FatFile::mkdir(FatFile* parent, fname_t* fname) { goto fail; } // convert file to directory - m_flags = O_READ; - m_attr = FILE_ATTR_SUBDIR; + m_flags = FILE_FLAG_READ; + m_attributes = FILE_ATTR_SUBDIR; // allocate and zero first cluster if (!addDirCluster()) { @@ -333,13 +399,13 @@ bool FatFile::mkdir(FatFile* parent, fname_t* fname) { goto fail; } // cache entry - should already be in cache due to sync() call - dir = cacheDirEntry(FatCache::CACHE_FOR_WRITE); + dir = cacheDirEntry(FsCache::CACHE_FOR_WRITE); if (!dir) { DBG_FAIL_MACRO; goto fail; } - // change directory entry attribute - dir->attributes = DIR_ATT_DIRECTORY; + // change directory entry attribute + dir->attributes = FS_ATTRIB_DIRECTORY; // make entry for '.' memcpy(&dot, dir, sizeof(dot)); @@ -348,35 +414,40 @@ bool FatFile::mkdir(FatFile* parent, fname_t* fname) { dot.name[i] = ' '; } - // cache block for '.' and '..' - block = m_vol->clusterFirstBlock(m_firstCluster); - pc = m_vol->cacheFetchData(block, FatCache::CACHE_FOR_WRITE); - if (!pc) { + // cache sector for '.' and '..' + sector = m_vol->clusterStartSector(m_firstCluster); + pc = m_vol->dataCachePrepare(sector, FsCache::CACHE_FOR_WRITE); + dir = reinterpret_cast(pc); + if (!dir) { DBG_FAIL_MACRO; goto fail; } - // copy '.' to block - memcpy(&pc->dir[0], &dot, sizeof(dot)); + // copy '.' to sector + memcpy(&dir[0], &dot, sizeof(dot)); // make entry for '..' dot.name[1] = '.'; - dot.firstClusterLow = parent->m_firstCluster & 0XFFFF; - dot.firstClusterHigh = parent->m_firstCluster >> 16; - // copy '..' to block - memcpy(&pc->dir[1], &dot, sizeof(dot)); - // write first block + setLe16(dot.firstClusterLow, parent->m_firstCluster & 0XFFFF); + setLe16(dot.firstClusterHigh, parent->m_firstCluster >> 16); + // copy '..' to sector + memcpy(&dir[1], &dot, sizeof(dot)); + // write first sector return m_vol->cacheSync(); -fail: + fail: return false; } //------------------------------------------------------------------------------ -bool FatFile::open(FatFileSystem* fs, const char* path, uint8_t oflag) { - return open(fs->vwd(), path, oflag); +bool FatFile::open(const char* path, oflag_t oflag) { + return open(FatVolume::cwv(), path, oflag); } //------------------------------------------------------------------------------ -bool FatFile::open(FatFile* dirFile, const char* path, uint8_t oflag) { +bool FatFile::open(FatVolume* vol, const char* path, oflag_t oflag) { + return vol && open(vol->vwd(), path, oflag); +} +//------------------------------------------------------------------------------ +bool FatFile::open(FatFile* dirFile, const char* path, oflag_t oflag) { FatFile tmpDir; - fname_t fname; + FatName_t fname; // error if already open if (isOpen() || !dirFile->isDir()) { @@ -404,8 +475,8 @@ bool FatFile::open(FatFile* dirFile, const char* path, uint8_t oflag) { if (*path == 0) { break; } - if (!open(dirFile, &fname, O_READ)) { - DBG_FAIL_MACRO; + if (!open(dirFile, &fname, O_RDONLY)) { + DBG_WARN_MACRO; goto fail; } tmpDir = *this; @@ -414,112 +485,114 @@ bool FatFile::open(FatFile* dirFile, const char* path, uint8_t oflag) { } return open(dirFile, &fname, oflag); -fail: + fail: return false; } //------------------------------------------------------------------------------ -bool FatFile::open(FatFile* dirFile, uint16_t index, uint8_t oflag) { - uint8_t chksum = 0; - uint8_t lfnOrd = 0; - dir_t* dir; - ldir_t*ldir; - - // Error if already open. - if (isOpen() || !dirFile->isDir()) { - DBG_FAIL_MACRO; - goto fail; - } - // Don't open existing file if O_EXCL - user call error. - if (oflag & O_EXCL) { - DBG_FAIL_MACRO; - goto fail; - } +bool FatFile::open(uint16_t index, oflag_t oflag) { + FatVolume* vol = FatVolume::cwv(); + return vol ? open(vol->vwd(), index, oflag) : false; +} +//------------------------------------------------------------------------------ +bool FatFile::open(FatFile* dirFile, uint16_t index, oflag_t oflag) { if (index) { - // Check for LFN. - if (!dirFile->seekSet(32UL*(index -1))) { - DBG_FAIL_MACRO; - goto fail; - } - ldir = reinterpret_cast(dirFile->readDirCache()); - if (!ldir) { - DBG_FAIL_MACRO; - goto fail; - } - if (ldir->attr == DIR_ATT_LONG_NAME) { - if (1 == (ldir->ord & 0X1F)) { - chksum = ldir->chksum; - // Use largest possible number. - lfnOrd = index > 20 ? 20 : index; + // Find start of LFN. + DirLfn_t* ldir; + uint8_t n = index < 20 ? index : 20; + for (uint8_t i = 1; i <= n; i++) { + ldir = reinterpret_cast(dirFile->cacheDir(index - i)); + if (!ldir) { + DBG_FAIL_MACRO; + goto fail; + } + if (ldir->attributes != FAT_ATTRIB_LONG_NAME) { + break; + } + if (ldir->order & FAT_ORDER_LAST_LONG_ENTRY) { + if (!dirFile->seekSet(32UL*(index - i))) { + DBG_FAIL_MACRO; + goto fail; + } + break; } } } else { dirFile->rewind(); } - // read entry into cache - dir = dirFile->readDirCache(); - if (!dir) { + if (!openNext(dirFile, oflag)) { DBG_FAIL_MACRO; goto fail; } - // error if empty slot or '.' or '..' - if (dir->name[0] == DIR_NAME_DELETED || - dir->name[0] == DIR_NAME_FREE || - dir->name[0] == '.') { - DBG_FAIL_MACRO; - goto fail; - } - if (lfnOrd && chksum != lfnChecksum(dir->name)) { - DBG_FAIL_MACRO; - goto fail; - } - // open cached entry - if (!openCachedEntry(dirFile, index, oflag, lfnOrd)) { + if (dirIndex() != index) { + close(); DBG_FAIL_MACRO; goto fail; } return true; -fail: + fail: return false; } //------------------------------------------------------------------------------ // open a cached directory entry. - bool FatFile::openCachedEntry(FatFile* dirFile, uint16_t dirIndex, - uint8_t oflag, uint8_t lfnOrd) { + oflag_t oflag, uint8_t lfnOrd) { uint32_t firstCluster; memset(this, 0, sizeof(FatFile)); // location of entry in cache m_vol = dirFile->m_vol; m_dirIndex = dirIndex; m_dirCluster = dirFile->m_firstCluster; - dir_t* dir = &m_vol->cacheAddress()->dir[0XF & dirIndex]; + DirFat_t* dir = reinterpret_cast(m_vol->cacheAddress()); + dir += 0XF & dirIndex; // Must be file or subdirectory. - if (!DIR_IS_FILE_OR_SUBDIR(dir)) { + if (!isFatFileOrSubdir(dir)) { DBG_FAIL_MACRO; goto fail; } - m_attr = dir->attributes & FILE_ATTR_COPY; - if (DIR_IS_FILE(dir)) { - m_attr |= FILE_ATTR_FILE; + m_attributes = dir->attributes & FS_ATTRIB_COPY; + if (isFatFile(dir)) { + m_attributes |= FILE_ATTR_FILE; } m_lfnOrd = lfnOrd; - // Write, truncate, or at end is an error for a directory or read-only file. - if (oflag & (O_WRITE | O_TRUNC | O_AT_END)) { + + switch (oflag & O_ACCMODE) { + case O_RDONLY: + if (oflag & O_TRUNC) { + DBG_FAIL_MACRO; + goto fail; + } + m_flags = FILE_FLAG_READ; + break; + + case O_RDWR: + m_flags = FILE_FLAG_READ | FILE_FLAG_WRITE; + break; + + case O_WRONLY: + m_flags = FILE_FLAG_WRITE; + break; + + default: + DBG_FAIL_MACRO; + goto fail; + } + + if (m_flags & FILE_FLAG_WRITE) { if (isSubDir() || isReadOnly()) { DBG_FAIL_MACRO; goto fail; } + m_attributes |= FS_ATTRIB_ARCHIVE; } - // save open flags for read/write - m_flags = oflag & F_OFLAG; + m_flags |= (oflag & O_APPEND ? FILE_FLAG_APPEND : 0); - m_dirBlock = m_vol->cacheBlockNumber(); + m_dirSector = m_vol->cacheSectorNumber(); // copy first cluster number for directory fields - firstCluster = ((uint32_t)dir->firstClusterHigh << 16) - | dir->firstClusterLow; + firstCluster = ((uint32_t)getLe16(dir->firstClusterHigh) << 16) + | getLe16(dir->firstClusterLow); if (oflag & O_TRUNC) { if (firstCluster && !m_vol->freeChain(firstCluster)) { @@ -527,10 +600,10 @@ bool FatFile::openCachedEntry(FatFile* dirFile, uint16_t dirIndex, goto fail; } // need to update directory entry - m_flags |= F_FILE_DIR_DIRTY; + m_flags |= FILE_FLAG_DIR_DIRTY; } else { m_firstCluster = firstCluster; - m_fileSize = dir->fileSize; + m_fileSize = getLe32(dir->fileSize); } if ((oflag & O_AT_END) && !seekSet(m_fileSize)) { DBG_FAIL_MACRO; @@ -538,14 +611,40 @@ bool FatFile::openCachedEntry(FatFile* dirFile, uint16_t dirIndex, } return true; -fail: - m_attr = FILE_ATTR_CLOSED; + fail: + m_attributes = FILE_ATTR_CLOSED; + m_flags = 0; + return false; +} +//------------------------------------------------------------------------------ +bool FatFile::openCluster(FatFile* file) { + if (file->m_dirCluster == 0) { + return openRoot(file->m_vol); + } + memset(this, 0, sizeof(FatFile)); + m_attributes = FILE_ATTR_SUBDIR; + m_flags = FILE_FLAG_READ; + m_vol = file->m_vol; + m_firstCluster = file->m_dirCluster; + return true; +} +//------------------------------------------------------------------------------ +bool FatFile::openCwd() { + if (isOpen() || !FatVolume::cwv()) { + DBG_FAIL_MACRO; + goto fail; + } + *this = *FatVolume::cwv()->vwd(); + rewind(); + return true; + + fail: return false; } //------------------------------------------------------------------------------ -bool FatFile::openNext(FatFile* dirFile, uint8_t oflag) { - uint8_t chksum = 0; - ldir_t* ldir; +bool FatFile::openNext(FatFile* dirFile, oflag_t oflag) { + uint8_t checksum = 0; + DirLfn_t* ldir; uint8_t lfnOrd = 0; uint16_t index; @@ -556,8 +655,8 @@ bool FatFile::openNext(FatFile* dirFile, uint8_t oflag) { } while (1) { // read entry into cache - index = dirFile->curPosition()/32; - dir_t* dir = dirFile->readDirCache(); + index = dirFile->curPosition()/FS_DIR_SIZE; + DirFat_t* dir = dirFile->readDirCache(); if (!dir) { if (dirFile->getError()) { DBG_FAIL_MACRO; @@ -565,14 +664,14 @@ bool FatFile::openNext(FatFile* dirFile, uint8_t oflag) { goto fail; } // done if last entry - if (dir->name[0] == DIR_NAME_FREE) { + if (dir->name[0] == FAT_NAME_FREE) { goto fail; } // skip empty slot or '.' or '..' - if (dir->name[0] == '.' || dir->name[0] == DIR_NAME_DELETED) { + if (dir->name[0] == '.' || dir->name[0] == FAT_NAME_DELETED) { lfnOrd = 0; - } else if (DIR_IS_FILE_OR_SUBDIR(dir)) { - if (lfnOrd && chksum != lfnChecksum(dir->name)) { + } else if (isFatFileOrSubdir(dir)) { + if (lfnOrd && checksum != lfnChecksum(dir->name)) { DBG_FAIL_MACRO; goto fail; } @@ -581,79 +680,20 @@ bool FatFile::openNext(FatFile* dirFile, uint8_t oflag) { goto fail; } return true; - } else if (DIR_IS_LONG_NAME(dir)) { - ldir = reinterpret_cast(dir); - if (ldir->ord & LDIR_ORD_LAST_LONG_ENTRY) { - lfnOrd = ldir->ord & 0X1F; - chksum = ldir->chksum; + } else if (isFatLongName(dir)) { + ldir = reinterpret_cast(dir); + if (ldir->order & FAT_ORDER_LAST_LONG_ENTRY) { + lfnOrd = ldir->order & 0X1F; + checksum = ldir->checksum; } } else { lfnOrd = 0; } } -fail: + fail: return false; } -#ifndef DOXYGEN_SHOULD_SKIP_THIS -//------------------------------------------------------------------------------ -/** Open a file's parent directory. - * - * \param[in] file Parent of this directory will be opened. Must not be root. - * - * \return The value true is returned for success and - * the value false is returned for failure. - */ -bool FatFile::openParent(FatFile* dirFile) { - FatFile dotdot; - uint32_t lbn; - dir_t* dir; - uint32_t ddc; - cache_t* cb; - - if (isOpen() || !dirFile->isOpen()) { - goto fail; - } - if (dirFile->m_dirCluster == 0) { - return openRoot(dirFile->m_vol); - } - lbn = dirFile->m_vol->clusterFirstBlock(dirFile->m_dirCluster); - cb = dirFile->m_vol->cacheFetchData(lbn, FatCache::CACHE_FOR_READ); - if (!cb) { - DBG_FAIL_MACRO; - goto fail; - } - // Point to dir entery for .. - dir = cb->dir + 1; - ddc = dir->firstClusterLow | ((uint32_t)dir->firstClusterHigh << 16); - if (ddc == 0) { - if (!dotdot.openRoot(dirFile->m_vol)) { - DBG_FAIL_MACRO; - goto fail; - } - } else { - memset(&dotdot, 0, sizeof(FatFile)); - dotdot.m_attr = FILE_ATTR_SUBDIR; - dotdot.m_flags = O_READ; - dotdot.m_vol = dirFile->m_vol; - dotdot.m_firstCluster = ddc; - } - uint32_t di; - do { - di = dotdot.curPosition()/32; - dir = dotdot.readDirCache(); - if (!dir) { - DBG_FAIL_MACRO; - goto fail; - } - ddc = dir->firstClusterLow | ((uint32_t)dir->firstClusterHigh << 16); - } while (ddc != dirFile->m_dirCluster); - return open(&dotdot, di, O_READ); - -fail: - return false; -} -#endif // DOXYGEN_SHOULD_SKIP_THIS //------------------------------------------------------------------------------ bool FatFile::openRoot(FatVolume* vol) { // error if file is already open @@ -669,11 +709,11 @@ bool FatFile::openRoot(FatVolume* vol) { case 12: #endif // FAT12_SUPPORT case 16: - m_attr = FILE_ATTR_ROOT_FIXED; + m_attributes = FILE_ATTR_ROOT_FIXED; break; case 32: - m_attr = FILE_ATTR_ROOT32; + m_attributes = FILE_ATTR_ROOT32; break; default: @@ -681,34 +721,59 @@ bool FatFile::openRoot(FatVolume* vol) { goto fail; } // read only - m_flags = O_READ; + m_flags = FILE_FLAG_READ; return true; -fail: + fail: return false; } //------------------------------------------------------------------------------ int FatFile::peek() { - FatPos_t pos; - getpos(&pos); + uint32_t curPosition = m_curPosition; + uint32_t curCluster = m_curCluster; int c = read(); - if (c >= 0) { - setpos(&pos); - } + m_curPosition = curPosition; + m_curCluster = curCluster; return c; } //------------------------------------------------------------------------------ +bool FatFile::preAllocate(uint32_t length) { + uint32_t need; + if (!length || !isWritable() || m_firstCluster) { + DBG_FAIL_MACRO; + goto fail; + } + need = 1 + ((length - 1) >> m_vol->bytesPerClusterShift()); + // allocate clusters + if (!m_vol->allocContiguous(need, &m_firstCluster)) { + DBG_FAIL_MACRO; + goto fail; + } + m_fileSize = length; + +#if USE_FAT_FILE_FLAG_CONTIGUOUS + // Mark contiguous and insure sync() will update dir entry + m_flags |= FILE_FLAG_PREALLOCATE | FILE_FLAG_CONTIGUOUS | FILE_FLAG_DIR_DIRTY; +#else // USE_FAT_FILE_FLAG_CONTIGUOUS + // insure sync() will update dir entry + m_flags |= FILE_FLAG_DIR_DIRTY; +#endif // USE_FAT_FILE_FLAG_CONTIGUOUS + return sync(); + + fail: + return false; +} +//------------------------------------------------------------------------------ int FatFile::read(void* buf, size_t nbyte) { int8_t fg; - uint8_t blockOfCluster = 0; + uint8_t sectorOfCluster = 0; uint8_t* dst = reinterpret_cast(buf); uint16_t offset; size_t toRead; - uint32_t block; // raw device block number - cache_t* pc; - + uint32_t sector; // raw device sector number + uint8_t* pc; // error if not open for read - if (!isOpen() || !(m_flags & O_READ)) { + if (!isReadable()) { DBG_FAIL_MACRO; goto fail; } @@ -719,7 +784,8 @@ int FatFile::read(void* buf, size_t nbyte) { nbyte = tmp32; } } else if (isRootFixed()) { - uint16_t tmp16 = 32*m_vol->m_rootDirEntryCount - (uint16_t)m_curPosition; + uint16_t tmp16 = + FS_DIR_SIZE*m_vol->m_rootDirEntryCount - (uint16_t)m_curPosition; if (nbyte > tmp16) { nbyte = tmp16; } @@ -727,16 +793,21 @@ int FatFile::read(void* buf, size_t nbyte) { toRead = nbyte; while (toRead) { size_t n; - offset = m_curPosition & 0X1FF; // offset in block + offset = m_curPosition & m_vol->sectorMask(); // offset in sector if (isRootFixed()) { - block = m_vol->rootDirStart() + (m_curPosition >> 9); + sector = m_vol->rootDirStart() + + (m_curPosition >> m_vol->bytesPerSectorShift()); } else { - blockOfCluster = m_vol->blockOfCluster(m_curPosition); - if (offset == 0 && blockOfCluster == 0) { + sectorOfCluster = m_vol->sectorOfCluster(m_curPosition); + if (offset == 0 && sectorOfCluster == 0) { // start of new cluster if (m_curPosition == 0) { // use first cluster in file m_curCluster = isRoot32() ? m_vol->rootDirStart() : m_firstCluster; +#if USE_FAT_FILE_FLAG_CONTIGUOUS + } else if (isFile() && isContiguous()) { + m_curCluster++; +#endif // USE_FAT_FILE_FLAG_CONTIGUOUS } else { // get next cluster from FAT fg = m_vol->fatGet(m_curCluster, &m_curCluster); @@ -753,49 +824,42 @@ int FatFile::read(void* buf, size_t nbyte) { } } } - block = m_vol->clusterFirstBlock(m_curCluster) + blockOfCluster; + sector = m_vol->clusterStartSector(m_curCluster) + sectorOfCluster; } - if (offset != 0 || toRead < 512 || block == m_vol->cacheBlockNumber()) { - // amount to be read from current block - n = 512 - offset; + if (offset != 0 || toRead < m_vol->bytesPerSector() + || sector == m_vol->cacheSectorNumber()) { + // amount to be read from current sector + n = m_vol->bytesPerSector() - offset; if (n > toRead) { n = toRead; } - // read block to cache and copy data to caller - pc = m_vol->cacheFetchData(block, FatCache::CACHE_FOR_READ); + // read sector to cache and copy data to caller + pc = m_vol->dataCachePrepare(sector, FsCache::CACHE_FOR_READ); if (!pc) { DBG_FAIL_MACRO; goto fail; } - uint8_t* src = pc->data + offset; + uint8_t* src = pc + offset; memcpy(dst, src, n); -#if USE_MULTI_BLOCK_IO - } else if (toRead >= 1024) { - size_t nb = toRead >> 9; +#if USE_MULTI_SECTOR_IO + } else if (toRead >= 2*m_vol->bytesPerSector()) { + uint32_t ns = toRead >> m_vol->bytesPerSectorShift(); if (!isRootFixed()) { - uint8_t mb = m_vol->blocksPerCluster() - blockOfCluster; - if (mb < nb) { - nb = mb; + uint32_t mb = m_vol->sectorsPerCluster() - sectorOfCluster; + if (mb < ns) { + ns = mb; } } - n = 512*nb; - if (block <= m_vol->cacheBlockNumber() - && block < (m_vol->cacheBlockNumber() + nb)) { - // flush cache if a block is in the cache - if (!m_vol->cacheSyncData()) { - DBG_FAIL_MACRO; - goto fail; - } - } - if (!m_vol->readBlocks(block, dst, nb)) { + n = ns << m_vol->bytesPerSectorShift(); + if (!m_vol->cacheSafeRead(sector, dst, ns)) { DBG_FAIL_MACRO; goto fail; } -#endif // USE_MULTI_BLOCK_IO +#endif // USE_MULTI_SECTOR_IO } else { - // read single block - n = 512; - if (!m_vol->readBlock(block, dst)) { + // read single sector + n = m_vol->bytesPerSector(); + if (!m_vol->cacheSafeRead(sector, dst)) { DBG_FAIL_MACRO; goto fail; } @@ -806,12 +870,12 @@ int FatFile::read(void* buf, size_t nbyte) { } return nbyte - toRead; -fail: + fail: m_error |= READ_ERROR; return -1; } //------------------------------------------------------------------------------ -int8_t FatFile::readDir(dir_t* dir) { +int8_t FatFile::readDir(DirFat_t* dir) { int16_t n; // if not a directory file or miss-positioned return an error if (!isDir() || (0X1F & m_curPosition)) { @@ -819,29 +883,29 @@ int8_t FatFile::readDir(dir_t* dir) { } while (1) { - n = read(dir, sizeof(dir_t)); - if (n != sizeof(dir_t)) { + n = read(dir, sizeof(DirFat_t)); + if (n != sizeof(DirFat_t)) { return n == 0 ? 0 : -1; } - // last entry if DIR_NAME_FREE - if (dir->name[0] == DIR_NAME_FREE) { + // last entry if FAT_NAME_FREE + if (dir->name[0] == FAT_NAME_FREE) { return 0; } // skip empty entries and entry for . and .. - if (dir->name[0] == DIR_NAME_DELETED || dir->name[0] == '.') { + if (dir->name[0] == FAT_NAME_DELETED || dir->name[0] == '.') { continue; } // return if normal file or subdirectory - if (DIR_IS_FILE_OR_SUBDIR(dir)) { + if (isFatFileOrSubdir(dir)) { return n; } } } //------------------------------------------------------------------------------ -// Read next directory entry into the cache -// Assumes file is correctly positioned -dir_t* FatFile::readDirCache(bool skipReadOk) { -// uint8_t b; +// Read next directory entry into the cache. +// Assumes file is correctly positioned. +DirFat_t* FatFile::readDirCache(bool skipReadOk) { + DBG_HALT_IF(m_curPosition & 0X1F); uint8_t i = (m_curPosition >> 5) & 0XF; if (i == 0 || !skipReadOk) { @@ -852,36 +916,40 @@ dir_t* FatFile::readDirCache(bool skipReadOk) { } goto fail; } - m_curPosition += 31; + m_curPosition += FS_DIR_SIZE - 1; } else { - m_curPosition += 32; + m_curPosition += FS_DIR_SIZE; } // return pointer to entry - return m_vol->cacheAddress()->dir + i; + return reinterpret_cast(m_vol->cacheAddress()) + i; -fail: - return 0; + fail: + return nullptr; } //------------------------------------------------------------------------------ -bool FatFile::remove(FatFile* dirFile, const char* path) { +bool FatFile::remove(const char* path) { FatFile file; - if (!file.open(dirFile, path, O_WRITE)) { + if (!file.open(this, path, O_WRONLY)) { DBG_FAIL_MACRO; goto fail; } return file.remove(); -fail: + fail: return false; } //------------------------------------------------------------------------------ +bool FatFile::rename(const char* newPath) { + return rename(m_vol->vwd(), newPath); +} +//------------------------------------------------------------------------------ bool FatFile::rename(FatFile* dirFile, const char* newPath) { - dir_t entry; + DirFat_t entry; uint32_t dirCluster = 0; FatFile file; FatFile oldFile; - cache_t* pc; - dir_t* dir; + uint8_t* pc; + DirFat_t* dir; // Must be an open file or subdirectory. if (!(isFile() || isSubDir())) { @@ -901,7 +969,7 @@ bool FatFile::rename(FatFile* dirFile, const char* newPath) { // sync() and cache directory entry sync(); oldFile = *this; - dir = cacheDirEntry(FatCache::CACHE_FOR_READ); + dir = cacheDirEntry(FsCache::CACHE_FOR_READ); if (!dir) { DBG_FAIL_MACRO; goto fail; @@ -910,7 +978,7 @@ bool FatFile::rename(FatFile* dirFile, const char* newPath) { memcpy(&entry, dir, sizeof(entry)); // make directory entry for new path if (isFile()) { - if (!file.open(dirFile, newPath, O_CREAT | O_EXCL | O_WRITE)) { + if (!file.open(dirFile, newPath, O_CREAT | O_EXCL | O_WRONLY)) { DBG_FAIL_MACRO; goto fail; } @@ -925,34 +993,36 @@ bool FatFile::rename(FatFile* dirFile, const char* newPath) { } // change to new directory entry - m_dirBlock = file.m_dirBlock; + m_dirSector = file.m_dirSector; m_dirIndex = file.m_dirIndex; m_lfnOrd = file.m_lfnOrd; m_dirCluster = file.m_dirCluster; // mark closed to avoid possible destructor close call - file.m_attr = FILE_ATTR_CLOSED; + file.m_attributes = FILE_ATTR_CLOSED; + file.m_flags = 0; // cache new directory entry - dir = cacheDirEntry(FatCache::CACHE_FOR_WRITE); + dir = cacheDirEntry(FsCache::CACHE_FOR_WRITE); if (!dir) { DBG_FAIL_MACRO; goto fail; } // copy all but name and name flags to new directory entry - memcpy(&dir->creationTimeTenths, &entry.creationTimeTenths, + memcpy(&dir->createTimeMs, &entry.createTimeMs, sizeof(entry) - sizeof(dir->name) - 2); dir->attributes = entry.attributes; // update dot dot if directory if (dirCluster) { // get new dot dot - uint32_t block = m_vol->clusterFirstBlock(dirCluster); - pc = m_vol->cacheFetchData(block, FatCache::CACHE_FOR_READ); - if (!pc) { + uint32_t sector = m_vol->clusterStartSector(dirCluster); + pc = m_vol->dataCachePrepare(sector, FsCache::CACHE_FOR_READ); + dir = reinterpret_cast(pc); + if (!dir) { DBG_FAIL_MACRO; goto fail; } - memcpy(&entry, &pc->dir[1], sizeof(entry)); + memcpy(&entry, &dir[1], sizeof(entry)); // free unused cluster if (!m_vol->freeChain(dirCluster)) { @@ -960,25 +1030,26 @@ bool FatFile::rename(FatFile* dirFile, const char* newPath) { goto fail; } // store new dot dot - block = m_vol->clusterFirstBlock(m_firstCluster); - pc = m_vol->cacheFetchData(block, FatCache::CACHE_FOR_WRITE); - if (!pc) { + sector = m_vol->clusterStartSector(m_firstCluster); + uint8_t* pc = m_vol->dataCachePrepare(sector, FsCache::CACHE_FOR_WRITE); + dir = reinterpret_cast(pc); + if (!dir) { DBG_FAIL_MACRO; goto fail; } - memcpy(&pc->dir[1], &entry, sizeof(entry)); + memcpy(&dir[1], &entry, sizeof(entry)); } // Remove old directory entry; oldFile.m_firstCluster = 0; - oldFile.m_flags = O_WRITE; - oldFile.m_attr = FILE_ATTR_FILE; + oldFile.m_flags = FILE_FLAG_WRITE; + oldFile.m_attributes = FILE_ATTR_FILE; if (!oldFile.remove()) { DBG_FAIL_MACRO; goto fail; } return m_vol->cacheSync(); -fail: + fail: return false; } //------------------------------------------------------------------------------ @@ -992,7 +1063,7 @@ bool FatFile::rmdir() { // make sure directory is empty while (1) { - dir_t* dir = readDirCache(true); + DirFat_t* dir = readDirCache(true); if (!dir) { // EOF if no error. if (!getError()) { @@ -1002,25 +1073,25 @@ bool FatFile::rmdir() { goto fail; } // done if past last used entry - if (dir->name[0] == DIR_NAME_FREE) { + if (dir->name[0] == FAT_NAME_FREE) { break; } // skip empty slot, '.' or '..' - if (dir->name[0] == DIR_NAME_DELETED || dir->name[0] == '.') { + if (dir->name[0] == FAT_NAME_DELETED || dir->name[0] == '.') { continue; } // error not empty - if (DIR_IS_FILE_OR_SUBDIR(dir)) { + if (isFatFileOrSubdir(dir)) { DBG_FAIL_MACRO; goto fail; } } // convert empty directory to normal file for remove - m_attr = FILE_ATTR_FILE; - m_flags |= O_WRITE; + m_attributes = FILE_ATTR_FILE; + m_flags |= FILE_FLAG_WRITE; return remove(); -fail: + fail: return false; } //------------------------------------------------------------------------------ @@ -1034,9 +1105,9 @@ bool FatFile::rmRfStar() { rewind(); while (1) { // remember position - index = m_curPosition/32; + index = m_curPosition/FS_DIR_SIZE; - dir_t* dir = readDirCache(); + DirFat_t* dir = readDirCache(); if (!dir) { // At EOF if no error. if (!getError()) { @@ -1046,21 +1117,21 @@ bool FatFile::rmRfStar() { goto fail; } // done if past last entry - if (dir->name[0] == DIR_NAME_FREE) { + if (dir->name[0] == FAT_NAME_FREE) { break; } // skip empty slot or '.' or '..' - if (dir->name[0] == DIR_NAME_DELETED || dir->name[0] == '.') { + if (dir->name[0] == FAT_NAME_DELETED || dir->name[0] == '.') { continue; } // skip if part of long file name or volume label in root - if (!DIR_IS_FILE_OR_SUBDIR(dir)) { + if (!isFatFileOrSubdir(dir)) { continue; } - if (!f.open(this, index, O_READ)) { + if (!f.open(this, index, O_RDONLY)) { DBG_FAIL_MACRO; goto fail; } @@ -1072,7 +1143,7 @@ bool FatFile::rmRfStar() { } } else { // ignore read-only - f.m_flags |= O_WRITE; + f.m_flags |= FILE_FLAG_WRITE; if (!f.remove()) { DBG_FAIL_MACRO; goto fail; @@ -1095,7 +1166,7 @@ bool FatFile::rmRfStar() { } return true; -fail: + fail: return false; } //------------------------------------------------------------------------------ @@ -1123,15 +1194,22 @@ bool FatFile::seekSet(uint32_t pos) { goto fail; } } else if (isRootFixed()) { - if (pos <= 32*m_vol->rootDirEntryCount()) { + if (pos <= FS_DIR_SIZE*m_vol->rootDirEntryCount()) { goto done; } DBG_FAIL_MACRO; goto fail; } - // calculate cluster index for cur and new position - nCur = (m_curPosition - 1) >> (m_vol->clusterSizeShift() + 9); - nNew = (pos - 1) >> (m_vol->clusterSizeShift() + 9); + // calculate cluster index for new position + nNew = (pos - 1) >> (m_vol->bytesPerClusterShift()); +#if USE_FAT_FILE_FLAG_CONTIGUOUS + if (isContiguous()) { + m_curCluster = m_firstCluster + nNew; + goto done; + } +#endif // USE_FAT_FILE_FLAG_CONTIGUOUS + // calculate cluster index for current position + nCur = (m_curPosition - 1) >> (m_vol->bytesPerClusterShift()); if (nNew < nCur || m_curPosition == 0) { // must follow chain from first cluster @@ -1147,97 +1225,63 @@ bool FatFile::seekSet(uint32_t pos) { } } -done: + done: m_curPosition = pos; + m_flags &= ~FILE_FLAG_PREALLOCATE; return true; -fail: + fail: m_curCluster = tmp; return false; } //------------------------------------------------------------------------------ -void FatFile::setpos(FatPos_t* pos) { - m_curPosition = pos->position; - m_curCluster = pos->cluster; -} -//------------------------------------------------------------------------------ bool FatFile::sync() { + uint16_t date, time; + uint8_t ms10; if (!isOpen()) { return true; } - if (m_flags & F_FILE_DIR_DIRTY) { - dir_t* dir = cacheDirEntry(FatCache::CACHE_FOR_WRITE); + if (m_flags & FILE_FLAG_DIR_DIRTY) { + DirFat_t* dir = cacheDirEntry(FsCache::CACHE_FOR_WRITE); // check for deleted by another open file object - if (!dir || dir->name[0] == DIR_NAME_DELETED) { + if (!dir || dir->name[0] == FAT_NAME_DELETED) { DBG_FAIL_MACRO; goto fail; } + dir->attributes = m_attributes & FS_ATTRIB_COPY; // do not set filesize for dir files if (isFile()) { - dir->fileSize = m_fileSize; + setLe32(dir->fileSize, m_fileSize); } - // update first cluster fields - dir->firstClusterLow = m_firstCluster & 0XFFFF; - dir->firstClusterHigh = m_firstCluster >> 16; + setLe16(dir->firstClusterLow, m_firstCluster & 0XFFFF); + setLe16(dir->firstClusterHigh, m_firstCluster >> 16); // set modify time if user supplied a callback date/time function - if (m_dateTime) { - m_dateTime(&dir->lastWriteDate, &dir->lastWriteTime); - dir->lastAccessDate = dir->lastWriteDate; + if (FsDateTime::callback) { + FsDateTime::callback(&date, &time, &ms10); + setLe16(dir->modifyDate, date); + setLe16(dir->accessDate, date); + setLe16(dir->modifyTime, time); } // clear directory dirty - m_flags &= ~F_FILE_DIR_DIRTY; + m_flags &= ~FILE_FLAG_DIR_DIRTY; } if (m_vol->cacheSync()) { return true; } DBG_FAIL_MACRO; -fail: + fail: m_error |= WRITE_ERROR; return false; } //------------------------------------------------------------------------------ -bool FatFile::timestamp(FatFile* file) { - dir_t* dir; - dir_t srcDir; - - // most be files get timestamps - if (!isFile() || !file->isFile() || !file->dirEntry(&srcDir)) { - DBG_FAIL_MACRO; - goto fail; - } - // update directory fields - if (!sync()) { - DBG_FAIL_MACRO; - goto fail; - } - dir = cacheDirEntry(FatCache::CACHE_FOR_WRITE); - if (!dir) { - DBG_FAIL_MACRO; - goto fail; - } - // copy timestamps - dir->lastAccessDate = srcDir.lastAccessDate; - dir->creationDate = srcDir.creationDate; - dir->creationTime = srcDir.creationTime; - dir->creationTimeTenths = srcDir.creationTimeTenths; - dir->lastWriteDate = srcDir.lastWriteDate; - dir->lastWriteTime = srcDir.lastWriteTime; - - // write back entry - return m_vol->cacheSync(); - -fail: - return false; -} -//------------------------------------------------------------------------------ bool FatFile::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; - dir_t* dir; + DirFat_t* dir; if (!isFile() || year < 1980 @@ -1257,115 +1301,91 @@ bool FatFile::timestamp(uint8_t flags, uint16_t year, uint8_t month, DBG_FAIL_MACRO; goto fail; } - dir = cacheDirEntry(FatCache::CACHE_FOR_WRITE); + dir = cacheDirEntry(FsCache::CACHE_FOR_WRITE); if (!dir) { DBG_FAIL_MACRO; goto fail; } - dirDate = FAT_DATE(year, month, day); - dirTime = FAT_TIME(hour, minute, second); + dirDate = FS_DATE(year, month, day); + dirTime = FS_TIME(hour, minute, second); if (flags & T_ACCESS) { - dir->lastAccessDate = dirDate; + setLe16(dir->accessDate, dirDate); } if (flags & T_CREATE) { - dir->creationDate = dirDate; - dir->creationTime = dirTime; - // seems to be units of 1/100 second not 1/10 as Microsoft states - dir->creationTimeTenths = second & 1 ? 100 : 0; + setLe16(dir->createDate, dirDate); + setLe16(dir->createTime, dirTime); + // units of 10 ms + dir->createTimeMs = second & 1 ? 100 : 0; } if (flags & T_WRITE) { - dir->lastWriteDate = dirDate; - dir->lastWriteTime = dirTime; + setLe16(dir->modifyDate, dirDate); + setLe16(dir->modifyTime, dirTime); } return m_vol->cacheSync(); -fail: + fail: return false; } //------------------------------------------------------------------------------ -bool FatFile::truncate(uint32_t length) { - uint32_t newPos; +bool FatFile::truncate() { + uint32_t toFree; // error if not a normal file or read-only - if (!isFile() || !(m_flags & O_WRITE)) { + if (!isWritable()) { DBG_FAIL_MACRO; goto fail; } - // error if length is greater than current size - if (length > m_fileSize) { - DBG_FAIL_MACRO; - goto fail; - } - // fileSize and length are zero - nothing to do - if (m_fileSize == 0) { - return true; - } - - // remember position for seek after truncation - newPos = m_curPosition > length ? length : m_curPosition; - - // position to last cluster in truncated file - if (!seekSet(length)) { - DBG_FAIL_MACRO; - goto fail; + if (m_firstCluster == 0) { + return true; } - if (length == 0) { - // free all clusters - if (!m_vol->freeChain(m_firstCluster)) { - DBG_FAIL_MACRO; - goto fail; - } - m_firstCluster = 0; - } else { - uint32_t toFree; + if (m_curCluster) { + toFree = 0; int8_t fg = m_vol->fatGet(m_curCluster, &toFree); if (fg < 0) { DBG_FAIL_MACRO; goto fail; } if (fg) { - // free extra clusters - if (!m_vol->freeChain(toFree)) { - DBG_FAIL_MACRO; - goto fail; - } // current cluster is end of chain if (!m_vol->fatPutEOC(m_curCluster)) { DBG_FAIL_MACRO; goto fail; } } + } else { + toFree = m_firstCluster; + m_firstCluster = 0; } - m_fileSize = length; + if (toFree) { + if (!m_vol->freeChain(toFree)) { + DBG_FAIL_MACRO; + goto fail; + } + } + m_fileSize = m_curPosition; // need to update directory entry - m_flags |= F_FILE_DIR_DIRTY; - - if (!sync()) { - DBG_FAIL_MACRO; - goto fail; - } - // set file to correct position - return seekSet(newPos); + m_flags |= FILE_FLAG_DIR_DIRTY; + return sync(); -fail: + fail: return false; } //------------------------------------------------------------------------------ -int FatFile::write(const void* buf, size_t nbyte) { +size_t FatFile::write(const void* buf, size_t nbyte) { // convert void* to uint8_t* - must be before goto statements const uint8_t* src = reinterpret_cast(buf); - cache_t* pc; + uint8_t* pc; uint8_t cacheOption; // number of bytes left to write - must be before goto statements size_t nToWrite = nbyte; size_t n; // error if not a normal file or is read-only - if (!isFile() || !(m_flags & O_WRITE)) { + if (!isWritable()) { DBG_FAIL_MACRO; goto fail; } // seek to end of file if append flag - if ((m_flags & O_APPEND)) { + if ((m_flags & FILE_FLAG_APPEND)) { if (!seekSet(m_fileSize)) { DBG_FAIL_MACRO; goto fail; @@ -1377,16 +1397,30 @@ int FatFile::write(const void* buf, size_t nbyte) { goto fail; } while (nToWrite) { - uint8_t blockOfCluster = m_vol->blockOfCluster(m_curPosition); - uint16_t blockOffset = m_curPosition & 0X1FF; - if (blockOfCluster == 0 && blockOffset == 0) { + uint8_t sectorOfCluster = m_vol->sectorOfCluster(m_curPosition); + uint16_t sectorOffset = m_curPosition & m_vol->sectorMask(); + if (sectorOfCluster == 0 && sectorOffset == 0) { // start of new cluster if (m_curCluster != 0) { +#if USE_FAT_FILE_FLAG_CONTIGUOUS + int8_t fg; + if (isContiguous() && m_fileSize > m_curPosition) { + m_curCluster++; + fg = 1; + } else { + fg = m_vol->fatGet(m_curCluster, &m_curCluster); + if (fg < 0) { + DBG_FAIL_MACRO; + goto fail; + } + } +#else // USE_FAT_FILE_FLAG_CONTIGUOUS int8_t fg = m_vol->fatGet(m_curCluster, &m_curCluster); if (fg < 0) { DBG_FAIL_MACRO; goto fail; } +#endif // USE_FAT_FILE_FLAG_CONTIGUOUS if (fg == 0) { // add cluster if at end of chain if (!addCluster()) { @@ -1407,65 +1441,59 @@ int FatFile::write(const void* buf, size_t nbyte) { } } } - // block for data write - uint32_t block = m_vol->clusterFirstBlock(m_curCluster) + blockOfCluster; + // sector for data write + uint32_t sector = m_vol->clusterStartSector(m_curCluster) + + sectorOfCluster; - if (blockOffset != 0 || nToWrite < 512) { - // partial block - must use cache - // max space in block - n = 512 - blockOffset; + if (sectorOffset != 0 || nToWrite < m_vol->bytesPerSector()) { + // partial sector - must use cache + // max space in sector + n = m_vol->bytesPerSector() - sectorOffset; // lesser of space and amount to write if (n > nToWrite) { n = nToWrite; } - if (blockOffset == 0 && m_curPosition >= m_fileSize) { - // start of new block don't need to read into cache - cacheOption = FatCache::CACHE_RESERVE_FOR_WRITE; + if (sectorOffset == 0 && + (m_curPosition >= m_fileSize || m_flags & FILE_FLAG_PREALLOCATE)) { + // start of new sector don't need to read into cache + cacheOption = FsCache::CACHE_RESERVE_FOR_WRITE; } else { - // rewrite part of block - cacheOption = FatCache::CACHE_FOR_WRITE; + // rewrite part of sector + cacheOption = FsCache::CACHE_FOR_WRITE; } - pc = m_vol->cacheFetchData(block, cacheOption); + pc = m_vol->dataCachePrepare(sector, cacheOption); if (!pc) { DBG_FAIL_MACRO; goto fail; } - uint8_t* dst = pc->data + blockOffset; + uint8_t* dst = pc + sectorOffset; memcpy(dst, src, n); - if (512 == (n + blockOffset)) { - // Force write if block is full - improves large writes. + if (m_vol->bytesPerSector() == (n + sectorOffset)) { + // Force write if sector is full - improves large writes. if (!m_vol->cacheSyncData()) { DBG_FAIL_MACRO; goto fail; } } -#if USE_MULTI_BLOCK_IO - } else if (nToWrite >= 1024) { - // use multiple block write command - uint8_t maxBlocks = m_vol->blocksPerCluster() - blockOfCluster; - size_t nBlock = nToWrite >> 9; - if (nBlock > maxBlocks) { - nBlock = maxBlocks; - } - n = 512*nBlock; - if (block <= m_vol->cacheBlockNumber() - && block < (m_vol->cacheBlockNumber() + nBlock)) { - // invalidate cache if block is in cache - m_vol->cacheInvalidate(); +#if USE_MULTI_SECTOR_IO + } else if (nToWrite >= 2*m_vol->bytesPerSector()) { + // use multiple sector write command + uint32_t maxSectors = m_vol->sectorsPerCluster() - sectorOfCluster; + uint32_t nSector = nToWrite >> m_vol->bytesPerSectorShift(); + if (nSector > maxSectors) { + nSector = maxSectors; } - if (!m_vol->writeBlocks(block, src, nBlock)) { + n = nSector << m_vol->bytesPerSectorShift(); + if (!m_vol->cacheSafeWrite(sector, src, nSector)) { DBG_FAIL_MACRO; goto fail; } -#endif // USE_MULTI_BLOCK_IO +#endif // USE_MULTI_SECTOR_IO } else { - // use single block write command - n = 512; - if (m_vol->cacheBlockNumber() == block) { - m_vol->cacheInvalidate(); - } - if (!m_vol->writeBlock(block, src)) { + // use single sector write command + n = m_vol->bytesPerSector(); + if (!m_vol->cacheSafeWrite(sector, src)) { DBG_FAIL_MACRO; goto fail; } @@ -1477,22 +1505,15 @@ int FatFile::write(const void* buf, size_t nbyte) { if (m_curPosition > m_fileSize) { // update fileSize and insure sync will update dir entry m_fileSize = m_curPosition; - m_flags |= F_FILE_DIR_DIRTY; - } else if (m_dateTime) { + m_flags |= FILE_FLAG_DIR_DIRTY; + } else if (FsDateTime::callback) { // insure sync will update modified date and time - m_flags |= F_FILE_DIR_DIRTY; - } - - if (m_flags & O_SYNC) { - if (!sync()) { - DBG_FAIL_MACRO; - goto fail; - } + m_flags |= FILE_FLAG_DIR_DIRTY; } return nbyte; -fail: + fail: // return for write error m_error |= WRITE_ERROR; - return -1; + return 0; } diff --git a/avr/cores/megacommand/SdFat/FatLib/FatFile.h b/avr/cores/megacommand/SdFat/FatLib/FatFile.h index cc8745061..19546f56d 100644 --- a/avr/cores/megacommand/SdFat/FatLib/FatFile.h +++ b/avr/cores/megacommand/SdFat/FatLib/FatFile.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 20011-2017 Bill Greiman + * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -28,15 +28,15 @@ * \file * \brief FatFile class */ -// #include #include #include #include -#include "FatLibConfig.h" -#include "FatApiConstants.h" -#include "FatStructs.h" -#include "FatVolume.h" -class FatFileSystem; +#include "../common/FmtNumber.h" +#include "../common/FsApiConstants.h" +#include "../common/FsDateTime.h" +#include "../common/FsName.h" +#include "FatPartition.h" +class FatVolume; //------------------------------------------------------------------------------ // Stuff to store strings in AVR flash. #ifdef __AVR__ @@ -69,28 +69,47 @@ struct FatPos_t { uint32_t position; /** cluster for position */ uint32_t cluster; - FatPos_t() : position(0), cluster(0) {} }; //------------------------------------------------------------------------------ /** Expression for path name separator. */ #define isDirSeparator(c) ((c) == '/') //------------------------------------------------------------------------------ /** - * \struct fname_t - * \brief Internal type for Short File Name - do not use in user apps. + * \class FatLfn_t + * \brief Internal type for Long File Name - do not use in user apps. */ -struct fname_t { - /** Flags for base and extension character case and LFN. */ - uint8_t flags; - /** length of Long File Name */ + +class FatLfn_t : public FsName { + public: + /** UTF-16 length of Long File Name */ size_t len; - /** Long File Name start. */ - const char* lfn; - /** position for sequence number */ + /** Position for sequence number. */ uint8_t seqPos; + /** Flags for base and extension character case and LFN. */ + uint8_t flags; + /** Short File Name */ + uint8_t sfn[11]; +}; +/** + * \class FatSfn_t + * \brief Internal type for Short 8.3 File Name - do not use in user apps. + */ +class FatSfn_t { + public: + /** Flags for base and extension character case and LFN. */ + uint8_t flags; /** Short File Name */ uint8_t sfn[11]; }; + +#if USE_LONG_FILE_NAMES +/** Internal class for file names */ +typedef FatLfn_t FatName_t; +#else // USE_LONG_FILE_NAMES +/** Internal class for file names */ +typedef FatSfn_t FatName_t; +#endif // USE_LONG_FILE_NAMES + /** Derived from a LFN with loss or conversion of characters. */ const uint8_t FNAME_FLAG_LOST_CHARS = 0X01; /** Base-name or extension has mixed case. */ @@ -99,9 +118,12 @@ const uint8_t FNAME_FLAG_MIXED_CASE = 0X02; const uint8_t FNAME_FLAG_NEED_LFN = FNAME_FLAG_LOST_CHARS | FNAME_FLAG_MIXED_CASE; /** Filename base-name is all lower case */ -const uint8_t FNAME_FLAG_LC_BASE = DIR_NT_LC_BASE; +const uint8_t FNAME_FLAG_LC_BASE = FAT_CASE_LC_BASE; /** Filename extension is all lower case. */ -const uint8_t FNAME_FLAG_LC_EXT = DIR_NT_LC_EXT; +const uint8_t FNAME_FLAG_LC_EXT = FAT_CASE_LC_EXT; +#if FNAME_FLAG_NEED_LFN & (FAT_CASE_LC_BASE || FAT_CASE_LC_EXT) +#error FNAME_FLAG_NEED_LFN & (FAT_CASE_LC_BASE || FAT_CASE_LC_EXT) +#endif // FNAME_FLAG_NEED_LFN & (FAT_CASE_LC_BASE || FAT_CASE_LC_EXT) //============================================================================== /** * \class FatFile @@ -110,206 +132,119 @@ const uint8_t FNAME_FLAG_LC_EXT = DIR_NT_LC_EXT; class FatFile { public: /** Create an instance. */ - FatFile() : m_attr(FILE_ATTR_CLOSED), m_error(0) {} + FatFile() {} /** Create a file object and open it in the current working directory. * - * \param[in] path A path with a valid 8.3 DOS name for a file to be opened. + * \param[in] path A path for a file to be opened. * * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive * OR of open flags. see FatFile::open(FatFile*, const char*, uint8_t). */ - FatFile(const char* path, uint8_t oflag) { - m_attr = FILE_ATTR_CLOSED; - m_error = 0; + FatFile(const char* path, oflag_t oflag) { open(path, oflag); } #if DESTRUCTOR_CLOSES_FILE + /** Destructor */ ~FatFile() { if (isOpen()) { close(); } } #endif // DESTRUCTOR_CLOSES_FILE - -#if ENABLE_ARDUINO_FEATURES - /** List directory contents. + /** The parenthesis operator. * - * \param[in] flags The inclusive OR of - * - * LS_DATE - %Print file modification date - * - * LS_SIZE - %Print file size. - * - * LS_R - Recursive list of subdirectories. + * \return true if a file is open. + */ + operator bool() const {return isOpen();} + /** + * \return user settable file attributes for success else -1. */ - void ls(uint8_t flags = 0) { - ls(&Serial, flags); + int attrib() { + return isFileOrSubDir() ? m_attributes & FS_ATTRIB_USER_SETTABLE : -1; } - /** %Print a directory date field. + /** Set file attributes * - * Format is yyyy-mm-dd. + * \param[in] bits bit-wise or of selected attributes: FS_ATTRIB_READ_ONLY, + * FS_ATTRIB_HIDDEN, FS_ATTRIB_SYSTEM, FS_ATTRIB_ARCHIVE. * - * \param[in] fatDate The date field from a directory entry. + * \note attrib() will fail for set read-only if the file is open for write. + * \return true for success or false for failure. */ - static void printFatDate(uint16_t fatDate) { - printFatDate(&Serial, fatDate); - } - /** %Print a directory time field. + bool attrib(uint8_t bits); + /** \return The number of bytes available from the current position + * to EOF for normal files. INT_MAX is returned for very large files. * - * Format is hh:mm:ss. + * available32() is recomended for very large files. * - * \param[in] fatTime The time field from a directory entry. - */ - static void printFatTime(uint16_t fatTime) { - printFatTime(&Serial, fatTime); - } - /** Print a file's name. + * Zero is returned for directory files. * - * \return The value true is returned for success and - * the value false is returned for failure. */ - size_t printName() { - return FatFile::printName(&Serial); - } -#endif // ENABLE_ARDUINO_FEATURES - - /** \return value of writeError */ - bool getWriteError() { - return m_error & WRITE_ERROR; + int available() const { + uint32_t n = available32(); + return n > INT_MAX ? INT_MAX : n; } - /** Set writeError to zero */ - void clearWriteError() { - m_error &= ~WRITE_ERROR; + /** \return The number of bytes available from the current position + * to EOF for normal files. Zero is returned for directory files. + */ + uint32_t available32() const { + return isFile() ? fileSize() - curPosition() : 0; } /** Clear all error bits. */ void clearError() { m_error = 0; } - /** \return All error bits. */ - uint8_t getError() { - return m_error; - } - /** get position for streams - * \param[out] pos struct to receive position - */ - void getpos(FatPos_t* pos); - /** set position for streams - * \param[out] pos struct with value for new position - */ - void setpos(FatPos_t* pos); - /** \return The number of bytes available from the current position - * to EOF for normal files. Zero is returned for directory files. - */ - uint32_t available() { - return isFile() ? fileSize() - curPosition() : 0; + /** Set writeError to zero */ + void clearWriteError() { + m_error &= ~WRITE_ERROR; } /** Close a file and force cached data and directory information * to be written to the storage device. * - * \return The value true is returned for success and - * the value false is returned for failure. + * \return true for success or false for failure. */ bool close(); - /** Check for contiguous file and return its raw block range. + /** Check for contiguous file and return its raw sector range. * - * \param[out] bgnBlock the first block address for the file. - * \param[out] endBlock the last block address for the file. + * \param[out] bgnSector the first sector address for the file. + * \param[out] endSector the last sector address for the file. * - * \return The value true is returned for success and - * the value false is returned for failure. + * Set the contiguous flag if the file is contiguous. + * The parameters may be nullptr to only set the flag. + * \return true for success or false for failure. */ - bool contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock); + bool contiguousRange(uint32_t* bgnSector, uint32_t* endSector); /** Create and open a new contiguous file of a specified size. * * \param[in] dirFile The directory where the file will be created. - * \param[in] path A path with a validfile name. + * \param[in] path A path with a valid file name. * \param[in] size The desired file size. * - * \return The value true is returned for success and - * the value false, is returned for failure. + * \return true for success or false for failure. */ bool createContiguous(FatFile* dirFile, const char* path, uint32_t size); /** Create and open a new contiguous file of a specified size. * - * \param[in] path A path with a validfile name. + * \param[in] path A path with a valid file name. * \param[in] size The desired file size. * - * \return The value true is returned for success and - * the value false, is returned for failure. + * \return true for success or false for failure. */ - bool createContiguous(const char* path, uint32_t size) { - return createContiguous(m_cwd, path, size); - } + bool createContiguous(const char* path, uint32_t size); /** \return The current cluster number for a file or directory. */ - uint32_t curCluster() const { - return m_curCluster; - } + uint32_t curCluster() const {return m_curCluster;} + /** \return The current position for a file or directory. */ - uint32_t curPosition() const { - return m_curPosition; - } - /** \return Current working directory */ - static FatFile* cwd() { - return m_cwd; - } - /** Set the date/time callback function - * - * \param[in] dateTime The user's call back function. The callback - * function is of the form: - * - * \code - * void dateTime(uint16_t* date, uint16_t* time) { - * uint16_t year; - * uint8_t month, day, hour, minute, second; - * - * // User gets date and time from GPS or real-time clock here - * - * // return date using FAT_DATE macro to format fields - * *date = FAT_DATE(year, month, day); - * - * // return time using FAT_TIME macro to format fields - * *time = FAT_TIME(hour, minute, second); - * } - * \endcode - * - * Sets the function that is called when a file is created or when - * a file's directory entry is modified by sync(). All timestamps, - * access, creation, and modify, are set when a file is created. - * sync() maintains the last access date and last modify date/time. - * - * See the timestamp() function. - */ - static void dateTimeCallback( - void (*dateTime)(uint16_t* date, uint16_t* time)) { - m_dateTime = dateTime; - } - /** Cancel the date/time callback function. */ - static void dateTimeCallbackCancel() { - m_dateTime = 0; - } + uint32_t curPosition() const {return m_curPosition;} /** Return a file's directory entry. * * \param[out] dir Location for return of the file's directory entry. * - * \return The value true is returned for success and - * the value false is returned for failure. - */ - bool dirEntry(dir_t* dir); - /** - * \return The index of this file in it's directory. - */ - uint16_t dirIndex() { - return m_dirIndex; - } - /** Format the name field of \a dir into the 13 byte array - * \a name in standard 8.3 short name format. - * - * \param[in] dir The directory structure containing the name. - * \param[out] name A 13 byte char array for the formatted name. - * \return length of the name. + * \return true for success or false for failure. */ - static uint8_t dirName(const dir_t* dir, char* name); + bool dirEntry(DirFat_t* dir); + /** \return Directory entry index. */ + uint16_t dirIndex() const {return m_dirIndex;} /** \return The number of bytes allocated to a directory or zero * if an error occurs. */ @@ -326,21 +261,25 @@ class FatFile { * * The calling instance must be an open directory file. * - * dirFile.exists("TOFIND.TXT") searches for "TOFIND.TXT" in the directory + * dirFile.exists("TOFIND.TXT") searches for "TOFIND.TXT" in the directory * dirFile. * - * \return true if the file exists else false. + * \return True if the file exists. */ bool exists(const char* path) { FatFile file; - return file.open(this, path, O_READ); + return file.open(this, path, O_RDONLY); } + /** get position for streams + * \param[out] pos struct to receive position + */ + void fgetpos(fspos_t* pos) const; /** * Get a string from a file. * * fgets() reads bytes from a file into the array pointed to by \a str, until - * \a num - 1 bytes are read, or a delimiter is read and transferred to \a str, - * or end-of-file is encountered. The string is then terminated + * \a num - 1 bytes are read, or a delimiter is read and transferred to + * \a str, or end-of-file is encountered. The string is then terminated * with a null byte. * * fgets() deletes CR, '\\r', from the string. This insures only a '\\n' @@ -353,17 +292,63 @@ class FatFile { * \param[in] delim Optional set of delimiters. The default is "\n". * * \return For success fgets() returns the length of the string in \a str. - * If no data is read, fgets() returns zero for EOF or -1 if an error occurred. + * If no data is read, fgets() returns zero for EOF or -1 if an error + * occurred. */ - int16_t fgets(char* str, int16_t num, char* delim = 0); + int fgets(char* str, int num, char* delim = nullptr); /** \return The total number of bytes in a file. */ - uint32_t fileSize() const { - return m_fileSize; - } - /** \return The first cluster number for a file or directory. */ - uint32_t firstCluster() const { - return m_firstCluster; + uint32_t fileSize() const {return m_fileSize;} + /** \return first sector of file or zero for empty file. */ + uint32_t firstBlock() const {return firstSector();} + /** \return Address of first sector or zero for empty file. */ + uint32_t firstSector() const; + /** Arduino name for sync() */ + void flush() {sync();} + /** set position for streams + * \param[in] pos struct with value for new position + */ + void fsetpos(const fspos_t* pos); + /** Get a file's access date. + * + * \param[out] pdate Packed date for directory entry. + * + * \return true for success or false for failure. + */ + bool getAccessDate(uint16_t* pdate); + /** Get a file's access date and time. + * + * \param[out] pdate Packed date for directory entry. + * \param[out] ptime return zero since FAT has no time. + * + * This function is for comparability in FsFile. + * + * \return true for success or false for failure. + */ + bool getAccessDateTime(uint16_t* pdate, uint16_t* ptime) { + if (!getAccessDate(pdate)) { + return false; + } + *ptime = 0; + return true; } + /** Get a file's create date and time. + * + * \param[out] pdate Packed date for directory entry. + * \param[out] ptime Packed time for directory entry. + * + * \return true for success or false for failure. + */ + bool getCreateDateTime(uint16_t* pdate, uint16_t* ptime); + /** \return All error bits. */ + uint8_t getError() const {return m_error;} + /** Get a file's modify date and time. + * + * \param[out] pdate Packed date for directory entry. + * \param[out] ptime Packed time for directory entry. + * + * \return true for success or false for failure. + */ + bool getModifyDateTime(uint16_t* pdate, uint16_t* ptime); /** * Get a file's name followed by a zero byte. * @@ -371,85 +356,79 @@ class FatFile { * \param[in] size The size of the array in bytes. The array * must be at least 13 bytes long. The file's name will be * truncated if the file's name is too long. - * \return The value true, is returned for success and - * the value false, is returned for failure. + * \return length for success or zero for failure. + */ + size_t getName(char* name, size_t size); + /** + * Get a file's ASCII name followed by a zero. + * + * \param[out] name An array of characters for the file's name. + * \param[in] size The size of the array in characters. + * \return the name length. */ - bool getName(char* name, size_t size); + size_t getName7(char* name, size_t size); + /** + * Get a file's UTF-8 name followed by a zero. + * + * \param[out] name An array of characters for the file's name. + * \param[in] size The size of the array in characters. + * \return the name length. + */ + size_t getName8(char* name, size_t size); +#ifndef DOXYGEN_SHOULD_SKIP_THIS + size_t __attribute__((error("use getSFN(name, size)"))) getSFN(char* name); +#endif // DOXYGEN_SHOULD_SKIP_THIS /** * Get a file's Short File Name followed by a zero byte. * * \param[out] name An array of characters for the file's name. - * The array must be at least 13 bytes long. - * \return The value true, is returned for success and - * the value false, is returned for failure. - */ - bool getSFN(char* name); - /** \return True if this is a directory else false. */ - bool isDir() const { - return m_attr & FILE_ATTR_DIR; - } - /** \return True if this is a normal file else false. */ - bool isFile() const { - return m_attr & FILE_ATTR_FILE; - } - /** \return True if this is a hidden file else false. */ - bool isHidden() const { - return m_attr & FILE_ATTR_HIDDEN; + * The array should be at least 13 bytes long. + * \param[in] size size of name array. + * \return true for success or false for failure. + */ + size_t getSFN(char* name, size_t size); + /** \return value of writeError */ + bool getWriteError() const { + return isOpen() ? m_error & WRITE_ERROR : true; } + /** + * Check for device busy. + * + * \return true if busy else false. + */ + bool isBusy(); +#if USE_FAT_FILE_FLAG_CONTIGUOUS + /** \return True if the file is contiguous. */ + bool isContiguous() const {return m_flags & FILE_FLAG_CONTIGUOUS;} +#endif // USE_FAT_FILE_FLAG_CONTIGUOUS + /** \return True if this is a directory. */ + bool isDir() const {return m_attributes & FILE_ATTR_DIR;} + /** \return True if this is a normal file. */ + bool isFile() const {return m_attributes & FILE_ATTR_FILE;} + /** \return True if this is a normal file or sub-directory. */ + bool isFileOrSubDir() const {return isFile() || isSubDir();} + /** \return True if this is a hidden file. */ + bool isHidden() const {return m_attributes & FS_ATTRIB_HIDDEN;} /** \return true if this file has a Long File Name. */ - bool isLFN() const { - return m_lfnOrd; - } - /** \return True if this is an open file/directory else false. */ - bool isOpen() const { - return m_attr; - } + bool isLFN() const {return m_lfnOrd;} + /** \return True if this is an open file/directory. */ + bool isOpen() const {return m_attributes;} + /** \return True file is readable. */ + bool isReadable() const {return m_flags & FILE_FLAG_READ;} + /** \return True if file is read-only */ + bool isReadOnly() const {return m_attributes & FS_ATTRIB_READ_ONLY;} /** \return True if this is the root directory. */ - bool isRoot() const { - return m_attr & FILE_ATTR_ROOT; - } + bool isRoot() const {return m_attributes & FILE_ATTR_ROOT;} /** \return True if this is the FAT32 root directory. */ - bool isRoot32() const { - return m_attr & FILE_ATTR_ROOT32; - } + bool isRoot32() const {return m_attributes & FILE_ATTR_ROOT32;} /** \return True if this is the FAT12 of FAT16 root directory. */ - bool isRootFixed() const { - return m_attr & FILE_ATTR_ROOT_FIXED; - } - /** \return True if file is read-only */ - bool isReadOnly() const { - return m_attr & FILE_ATTR_READ_ONLY; - } - /** \return True if this is a subdirectory else false. */ - bool isSubDir() const { - return m_attr & FILE_ATTR_SUBDIR; - } - /** \return True if this is a system file else false. */ - bool isSystem() const { - return m_attr & FILE_ATTR_SYSTEM; - } - /** Check for a legal 8.3 character. - * \param[in] c Character to be checked. - * \return true for a legal 8.3 character else false. - */ - static bool legal83Char(uint8_t c) { - if (c == '"' || c == '|') { - return false; - } - // *+,./ - if (0X2A <= c && c <= 0X2F && c != 0X2D) { - return false; - } - // :;<=>? - if (0X3A <= c && c <= 0X3F) { - return false; - } - // [\] - if (0X5B <= c && c <= 0X5D) { - return false; - } - return 0X20 < c && c < 0X7F; - } + bool isRootFixed() const {return m_attributes & FILE_ATTR_ROOT_FIXED;} + /** \return True if this is a sub-directory. */ + bool isSubDir() const {return m_attributes & FILE_ATTR_SUBDIR;} + /** \return True if this is a system file. */ + bool isSystem() const {return m_attributes & FS_ATTRIB_SYSTEM;} + /** \return True file is writable. */ + bool isWritable() const {return m_flags & FILE_FLAG_WRITE;} /** List directory contents. * * \param[in] pr Print stream for list. @@ -464,34 +443,34 @@ class FatFile { * * \param[in] indent Amount of space before file name. Used for recursive * list to indicate subdirectory level. + * + * \return true for success or false for failure. */ - void ls(print_t* pr, uint8_t flags = 0, uint8_t indent = 0); + bool ls(print_t* pr, uint8_t flags = 0, uint8_t indent = 0); /** Make a new directory. * * \param[in] dir An open FatFile 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. + * \param[in] path A path with a valid name for the new directory. * * \param[in] pFlag Create missing parent directories if true. * - * \return The value true is returned for success and - * the value false is returned for failure. + * \return true for success or false for failure. */ bool mkdir(FatFile* dir, const char* path, bool pFlag = true); - /** Open a file in the volume working directory of a FatFileSystem. + /** Open a file in the volume root directory. * - * \param[in] fs File System where the file is located. + * \param[in] vol Volume where the file is located. * - * \param[in] path with a valid 8.3 DOS name for a file to be opened. + * \param[in] path with a valid name for a file to be opened. * - * \param[in] oflag bitwise-inclusive OR of open mode flags. + * \param[in] oflag bitwise-inclusive OR of open flags. * See see FatFile::open(FatFile*, const char*, uint8_t). * - * \return The value true is returned for success and - * the value false is returned for failure. + * \return true for success or false for failure. */ - bool open(FatFileSystem* fs, const char* path, uint8_t oflag); + bool open(FatVolume* vol, const char* path, oflag_t oflag = O_RDONLY); /** Open a file by index. * * \param[in] dirFile An open FatFile instance for the directory. @@ -499,30 +478,44 @@ class FatFile { * \param[in] index The \a index of the directory entry for the file to be * opened. The value for \a index is (directory file position)/32. * - * \param[in] oflag bitwise-inclusive OR of open mode flags. + * \param[in] oflag bitwise-inclusive OR of open flags. * See see FatFile::open(FatFile*, const char*, uint8_t). * * See open() by path for definition of flags. * \return true for success or false for failure. */ - bool open(FatFile* dirFile, uint16_t index, uint8_t oflag); + bool open(FatFile* dirFile, uint16_t index, oflag_t oflag = O_RDONLY); + /** Open a file by index in the current working directory. + * + * \param[in] index The \a index of the directory entry for the file to be + * opened. The value for \a index is (directory file position)/32. + * + * \param[in] oflag bitwise-inclusive OR of open flags. + * See see FatFile::open(FatFile*, const char*, uint8_t). + * + * See open() by path for definition of flags. + * \return true for success or false for failure. + */ + bool open(uint16_t index, oflag_t oflag = O_RDONLY); /** Open a file or directory by name. * * \param[in] dirFile An open FatFile 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. + * \param[in] path A path with a valid name for a file to be opened. * * \param[in] oflag Values for \a oflag are constructed by a - * bitwise-inclusive OR of flags from the following list + * bitwise-inclusive OR of flags from the following list. + * Only one of O_RDONLY, O_READ, O_WRONLY, O_WRITE, or + * O_RDWR is allowed. * - * O_READ - Open for reading. + * O_RDONLY - Open for reading. * - * O_RDONLY - Same as O_READ. + * O_READ - Same as O_RDONLY. * - * O_WRITE - Open for writing. + * O_WRONLY - Open for writing. * - * O_WRONLY - Same as O_WRITE. + * O_WRITE - Same as O_WRONLY. * * O_RDWR - Open for reading and writing. * @@ -534,14 +527,12 @@ class FatFile { * O_CREAT - If the file exists, this flag has no effect except as noted * under O_EXCL below. Otherwise, the file shall be created * - * O_EXCL - If O_CREAT and O_EXCL are set, open() shall fail if the file exists. - * - * O_SYNC - Call sync() after each write. This flag should not be used with - * write(uint8_t) or any functions do character at a time writes since sync() - * will be called after each byte. + * O_EXCL - If O_CREAT and O_EXCL are set, open() shall fail if the file + * exists. * * O_TRUNC - If the file exists and is a regular file, and the file is - * successfully opened and is not read only, its length shall be truncated to 0. + * successfully opened and is not read only, its length shall be truncated + * to 0. * * WARNING: A given file must not be opened by more than one FatFile object * or file corruption may occur. @@ -549,55 +540,91 @@ class FatFile { * \note Directory files must be opened read only. Write and truncation is * not allowed for directory files. * - * \return The value true is returned for success and - * the value false is returned for failure. + * \return true for success or false for failure. */ - bool open(FatFile* dirFile, const char* path, uint8_t oflag); - /** Open a file in the current working directory. + bool open(FatFile* dirFile, const char* path, oflag_t oflag = O_RDONLY); + /** Open a file in the current working volume. * - * \param[in] path A path with a valid 8.3 DOS name for a file to be opened. + * \param[in] path A path with a valid name for a file to be opened. * - * \param[in] oflag bitwise-inclusive OR of open mode flags. + * \param[in] oflag bitwise-inclusive OR of open flags. * See see FatFile::open(FatFile*, const char*, uint8_t). * - * \return The value true is returned for success and - * the value false is returned for failure. + * \return true for success or false for failure. */ - bool open(const char* path, uint8_t oflag = O_READ) { - return open(m_cwd, path, oflag); - } + bool open(const char* path, oflag_t oflag = O_RDONLY); + /** Open the current working directory. + * + * \return true for success or false for failure. + */ + bool openCwd(); + /** Open existing file wih Short 8.3 names. + * \param[in] path with short 8.3 names. + * + * the purpose of this function is to save flash on Uno + * and other small boards. + * + * Directories will be opened O_RDONLY, files O_RDWR. + * \return true for success or false for failure. + */ + bool openExistingSFN(const char* path); /** Open the next file or subdirectory in a directory. * * \param[in] dirFile An open FatFile instance for the directory * containing the file to be opened. * - * \param[in] oflag bitwise-inclusive OR of open mode flags. + * \param[in] oflag bitwise-inclusive OR of open flags. * See see FatFile::open(FatFile*, const char*, uint8_t). * * \return true for success or false for failure. */ - bool openNext(FatFile* dirFile, uint8_t oflag = O_READ); + bool openNext(FatFile* dirFile, oflag_t oflag = O_RDONLY); /** Open a volume's root directory. * * \param[in] vol The FAT volume containing the root directory to be opened. * - * \return The value true is returned for success and - * the value false is returned for failure. + * \return true for success or false for failure. */ bool openRoot(FatVolume* vol); + /** Return the next available byte without consuming it. * * \return The byte if no error and not at eof else -1; */ int peek(); + /** Allocate contiguous clusters to an empty file. + * + * The file must be empty with no clusters allocated. + * + * The file will contain uninitialized data. + * + * \param[in] length size of the file in bytes. + * \return true for success or false for failure. + */ + bool preAllocate(uint32_t length); + /** Print a file's access date + * + * \param[in] pr Print stream for output. + * + * \return The number of characters printed. + */ + size_t printAccessDate(print_t* pr); + /** Print a file's access date + * + * \param[in] pr Print stream for output. + * + * \return The number of characters printed. + */ + size_t printAccessDateTime(print_t* pr) { + return printAccessDate(pr); + } /** Print a file's creation date and time * * \param[in] pr Print stream for output. * - * \return The value true is returned for success and - * the value false is returned for failure. + * \return The number of bytes printed. */ - bool printCreateDateTime(print_t* pr); + size_t printCreateDateTime(print_t* pr); /** %Print a directory date field. * * Format is yyyy-mm-dd. @@ -620,55 +647,94 @@ class FatFile { * \param[in] prec Number of digits after decimal point. * \return The number of bytes written or -1 if an error occurs. */ - int printField(float value, char term, uint8_t prec = 2); - /** Print a number followed by a field terminator. - * \param[in] value The number to be printed. - * \param[in] term The field terminator. Use '\\n' for CR LF. - * \return The number of bytes written or -1 if an error occurs. - */ - int printField(int16_t value, char term); + size_t printField(double value, char term, uint8_t prec = 2) { + char buf[24]; + char* str = buf + sizeof(buf); + if (term) { + *--str = term; + if (term == '\n') { + *--str = '\r'; + } + } + str = fmtDouble(str, value, prec, false); + return write(str, buf + sizeof(buf) - str); + } /** Print a number followed by a field terminator. * \param[in] value The number to be printed. * \param[in] term The field terminator. Use '\\n' for CR LF. + * \param[in] prec Number of digits after decimal point. * \return The number of bytes written or -1 if an error occurs. */ - int printField(uint16_t value, char term); + size_t printField(float value, char term, uint8_t prec = 2) { + return printField(static_cast(value), term, prec); + } /** Print a number followed by a field terminator. * \param[in] value The number to be printed. * \param[in] term The field terminator. Use '\\n' for CR LF. * \return The number of bytes written or -1 if an error occurs. */ - int printField(int32_t value, char term); - /** Print a number followed by a field terminator. - * \param[in] value The number to be printed. - * \param[in] term The field terminator. Use '\\n' for CR LF. - * \return The number of bytes written or -1 if an error occurs. + template + size_t printField(Type value, char term) { + char sign = 0; + char buf[3*sizeof(Type) + 3]; + char* str = buf + sizeof(buf); + + if (term) { + *--str = term; + if (term == '\n') { + *--str = '\r'; + } + } + if (value < 0) { + value = -value; + sign = '-'; + } + if (sizeof(Type) < 4) { + str = fmtBase10(str, (uint16_t)value); + } else { + str = fmtBase10(str, (uint32_t)value); + } + if (sign) { + *--str = sign; + } + return write(str, &buf[sizeof(buf)] - str); + } + /** Print a file's size. + * + * \param[in] pr Print stream for output. + * + * \return The number of characters printed is returned + * for success and zero is returned for failure. */ - int printField(uint32_t value, char term); + size_t printFileSize(print_t* pr); /** Print a file's modify date and time * * \param[in] pr Print stream for output. * - * \return The value true is returned for success and - * the value false is returned for failure. + * \return The number of characters printed. */ - bool printModifyDateTime(print_t* pr); + size_t printModifyDateTime(print_t* pr); /** Print a file's name * * \param[in] pr Print stream for output. * - * \return The value true is returned for success and - * the value false is returned for failure. + * \return length for success or zero for failure. */ size_t printName(print_t* pr); - /** Print a file's size. + /** Print a file's ASCII name * * \param[in] pr Print stream for output. * - * \return The number of characters printed is returned - * for success and zero is returned for failure. + * \return true for success or false for failure. */ - size_t printFileSize(print_t* pr); + size_t printName7(print_t* pr); + /** Print a file's UTF-8 name + * + * \param[in] pr Print stream for output. + * + * \return true for success or false for failure. + */ + size_t printName8(print_t* pr); /** Print a file's Short File Name. * * \param[in] pr Print stream for output. @@ -690,19 +756,17 @@ class FatFile { * * \param[out] buf Pointer to the location that will receive the data. * - * \param[in] nbyte Maximum number of bytes to read. + * \param[in] count Maximum number of bytes to read. * * \return For success read() returns the number of bytes read. * A value less than \a nbyte, including zero, will be returned * if end of file is reached. - * If an error occurs, read() returns -1. Possible errors include - * read() called before a file has been opened, corrupt file system - * or an I/O error occurred. + * If an error occurs, read() returns -1. */ - int read(void* buf, size_t nbyte); + int read(void* buf, size_t count); /** Read the next directory entry from a directory file. * - * \param[out] dir The dir_t struct that will receive the data. + * \param[out] dir The DirFat_t struct that will receive the data. * * \return For success readDir() returns the number of bytes read. * A value of zero will be returned if end of file is reached. @@ -710,7 +774,7 @@ class FatFile { * readDir() called before a directory has been opened, this is not * a directory file or an I/O error occurred. */ - int8_t readDir(dir_t* dir); + int8_t readDir(DirFat_t* dir); /** Remove a file. * * The directory entry and all data for the file are deleted. @@ -719,38 +783,45 @@ class FatFile { * 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 true is returned for success and - * the value false is returned for failure. + * \return true for success or false for failure. */ bool remove(); /** Remove a file. * * The directory entry and all data for the file are deleted. * - * \param[in] dirFile The directory that contains the file. * \param[in] path Path for the file to be removed. * + * Example use: dirFile.remove(filenameToRemove); + * * \note This function should not be used to delete the 8.3 version of a * file that has a long name. For example if a file has the long name * "New Text Document.txt" you should not delete the 8.3 name "NEWTEX~1.TXT". * - * \return The value true is returned for success and - * the value false is returned for failure. + * \return true for success or false for failure. */ - static bool remove(FatFile* dirFile, const char* path); - /** Set the file's current position to zero. */ - void rewind() { - seekSet(0); - } + bool remove(const char* path); + /** Rename a file or subdirectory. + * \note the renamed file will be moved to the current volume working + * directory. + * + * \param[in] newPath New path name for the file/directory. + * + * \return true for success or false for failure. + */ + bool rename(const char* newPath); /** 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 true is returned for success and - * the value false is returned for failure. + * \return true for success or false for failure. */ bool rename(FatFile* dirFile, const char* newPath); + /** Set the file's current position to zero. */ + void rewind() { + seekSet(0); + } /** Remove a directory file. * * The directory file will be removed only if it is empty and is not the @@ -761,8 +832,7 @@ class FatFile { * 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 true is returned for success and - * the value false is returned for failure. + * \return true for success or false for failure. */ bool rmdir(); /** Recursively delete a directory and all contained files. @@ -777,8 +847,7 @@ class FatFile { * \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 true is returned for success and - * the value false is returned for failure. + * \return true for success or false for failure. */ bool rmRfStar(); /** Set the files position to current position + \a pos. See seekSet(). @@ -800,49 +869,15 @@ class FatFile { * * \param[in] pos The new position in bytes from the beginning of the file. * - * \return The value true is returned for success and - * the value false is returned for failure. + * \return true for success or false for failure. */ bool seekSet(uint32_t pos); - /** Set the current working directory. - * - * \param[in] dir New current working directory. - * - * \return true for success else false. - */ - static bool setCwd(FatFile* dir) { - if (!dir->isDir()) { - return false; - } - m_cwd = dir; - return true; - } - /** \return first block of file or zero for empty file. */ - uint32_t firstBlock() { - if (m_firstCluster) { - return m_vol->clusterFirstBlock(m_firstCluster); - } - return 0; - } /** The sync() call causes all modified data and directory fields * to be written to the storage device. * - * \return The value true is returned for success and - * the value false is returned for failure. + * \return true for success or false for failure. */ bool sync(); - /** Copy a file's timestamps - * - * \param[in] file File to copy timestamps from. - * - * \note - * Modify and access timestamps may be overwritten if a date time callback - * function has been set by dateTimeCallback(). - * - * \return The value true is returned for success and - * the value false is returned for failure. - */ - bool timestamp(FatFile* file); /** Set a file's timestamps in its directory entry. * * \param[in] flags Values for \a flags are constructed by a bitwise-inclusive @@ -873,46 +908,41 @@ class FatFile { * Modify and access timestamps may be overwritten if a date time callback * function has been set by dateTimeCallback(). * - * \return The value true is returned for success and - * the value false is returned for failure. + * \return true for success or false for failure. */ bool timestamp(uint8_t flags, 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 fileType() - * if possible. + + /** Truncate a file at 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. * - * \return The file or directory type. + * \return true for success or false for failure. */ - uint8_t fileAttr() const { - return m_attr; - } + bool truncate(); /** 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. + * will be set to end of file. * * \param[in] length The desired length for the file. * - * \return The value true is returned for success and - * the value false is returned for failure. + * \return true for success or false for failure. */ - bool truncate(uint32_t length); - /** \return FatVolume that contains this file. */ - FatVolume* volume() const { - return m_vol; + bool truncate(uint32_t length) { + return seekSet(length) && truncate(); } /** Write a string to a file. Used by the Arduino Print class. * \param[in] str Pointer to the string. * Use getWriteError to check for errors. * \return count of characters written for success or -1 for failure. */ - int write(const char* str) { + size_t write(const char* str) { return write(str, strlen(str)); } /** Write a single byte. * \param[in] b The byte to be written. * \return +1 for success or -1 for failure. */ - int write(uint8_t b) { + size_t write(uint8_t b) { return write(&b, 1); } /** Write data to an open file. @@ -922,85 +952,135 @@ class FatFile { * * \param[in] buf Pointer to the location of the data to be written. * - * \param[in] nbyte Number of bytes to write. + * \param[in] count Number of bytes to write. * * \return For success write() returns the number of bytes written, always - * \a nbyte. If an error occurs, write() returns -1. Possible errors - * include write() is called before a file has been opened, write is called - * for a read-only file, device is full, a corrupt file system or an I/O error. + * \a count. If an error occurs, write() returns zero and writeError is set. * */ - int write(const void* buf, size_t nbyte); + size_t write(const void* buf, size_t count); //------------------------------------------------------------------------------ +#if ENABLE_ARDUINO_SERIAL + /** List directory contents. + * + * \param[in] flags The inclusive OR of + * + * LS_DATE - %Print file modification date + * + * LS_SIZE - %Print file size. + * + * LS_R - Recursive list of subdirectories. + * + * \return true for success or false for failure. + */ + bool ls(uint8_t flags = 0) { + return ls(&Serial, flags); + } + /** Print a file's name. + * + * \return length for success or zero for failure. + */ + size_t printName() { + return FatFile::printName(&Serial); + } +#endif // ENABLE_ARDUINO_SERIAL + private: + /** FatVolume allowed access to private members. */ + friend class FatVolume; + /** This file has not been opened. */ static const uint8_t FILE_ATTR_CLOSED = 0; - /** File is read-only. */ - static const uint8_t FILE_ATTR_READ_ONLY = DIR_ATT_READ_ONLY; - /** File should be hidden in directory listings. */ - static const uint8_t FILE_ATTR_HIDDEN = DIR_ATT_HIDDEN; - /** Entry is for a system file. */ - static const uint8_t FILE_ATTR_SYSTEM = DIR_ATT_SYSTEM; /** Entry for normal data file */ static const uint8_t FILE_ATTR_FILE = 0X08; /** Entry is for a subdirectory */ - static const uint8_t FILE_ATTR_SUBDIR = DIR_ATT_DIRECTORY; + static const uint8_t FILE_ATTR_SUBDIR = FS_ATTRIB_DIRECTORY; /** A FAT12 or FAT16 root directory */ - static const uint8_t FILE_ATTR_ROOT_FIXED = 0X20; + static const uint8_t FILE_ATTR_ROOT_FIXED = 0X40; /** A FAT32 root directory */ - static const uint8_t FILE_ATTR_ROOT32 = 0X40; + static const uint8_t FILE_ATTR_ROOT32 = 0X80; /** Entry is for root. */ - static const uint8_t FILE_ATTR_ROOT = FILE_ATTR_ROOT_FIXED | FILE_ATTR_ROOT32; + static const uint8_t FILE_ATTR_ROOT = + FILE_ATTR_ROOT_FIXED | FILE_ATTR_ROOT32; /** Directory type bits */ static const uint8_t FILE_ATTR_DIR = FILE_ATTR_SUBDIR | FILE_ATTR_ROOT; - /** Attributes to copy from directory entry */ - static const uint8_t FILE_ATTR_COPY = DIR_ATT_READ_ONLY | DIR_ATT_HIDDEN | - DIR_ATT_SYSTEM | DIR_ATT_DIRECTORY; - - /** experimental don't use */ - - bool openParent(FatFile* dir); // private functions + bool addCluster(); bool addDirCluster(); - dir_t* cacheDirEntry(uint8_t action); - static uint8_t lfnChecksum(uint8_t* name); - bool lfnUniqueSfn(fname_t* fname); + DirFat_t* cacheDir(uint16_t index) { + return seekSet(32UL*index) ? readDirCache() : nullptr; + } + DirFat_t* cacheDirEntry(uint8_t action); + bool cmpName(uint16_t index, FatLfn_t* fname, uint8_t lfnOrd); + bool createLFN(uint16_t index, FatLfn_t* fname, uint8_t lfnOrd); + uint16_t getLfnChar(DirLfn_t* ldir, uint8_t i); + uint8_t lfnChecksum(uint8_t* name) { + uint8_t sum = 0; + for (uint8_t i = 0; i < 11; i++) { + sum = (((sum & 1) << 7) | (sum >> 1)) + name[i]; + } + return sum; + } + static bool makeSFN(FatLfn_t* fname); + bool makeUniqueSfn(FatLfn_t* fname); bool openCluster(FatFile* file); - static bool parsePathName(const char* str, fname_t* fname, const char** ptr); - bool mkdir(FatFile* parent, fname_t* fname); - bool open(FatFile* dirFile, fname_t* fname, uint8_t oflag); - bool openCachedEntry(FatFile* dirFile, uint16_t cacheIndex, uint8_t oflag, + bool parsePathName(const char* str, FatLfn_t* fname, const char** ptr); + bool parsePathName(const char* str, FatSfn_t* fname, const char** ptr); + bool mkdir(FatFile* parent, FatName_t* fname); + bool open(FatFile* dirFile, FatLfn_t* fname, oflag_t oflag); + bool open(FatFile* dirFile, FatSfn_t* fname, oflag_t oflag); + bool openSFN(FatSfn_t* fname); + bool openCachedEntry(FatFile* dirFile, uint16_t cacheIndex, oflag_t oflag, uint8_t lfnOrd); - bool readLBN(uint32_t* lbn); - dir_t* readDirCache(bool skipReadOk = false); - bool setDirSize(); + DirFat_t* readDirCache(bool skipReadOk = false); // bits defined in m_flags - // should be 0X0F - static const uint8_t F_OFLAG = (O_ACCMODE | O_APPEND | O_SYNC); + static const uint8_t FILE_FLAG_READ = 0X01; + static const uint8_t FILE_FLAG_WRITE = 0X02; + static const uint8_t FILE_FLAG_APPEND = 0X08; + // treat curPosition as valid length. + static const uint8_t FILE_FLAG_PREALLOCATE = 0X20; + // file is contiguous + static const uint8_t FILE_FLAG_CONTIGUOUS = 0X40; // sync of directory entry required - static const uint8_t F_FILE_DIR_DIRTY = 0X80; + static const uint8_t FILE_FLAG_DIR_DIRTY = 0X80; - // global pointer to cwd dir - static FatFile* m_cwd; - // data time callback function - static void (*m_dateTime)(uint16_t* date, uint16_t* time); // private data static const uint8_t WRITE_ERROR = 0X1; static const uint8_t READ_ERROR = 0X2; - uint8_t m_attr; // File attributes - uint8_t m_error; // Error bits. - uint8_t m_flags; // See above for definition of m_flags bits + + uint8_t m_attributes = FILE_ATTR_CLOSED; + uint8_t m_error = 0; // Error bits. + uint8_t m_flags = 0; // See above for definition of m_flags bits uint8_t m_lfnOrd; uint16_t m_dirIndex; // index of directory entry in dir file FatVolume* m_vol; // volume where file is located uint32_t m_dirCluster; uint32_t m_curCluster; // cluster for current file position uint32_t m_curPosition; // current file position - uint32_t m_dirBlock; // block for this files directory entry + uint32_t m_dirSector; // sector for this files directory entry uint32_t m_fileSize; // file size in bytes uint32_t m_firstCluster; // first cluster of file }; + +#include "../common/ArduinoFiles.h" +/** + * \class File32 + * \brief FAT16/FAT32 file with Arduino Stream. + */ +class File32 : public StreamFile { + public: + /** Opens the next file or folder in a directory. + * + * \param[in] oflag open flags. + * \return a FatStream object. + */ + File32 openNextFile(oflag_t oflag = O_RDONLY) { + File32 tmpFile; + tmpFile.openNext(this, oflag); + return tmpFile; + } +}; #endif // FatFile_h diff --git a/avr/cores/megacommand/SdFat/FatLib/FatFileLFN.cpp b/avr/cores/megacommand/SdFat/FatLib/FatFileLFN.cpp index 52b978414..aa0533537 100644 --- a/avr/cores/megacommand/SdFat/FatLib/FatFileLFN.cpp +++ b/avr/cores/megacommand/SdFat/FatLib/FatFileLFN.cpp @@ -1,5 +1,5 @@ /** - * Copyright (c) 20011-2017 Bill Greiman + * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -22,76 +22,24 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ -#include "FatFile.h" -//------------------------------------------------------------------------------ -// -uint8_t FatFile::lfnChecksum(uint8_t* name) { - uint8_t sum = 0; - for (uint8_t i = 0; i < 11; i++) { - sum = (((sum & 1) << 7) | ((sum & 0xfe) >> 1)) + name[i]; - } - return sum; -} +#define DBG_FILE "FatFileLFN.cpp" +#include "../common/DebugMacros.h" +#include "../common/upcase.h" +#include "../common/FsUtf.h" +#include "FatLib.h" #if USE_LONG_FILE_NAMES //------------------------------------------------------------------------------ -// Saves about 90 bytes of flash on 328 over tolower(). -inline char lfnToLower(char c) { - return 'A' <= c && c <= 'Z' ? c + 'a' - 'A' : c; -} -//------------------------------------------------------------------------------ -// Daniel Bernstein University of Illinois at Chicago. -// Original had + instead of ^ -static uint16_t Bernstein(uint16_t hash, const char *str, size_t len) { - for (size_t i = 0; i < len; i++) { - // hash = hash * 33 ^ str[i]; - hash = ((hash << 5) + hash) ^ str[i]; - } - return hash; -} -//------------------------------------------------------------------------------ -/** - * Fetch a 16-bit long file name character. - * - * \param[in] ldir Pointer to long file name directory entry. - * \param[in] i Index of character. - * \return The 16-bit character. - */ -static uint16_t lfnGetChar(ldir_t *ldir, uint8_t i) { - if (i < LDIR_NAME1_DIM) { - return ldir->name1[i]; - } else if (i < (LDIR_NAME1_DIM + LDIR_NAME2_DIM)) { - return ldir->name2[i - LDIR_NAME1_DIM]; - } else if (i < (LDIR_NAME1_DIM + LDIR_NAME2_DIM + LDIR_NAME2_DIM)) { - return ldir->name3[i - LDIR_NAME1_DIM - LDIR_NAME2_DIM]; - } - return 0; +static bool isLower(char c) { + return 'a' <= c && c <= 'z'; } //------------------------------------------------------------------------------ -static bool lfnGetName(ldir_t *ldir, char* name, size_t n) { - uint8_t i; - size_t k = 13*((ldir->ord & 0X1F) - 1); - for (i = 0; i < 13; i++) { - uint16_t c = lfnGetChar(ldir, i); - if (c == 0 || k >= n) { - break; - } - name[k++] = c >= 0X7F ? '?' : c; - } - // Terminate with zero byte if name fits. - if (k < n && (ldir->ord & LDIR_ORD_LAST_LONG_ENTRY)) { - name[k] = 0; - } - // Truncate if name is too long. - name[n - 1] = 0; - return true; +static bool isUpper(char c) { + return 'A' <= c && c <= 'Z'; } //------------------------------------------------------------------------------ -inline bool lfnLegalChar(char c) { - if (c == '/' || c == '\\' || c == '"' || c == '*' || - c == ':' || c == '<' || c == '>' || c == '?' || c == '|') { - return false; - } - return 0X1F < c && c < 0X7F; +// A bit smaller than toupper in AVR 328. +inline char toUpper(char c) { + return isLower(c) ? c - 'a' + 'A' : c; } //------------------------------------------------------------------------------ /** @@ -99,175 +47,162 @@ inline bool lfnLegalChar(char c) { * * \param[in] ldir Pointer to long file name directory entry. * \param[in] i Index of character. - * \param[in] c The 16-bit character. + * \param[in] c The 16-bit character. */ -static void lfnPutChar(ldir_t *ldir, uint8_t i, uint16_t c) { - if (i < LDIR_NAME1_DIM) { - ldir->name1[i] = c; - } else if (i < (LDIR_NAME1_DIM + LDIR_NAME2_DIM)) { - ldir->name2[i - LDIR_NAME1_DIM] = c; - } else if (i < (LDIR_NAME1_DIM + LDIR_NAME2_DIM + LDIR_NAME2_DIM)) { - ldir->name3[i - LDIR_NAME1_DIM - LDIR_NAME2_DIM] = c; +static void putLfnChar(DirLfn_t* ldir, uint8_t i, uint16_t c) { + if (i < 5) { + setLe16(ldir->unicode1 + 2*i, c); + } else if (i < 11) { + setLe16(ldir->unicode2 + 2*i -10, c); + } else if (i < 13) { + setLe16(ldir->unicode3 + 2*i - 22, c); } } //------------------------------------------------------------------------------ -static void lfnPutName(ldir_t *ldir, const char* name, size_t n) { - size_t k = 13*((ldir->ord & 0X1F) - 1); - for (uint8_t i = 0; i < 13; i++, k++) { - uint16_t c = k < n ? name[k] : k == n ? 0 : 0XFFFF; - lfnPutChar(ldir, i, c); +// Daniel Bernstein University of Illinois at Chicago. +// Original had + instead of ^ +__attribute__((unused)) +static uint16_t Bernstein(const char* bgn, const char* end, uint16_t hash) { + while (bgn < end) { + // hash = hash * 33 ^ str[i]; + hash = ((hash << 5) + hash) ^ (*bgn++); } + return hash; } //============================================================================== -bool FatFile::getName(char* name, size_t size) { - FatFile dirFile; - ldir_t* ldir; - if (!isOpen() || size < 13) { - DBG_FAIL_MACRO; - goto fail; - } - if (!isLFN()) { - return getSFN(name); - } - if (!dirFile.openCluster(this)) { - DBG_FAIL_MACRO; - goto fail; - } - for (uint8_t ord = 1; ord <= m_lfnOrd; ord++) { - if (!dirFile.seekSet(32UL*(m_dirIndex - ord))) { - DBG_FAIL_MACRO; - goto fail; - } - ldir = reinterpret_cast(dirFile.readDirCache()); +bool FatFile::cmpName(uint16_t index, FatLfn_t* fname, uint8_t lfnOrd) { + FatFile dir = *this; + DirLfn_t* ldir; + fname->reset(); + for (uint8_t order = 1; order <= lfnOrd; order++) { + ldir = reinterpret_cast(dir.cacheDir(index - order)); if (!ldir) { DBG_FAIL_MACRO; goto fail; } - if (ldir->attr != DIR_ATT_LONG_NAME) { - DBG_FAIL_MACRO; - goto fail; - } - if (ord != (ldir->ord & 0X1F)) { - DBG_FAIL_MACRO; - goto fail; - } - if (!lfnGetName(ldir, name, size)) { - DBG_FAIL_MACRO; - goto fail; - } - if (ldir->ord & LDIR_ORD_LAST_LONG_ENTRY) { - return true; + // These should be checked in caller. + DBG_HALT_IF(ldir->attributes != FAT_ATTRIB_LONG_NAME); + DBG_HALT_IF(order != (ldir->order & 0X1F)); + for (uint8_t i = 0; i < 13; i++) { + uint16_t u = getLfnChar(ldir, i); + if (fname->atEnd()) { + return u == 0; + } +#if USE_UTF8_LONG_NAMES + uint16_t cp = fname->get16(); + // Make sure caller checked for valid UTF-8. + DBG_HALT_IF(cp == 0XFFFF); + if (toUpcase(u) != toUpcase(cp)) { + return false; + } +#else // USE_UTF8_LONG_NAMES + if (u > 0X7F || toUpper(u) != toUpper(fname->getch())) { + return false; + } +#endif // USE_UTF8_LONG_NAMES } } - // Fall into fail. - DBG_FAIL_MACRO; + return true; -fail: - name[0] = 0; + fail: return false; } //------------------------------------------------------------------------------ -bool FatFile::openCluster(FatFile* file) { - if (file->m_dirCluster == 0) { - return openRoot(file->m_vol); +bool FatFile::createLFN(uint16_t index, FatLfn_t* fname, uint8_t lfnOrd) { + FatFile dir = *this; + DirLfn_t* ldir; + uint8_t checksum = lfnChecksum(fname->sfn); + uint8_t fc = 0; + fname->reset(); + + for (uint8_t order = 1; order <= lfnOrd; order++) { + ldir = reinterpret_cast(dir.cacheDir(index - order)); + if (!ldir) { + DBG_FAIL_MACRO; + goto fail; + } + dir.m_vol->cacheDirty(); + ldir->order = order == lfnOrd ? FAT_ORDER_LAST_LONG_ENTRY | order : order; + ldir->attributes = FAT_ATTRIB_LONG_NAME; + ldir->mustBeZero1 = 0; + ldir->checksum = checksum; + setLe16(ldir->mustBeZero2, 0); + for (uint8_t i = 0; i < 13; i++) { + uint16_t cp; + if (fname->atEnd()) { + cp = fc++ ? 0XFFFF : 0; + } else { + cp = fname->get16(); + // Verify caller checked for valid UTF-8. + DBG_HALT_IF(cp == 0XFFFF); + } + putLfnChar(ldir, i, cp); + } } - memset(this, 0, sizeof(FatFile)); - m_attr = FILE_ATTR_SUBDIR; - m_flags = O_READ; - m_vol = file->m_vol; - m_firstCluster = file->m_dirCluster; return true; + + fail: + return false; } //------------------------------------------------------------------------------ -bool FatFile::parsePathName(const char* path, - fname_t* fname, const char** ptr) { - char c; +bool FatFile::makeSFN(FatLfn_t* fname) { bool is83; - uint8_t bit = DIR_NT_LC_BASE; +// char c; + uint8_t c; + uint8_t bit = FAT_CASE_LC_BASE; uint8_t lc = 0; uint8_t uc = 0; uint8_t i = 0; uint8_t in = 7; - int end; - int len = 0; - int si; - int dot; + const char* dot; + const char* end = fname->end; + const char* ptr = fname->begin; - // Skip leading spaces. - while (*path == ' ') { - path++; - } - fname->lfn = path; - - for (len = 0; ; len++) { - c = path[len]; - if (c == 0 || isDirSeparator(c)) { - break; - } - if (!lfnLegalChar(c)) { - return false; - } - } - // Advance to next path component. - for (end = len; path[end] == ' ' || isDirSeparator(path[end]); end++) {} - *ptr = &path[end]; + // Assume not zero length. + DBG_HALT_IF(end == ptr); + // Assume blanks removed from start and end. + DBG_HALT_IF(*ptr == ' ' || *(end - 1) == ' ' || *(end - 1) == '.'); - // Back over spaces and dots. - while (len) { - c = path[len - 1]; - if (c != '.' && c != ' ') { - break; - } - len--; - } - // Max length of LFN is 255. - if (len > 255) { - return false; - } - fname->len = len; // Blank file short name. for (uint8_t k = 0; k < 11; k++) { fname->sfn[k] = ' '; } - // skip leading spaces and dots. - for (si = 0; path[si] == '.' || path[si] == ' '; si++) {} - // Not 8.3 if leading dot or space. - is83 = !si; + // Not 8.3 if starts with dot. + is83 = *ptr == '.' ? false : true; + // Skip leading dots. + for (; *ptr == '.'; ptr++) {} + // Find last dot. + for (dot = end - 1; dot > ptr && *dot != '.'; dot--) {} - // find last dot. - for (dot = len - 1; dot >= 0 && path[dot] != '.'; dot--) {} - for (; si < len; si++) { - c = path[si]; - if (c == ' ' || (c == '.' && dot != si)) { - is83 = false; - continue; - } - if (!legal83Char(c) && si != dot) { - is83 = false; - c = '_'; - } - if (si == dot || i > in) { - if (in == 10) { - // Done - extension longer than three characters. + for (; ptr < end; ptr++) { + c = *ptr; + if (c == '.' && ptr == dot) { + in = 10; // Max index for full 8.3 name. + i = 8; // Place for extension. + bit = FAT_CASE_LC_EXT; // bit for extension. + } else { + if (sfnReservedChar(c)) { is83 = false; - break; + // Skip UTF-8 trailing characters. + if ((c & 0XC0) == 0X80) { + continue; + } + c = '_'; } - if (si != dot) { + if (i > in) { is83 = false; + if (in == 10 || ptr > dot) { + // Done - extension longer than three characters or no extension. + break; + } + // Skip to dot. + ptr = dot - 1; + continue; } - // Break if no dot and base-name is longer than eight characters. - if (si > dot) { - break; - } - si = dot; - in = 10; // Max index for full 8.3 name. - i = 8; // Place for extension. - bit = DIR_NT_LC_EXT; // bit for extension. - } else { - if ('a' <= c && c <= 'z') { + if (isLower(c)) { c += 'A' - 'a'; lc |= bit; - } else if ('A' <= c && c <= 'Z') { + } else if (isUpper(c)) { uc |= bit; } fname->sfn[i++] = c; @@ -277,9 +212,9 @@ bool FatFile::parsePathName(const char* path, } } if (fname->sfn[0] == ' ') { - return false; + DBG_HALT_MACRO; + goto fail; } - if (is83) { fname->flags = lc & uc ? FNAME_FLAG_MIXED_CASE : lc; } else { @@ -288,32 +223,95 @@ bool FatFile::parsePathName(const char* path, fname->sfn[fname->seqPos + 1] = '1'; } return true; + + fail: + return false; } //------------------------------------------------------------------------------ -bool FatFile::open(FatFile* dirFile, fname_t* fname, uint8_t oflag) { +bool FatFile::makeUniqueSfn(FatLfn_t* fname) { + const uint8_t FIRST_HASH_SEQ = 2; // min value is 2 + uint8_t pos = fname->seqPos; + DirFat_t* dir; + uint16_t hex = 0; + + DBG_HALT_IF(!(fname->flags & FNAME_FLAG_LOST_CHARS)); + DBG_HALT_IF(fname->sfn[pos] != '~' && fname->sfn[pos + 1] != '1'); + + for (uint8_t seq = FIRST_HASH_SEQ; seq < 100; seq++) { + DBG_WARN_IF(seq > FIRST_HASH_SEQ); +#ifdef USE_LFN_HASH + hex = Bernstein(fname->begin, fname->end, seq); +#else + hex += millis(); +#endif + if (pos > 3) { + // Make space in name for ~HHHH. + pos = 3; + } + for (uint8_t i = pos + 4 ; i > pos; i--) { + uint8_t h = hex & 0XF; + fname->sfn[i] = h < 10 ? h + '0' : h + 'A' - 10; + hex >>= 4; + } + fname->sfn[pos] = '~'; + rewind(); + while (1) { + dir = readDirCache(true); + if (!dir) { + if (!getError()) { + // At EOF and name not found if no error. + goto done; + } + DBG_FAIL_MACRO; + goto fail; + } + if (dir->name[0] == FAT_NAME_FREE) { + goto done; + } + if (isFatFileOrSubdir(dir) && !memcmp(fname->sfn, dir->name, 11)) { + // Name found - try another. + break; + } + } + } + // fall inti fail - too many tries. + DBG_FAIL_MACRO; + + fail: + return false; + + done: + return true; +} +//------------------------------------------------------------------------------ +bool FatFile::open(FatFile* dirFile, FatLfn_t* fname, oflag_t oflag) { bool fnameFound = false; uint8_t lfnOrd = 0; uint8_t freeNeed; uint8_t freeFound = 0; - uint8_t ord = 0; - uint8_t chksum = 0; + uint8_t order = 0; + uint8_t checksum = 0; + uint8_t ms10; + uint8_t nameOrd; uint16_t freeIndex = 0; uint16_t curIndex; - dir_t* dir; - ldir_t* ldir; - size_t len = fname->len; + uint16_t date; + uint16_t time; + DirFat_t* dir; + DirLfn_t* ldir; + auto vol = dirFile->m_vol; if (!dirFile->isDir() || isOpen()) { DBG_FAIL_MACRO; goto fail; } // Number of directory entries needed. - freeNeed = fname->flags & FNAME_FLAG_NEED_LFN ? 1 + (len + 12)/13 : 1; - + nameOrd = (fname->len + 12)/13; + freeNeed = fname->flags & FNAME_FLAG_NEED_LFN ? 1 + nameOrd : 1; dirFile->rewind(); while (1) { - curIndex = dirFile->m_curPosition/32; - dir = dirFile->readDirCache(true); + curIndex = dirFile->m_curPosition/FS_DIR_SIZE; + dir = dirFile->readDirCache(); if (!dir) { if (dirFile->getError()) { DBG_FAIL_MACRO; @@ -322,14 +320,14 @@ bool FatFile::open(FatFile* dirFile, fname_t* fname, uint8_t oflag) { // At EOF goto create; } - if (dir->name[0] == DIR_NAME_DELETED || dir->name[0] == DIR_NAME_FREE) { + if (dir->name[0] == FAT_NAME_DELETED || dir->name[0] == FAT_NAME_FREE) { if (freeFound == 0) { freeIndex = curIndex; } if (freeFound < freeNeed) { freeFound++; } - if (dir->name[0] == DIR_NAME_FREE) { + if (dir->name[0] == FAT_NAME_FREE) { goto create; } } else { @@ -338,44 +336,30 @@ bool FatFile::open(FatFile* dirFile, fname_t* fname, uint8_t oflag) { } } // skip empty slot or '.' or '..' - if (dir->name[0] == DIR_NAME_DELETED || dir->name[0] == '.') { + if (dir->name[0] == FAT_NAME_DELETED || dir->name[0] == '.') { lfnOrd = 0; - } else if (DIR_IS_LONG_NAME(dir)) { - ldir_t *ldir = reinterpret_cast(dir); + } else if (isFatLongName(dir)) { + ldir = reinterpret_cast(dir); if (!lfnOrd) { - if ((ldir->ord & LDIR_ORD_LAST_LONG_ENTRY) == 0) { + order = ldir->order & 0X1F; + if (order != nameOrd || + (ldir->order & FAT_ORDER_LAST_LONG_ENTRY) == 0) { continue; } - lfnOrd = ord = ldir->ord & 0X1F; - chksum = ldir->chksum; - } else if (ldir->ord != --ord || chksum != ldir->chksum) { + lfnOrd = nameOrd; + checksum = ldir->checksum; + } else if (ldir->order != --order || checksum != ldir->checksum) { lfnOrd = 0; continue; } - size_t k = 13*(ord - 1); - if (k >= len) { - // Not found. - lfnOrd = 0; - continue; - } - for (uint8_t i = 0; i < 13; i++) { - uint16_t u = lfnGetChar(ldir, i); - if (k == len) { - if (u != 0) { - // Not found. - lfnOrd = 0; - } - break; - } - if (u > 255 || lfnToLower(u) != lfnToLower(fname->lfn[k++])) { - // Not found. + if (order == 1) { + if (!dirFile->cmpName(curIndex + 1, fname, lfnOrd)) { lfnOrd = 0; - break; } } - } else if (DIR_IS_FILE_OR_SUBDIR(dir)) { + } else if (isFatFileOrSubdir(dir)) { if (lfnOrd) { - if (1 == ord && lfnChecksum(dir->name) == chksum) { + if (1 == order && lfnChecksum(dir->name) == checksum) { goto found; } DBG_FAIL_MACRO; @@ -392,7 +376,7 @@ bool FatFile::open(FatFile* dirFile, fname_t* fname, uint8_t oflag) { } } -found: + found: // Don't open if create only. if (oflag & O_EXCL) { DBG_FAIL_MACRO; @@ -400,13 +384,13 @@ bool FatFile::open(FatFile* dirFile, fname_t* fname, uint8_t oflag) { } goto open; -create: - // don't create unless O_CREAT and O_WRITE - if (!(oflag & O_CREAT) || !(oflag & O_WRITE)) { - DBG_FAIL_MACRO; + create: + // don't create unless O_CREAT and write mode + if (!(oflag & O_CREAT) || !isWriteMode(oflag)) { + DBG_WARN_MACRO; goto fail; } - // If at EOF start in next cluster. + // Keep found entries or start at current index if no free entries found. if (freeFound == 0) { freeIndex = curIndex; } @@ -429,66 +413,50 @@ bool FatFile::open(FatFile* dirFile, fname_t* fname, uint8_t oflag) { DBG_FAIL_MACRO; goto fail; } - // Done if more than one block per cluster. Max freeNeed is 21. - if (dirFile->m_vol->blocksPerCluster() > 1) { - break; - } - freeFound += 16; + freeFound += vol->dirEntriesPerCluster(); } if (fnameFound) { - if (!dirFile->lfnUniqueSfn(fname)) { + if (!dirFile->makeUniqueSfn(fname)) { goto fail; } } - if (!dirFile->seekSet(32UL*freeIndex)) { - DBG_FAIL_MACRO; - goto fail; - } lfnOrd = freeNeed - 1; - for (uint8_t ord = lfnOrd ; ord ; ord--) { - ldir = reinterpret_cast(dirFile->readDirCache()); - if (!ldir) { - DBG_FAIL_MACRO; - goto fail; - } - dirFile->m_vol->cacheDirty(); - ldir->ord = ord == lfnOrd ? LDIR_ORD_LAST_LONG_ENTRY | ord : ord; - ldir->attr = DIR_ATT_LONG_NAME; - ldir->type = 0; - ldir->chksum = lfnChecksum(fname->sfn); - ldir->mustBeZero = 0; - lfnPutName(ldir, fname->lfn, len); + curIndex = freeIndex + lfnOrd; + if (!dirFile->createLFN(curIndex, fname, lfnOrd)) { + goto fail; } - curIndex = dirFile->m_curPosition/32; - dir = dirFile->readDirCache(); + dir = dirFile->cacheDir(curIndex); if (!dir) { DBG_FAIL_MACRO; goto fail; } // initialize as empty file - memset(dir, 0, sizeof(dir_t)); + memset(dir, 0, sizeof(DirFat_t)); memcpy(dir->name, fname->sfn, 11); // Set base-name and extension lower case bits. - dir->reservedNT = (DIR_NT_LC_BASE | DIR_NT_LC_EXT) & fname->flags; + dir->caseFlags = (FAT_CASE_LC_BASE | FAT_CASE_LC_EXT) & fname->flags; - // set timestamps - if (m_dateTime) { + // Set timestamps. + if (FsDateTime::callback) { // call user date/time function - m_dateTime(&dir->creationDate, &dir->creationTime); + FsDateTime::callback(&date, &time, &ms10); + setLe16(dir->createDate, date); + setLe16(dir->createTime, time); + dir->createTimeMs = ms10; } else { - // use default date/time - dir->creationDate = FAT_DEFAULT_DATE; - dir->creationTime = FAT_DEFAULT_TIME; + setLe16(dir->createDate, FS_DEFAULT_DATE); + setLe16(dir->modifyDate, FS_DEFAULT_DATE); + setLe16(dir->accessDate, FS_DEFAULT_DATE); + if (FS_DEFAULT_TIME) { + setLe16(dir->createTime, FS_DEFAULT_TIME); + setLe16(dir->modifyTime, FS_DEFAULT_TIME); + } } - dir->lastAccessDate = dir->creationDate; - dir->lastWriteDate = dir->creationDate; - dir->lastWriteTime = dir->creationTime; - // Force write of entry to device. - dirFile->m_vol->cacheDirty(); + vol->cacheDirty(); -open: + open: // open entry in cache. if (!openCachedEntry(dirFile, curIndex, oflag, lfnOrd)) { DBG_FAIL_MACRO; @@ -496,71 +464,69 @@ bool FatFile::open(FatFile* dirFile, fname_t* fname, uint8_t oflag) { } return true; -fail: + fail: return false; } //------------------------------------------------------------------------------ -size_t FatFile::printName(print_t* pr) { - FatFile dirFile; - uint16_t u; - size_t n = 0; - ldir_t* ldir; - - if (!isLFN()) { - return printSFN(pr); - } - if (!dirFile.openCluster(this)) { - DBG_FAIL_MACRO; - goto fail; +bool FatFile::parsePathName(const char* path, + FatLfn_t* fname, const char** ptr) { + size_t len = 0; + // Skip leading spaces. + while (*path == ' ') { + path++; } - for (uint8_t ord = 1; ord <= m_lfnOrd; ord++) { - if (!dirFile.seekSet(32UL*(m_dirIndex - ord))) { + fname->begin = path; + fname->len = 0; + while (*path && !isDirSeparator(*path)) { +#if USE_UTF8_LONG_NAMES + uint32_t cp; + // Allow end = path + 4 since path is zero terminated. + path = FsUtf::mbToCp(path, path + 4, &cp); + if (!path) { DBG_FAIL_MACRO; goto fail; } - ldir = reinterpret_cast(dirFile.readDirCache()); - if (!ldir) { + len += cp <= 0XFFFF ? 1 : 2; + if (cp < 0X80 && lfnReservedChar(cp)) { DBG_FAIL_MACRO; goto fail; } - if (ldir->attr != DIR_ATT_LONG_NAME || - ord != (ldir->ord & 0X1F)) { +#else // USE_UTF8_LONG_NAMES + uint8_t cp = *path++; + if (cp >= 0X80 || lfnReservedChar(cp)) { DBG_FAIL_MACRO; goto fail; } - for (uint8_t i = 0; i < 13; i++) { - u = lfnGetChar(ldir, i); - if (u == 0) { - // End of name. - break; - } - if (u > 0X7E) { - u = '?'; - } - pr->write(static_cast(u)); - n++; - } - if (ldir->ord & LDIR_ORD_LAST_LONG_ENTRY) { - return n; + len++; +#endif // USE_UTF8_LONG_NAMES + if (cp != '.' && cp != ' ') { + // Need to trim trailing dots spaces. + fname->len = len; + fname->end = path; } } - // Fall into fail; - DBG_FAIL_MACRO; + if (!fname->len || fname->len > FAT_MAX_LFN_LENGTH) { + DBG_FAIL_MACRO; + goto fail; + } + // Advance to next path component. + for (; *path == ' ' || isDirSeparator(*path); path++) {} + *ptr = path; + return makeSFN(fname); -fail: - return 0; + fail: + return false; } //------------------------------------------------------------------------------ bool FatFile::remove() { bool last; - uint8_t chksum; - uint8_t ord; + uint8_t checksum; FatFile dirFile; - dir_t* dir; - ldir_t* ldir; + DirFat_t* dir; + DirLfn_t* ldir; // Cant' remove not open for write. - if (!isFile() || !(m_flags & O_WRITE)) { + if (!isWritable()) { DBG_FAIL_MACRO; goto fail; } @@ -570,18 +536,19 @@ bool FatFile::remove() { goto fail; } // Cache directory entry. - dir = cacheDirEntry(FatCache::CACHE_FOR_WRITE); + dir = cacheDirEntry(FsCache::CACHE_FOR_WRITE); if (!dir) { DBG_FAIL_MACRO; goto fail; } - chksum = lfnChecksum(dir->name); + checksum = lfnChecksum(dir->name); // Mark entry deleted. - dir->name[0] = DIR_NAME_DELETED; + dir->name[0] = FAT_NAME_DELETED; // Set this file closed. - m_attr = FILE_ATTR_CLOSED; + m_attributes = FILE_ATTR_CLOSED; + m_flags = 0; // Write entry to device. if (!m_vol->cacheSync()) { @@ -596,24 +563,20 @@ bool FatFile::remove() { DBG_FAIL_MACRO; goto fail; } - for (ord = 1; ord <= m_lfnOrd; ord++) { - if (!dirFile.seekSet(32UL*(m_dirIndex - ord))) { - DBG_FAIL_MACRO; - goto fail; - } - ldir = reinterpret_cast(dirFile.readDirCache()); + for (uint8_t order = 1; order <= m_lfnOrd; order++) { + ldir = reinterpret_cast(dirFile.cacheDir(m_dirIndex - order)); if (!ldir) { DBG_FAIL_MACRO; goto fail; } - if (ldir->attr != DIR_ATT_LONG_NAME || - ord != (ldir->ord & 0X1F) || - chksum != ldir->chksum) { + if (ldir->attributes != FAT_ATTRIB_LONG_NAME || + order != (ldir->order & 0X1F) || + checksum != ldir->checksum) { DBG_FAIL_MACRO; goto fail; } - last = ldir->ord & LDIR_ORD_LAST_LONG_ENTRY; - ldir->ord = DIR_NAME_DELETED; + last = ldir->order & FAT_ORDER_LAST_LONG_ENTRY; + ldir->order = FAT_NAME_DELETED; m_vol->cacheDirty(); if (last) { if (!m_vol->cacheSync()) { @@ -626,63 +589,7 @@ bool FatFile::remove() { // Fall into fail. DBG_FAIL_MACRO; -fail: - return false; -} -//------------------------------------------------------------------------------ -bool FatFile::lfnUniqueSfn(fname_t* fname) { - const uint8_t FIRST_HASH_SEQ = 2; // min value is 2 - uint8_t pos = fname->seqPos;; - dir_t *dir; - uint16_t hex; - - DBG_HALT_IF(!(fname->flags & FNAME_FLAG_LOST_CHARS)); - DBG_HALT_IF(fname->sfn[pos] != '~' && fname->sfn[pos + 1] != '1'); - - for (uint8_t seq = 2; seq < 100; seq++) { - if (seq < FIRST_HASH_SEQ) { - fname->sfn[pos + 1] = '0' + seq; - } else { - DBG_PRINT_IF(seq > FIRST_HASH_SEQ); - hex = Bernstein(seq + fname->len, fname->lfn, fname->len); - if (pos > 3) { - // Make space in name for ~HHHH. - pos = 3; - } - for (uint8_t i = pos + 4 ; i > pos; i--) { - uint8_t h = hex & 0XF; - fname->sfn[i] = h < 10 ? h + '0' : h + 'A' - 10; - hex >>= 4; - } - } - fname->sfn[pos] = '~'; - rewind(); - while (1) { - dir = readDirCache(true); - if (!dir) { - if (!getError()) { - // At EOF and name not found if no error. - goto done; - } - DBG_FAIL_MACRO; - goto fail; - } - if (dir->name[0] == DIR_NAME_FREE) { - goto done; - } - if (DIR_IS_FILE_OR_SUBDIR(dir) && !memcmp(fname->sfn, dir->name, 11)) { - // Name found - try another. - break; - } - } - } - // fall inti fail - too many tries. - DBG_FAIL_MACRO; - -fail: + fail: return false; - -done: - return true; } #endif // #if USE_LONG_FILE_NAMES diff --git a/avr/cores/megacommand/SdFat/FatLib/FatFilePrint.cpp b/avr/cores/megacommand/SdFat/FatLib/FatFilePrint.cpp index 7ac2c7117..99cb17978 100644 --- a/avr/cores/megacommand/SdFat/FatLib/FatFilePrint.cpp +++ b/avr/cores/megacommand/SdFat/FatLib/FatFilePrint.cpp @@ -1,5 +1,5 @@ /** - * Copyright (c) 20011-2017 Bill Greiman + * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -23,83 +23,19 @@ * DEALINGS IN THE SOFTWARE. */ #include -#include "FatFile.h" -#include "FmtNumber.h" -//------------------------------------------------------------------------------ -// print uint8_t with width 2 -static void print2u(print_t* pr, uint8_t v) { - char c0 = '?'; - char c1 = '?'; - if (v < 100) { - c1 = v/10; - c0 = v - 10*c1 + '0'; - c1 += '0'; - } - pr->write(c1); - pr->write(c0); -} -//------------------------------------------------------------------------------ -static void printU32(print_t* pr, uint32_t v) { - char buf[11]; - char* ptr = buf + sizeof(buf); - *--ptr = 0; - pr->write(fmtDec(v, ptr)); -} -//------------------------------------------------------------------------------ -static void printHex(print_t* pr, uint8_t w, uint16_t h) { - char buf[5]; - char* ptr = buf + sizeof(buf); - *--ptr = 0; - for (uint8_t i = 0; i < w; i++) { - char c = h & 0XF; - *--ptr = c < 10 ? c + '0' : c + 'A' - 10; - h >>= 4; - } - pr->write(ptr); -} -//------------------------------------------------------------------------------ -void FatFile::dmpFile(print_t* pr, uint32_t pos, size_t n) { - char text[17]; - text[16] = 0; - if (n >= 0XFFF0) { - n = 0XFFF0; - } - if (!seekSet(pos)) { - return; - } - for (size_t i = 0; i <= n; i++) { - if ((i & 15) == 0) { - if (i) { - pr->write(' '); - pr->write(text); - if (i == n) { - break; - } - } - pr->write('\r'); - pr->write('\n'); - if (i >= n) { - break; - } - printHex(pr, 4, i); - pr->write(' '); - } - int16_t h = read(); - if (h < 0) { - break; - } - pr->write(' '); - printHex(pr, 2, h); - text[i&15] = ' ' <= h && h < 0X7F ? h : '.'; - } - pr->write('\r'); - pr->write('\n'); -} +#define DBG_FILE "FatFilePrint.cpp" +#include "../common/DebugMacros.h" +#include "FatLib.h" + //------------------------------------------------------------------------------ -void FatFile::ls(print_t* pr, uint8_t flags, uint8_t indent) { +bool FatFile::ls(print_t* pr, uint8_t flags, uint8_t indent) { FatFile file; + if (!isDir()) { + DBG_FAIL_MACRO; + goto fail; + } rewind(); - while (file.openNext(this, O_READ)) { + while (file.openNext(this, O_RDONLY)) { // indent for dir level if (!file.isHidden() || (flags & LS_A)) { for (uint8_t i = 0; i < indent; i++) { @@ -125,126 +61,47 @@ void FatFile::ls(print_t* pr, uint8_t flags, uint8_t indent) { } file.close(); } -} -//------------------------------------------------------------------------------ -bool FatFile::printCreateDateTime(print_t* pr) { - dir_t dir; - if (!dirEntry(&dir)) { + if (getError()) { DBG_FAIL_MACRO; goto fail; } - printFatDate(pr, dir.creationDate); - pr->write(' '); - printFatTime(pr, dir.creationTime); return true; -fail: + fail: return false; } //------------------------------------------------------------------------------ -void FatFile::printFatDate(print_t* pr, uint16_t fatDate) { - printU32(pr, FAT_YEAR(fatDate)); - pr->write('-'); - print2u(pr, FAT_MONTH(fatDate)); - pr->write('-'); - print2u(pr, FAT_DAY(fatDate)); -} -//------------------------------------------------------------------------------ -void FatFile::printFatTime(print_t* pr, uint16_t fatTime) { - print2u(pr, FAT_HOUR(fatTime)); - pr->write(':'); - print2u(pr, FAT_MINUTE(fatTime)); - pr->write(':'); - print2u(pr, FAT_SECOND(fatTime)); -} -//------------------------------------------------------------------------------ -/** Template for FatFile::printField() */ -template -static int printFieldT(FatFile* file, char sign, Type value, char term) { - char buf[3*sizeof(Type) + 3]; - char* str = &buf[sizeof(buf)]; - - if (term) { - *--str = term; - if (term == '\n') { - *--str = '\r'; - } - } -#ifdef OLD_FMT - do { - Type m = value; - value /= 10; - *--str = '0' + m - 10*value; - } while (value); -#else // OLD_FMT - str = fmtDec(value, str); -#endif // OLD_FMT - if (sign) { - *--str = sign; - } - return file->write(str, &buf[sizeof(buf)] - str); -} -//------------------------------------------------------------------------------ - -int FatFile::printField(float value, char term, uint8_t prec) { - char buf[24]; - char* str = &buf[sizeof(buf)]; - if (term) { - *--str = term; - if (term == '\n') { - *--str = '\r'; - } - } - str = fmtFloat(value, str, prec); - return write(str, buf + sizeof(buf) - str); -} -//------------------------------------------------------------------------------ -int FatFile::printField(uint16_t value, char term) { - return printFieldT(this, 0, value, term); -} -//------------------------------------------------------------------------------ -int FatFile::printField(int16_t value, char term) { - char sign = 0; - if (value < 0) { - sign = '-'; - value = -value; +size_t FatFile::printAccessDate(print_t* pr) { + uint16_t date; + if (getAccessDate(&date)) { + return fsPrintDate(pr, date); } - return printFieldT(this, sign, (uint16_t)value, term); + return 0; } //------------------------------------------------------------------------------ -int FatFile::printField(uint32_t value, char term) { - return printFieldT(this, 0, value, term); -} -//------------------------------------------------------------------------------ -int FatFile::printField(int32_t value, char term) { - char sign = 0; - if (value < 0) { - sign = '-'; - value = -value; +size_t FatFile::printCreateDateTime(print_t* pr) { + uint16_t date; + uint16_t time; + if (getCreateDateTime(&date, &time)) { + return fsPrintDateTime(pr, date, time); } - return printFieldT(this, sign, (uint32_t)value, term); + return 0; } //------------------------------------------------------------------------------ -bool FatFile::printModifyDateTime(print_t* pr) { - dir_t dir; - if (!dirEntry(&dir)) { - DBG_FAIL_MACRO; - goto fail; +size_t FatFile::printModifyDateTime(print_t* pr) { + uint16_t date; + uint16_t time; + if (getModifyDateTime(&date, &time)) { + return fsPrintDateTime(pr, date, time); } - printFatDate(pr, dir.lastWriteDate); - pr->write(' '); - printFatTime(pr, dir.lastWriteTime); - return true; - -fail: - return false; + return 0; } //------------------------------------------------------------------------------ size_t FatFile::printFileSize(print_t* pr) { char buf[11]; char *ptr = buf + sizeof(buf); *--ptr = 0; - ptr = fmtDec(fileSize(), ptr); + ptr = fmtBase10(ptr, fileSize()); while (ptr > buf) { *--ptr = ' '; } diff --git a/avr/cores/megacommand/SdFat/FatLib/FatFileSFN.cpp b/avr/cores/megacommand/SdFat/FatLib/FatFileSFN.cpp index 0ebcfe446..74798a9ef 100644 --- a/avr/cores/megacommand/SdFat/FatLib/FatFileSFN.cpp +++ b/avr/cores/megacommand/SdFat/FatLib/FatFileSFN.cpp @@ -1,5 +1,5 @@ /** - * Copyright (c) 20011-2017 Bill Greiman + * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -22,123 +22,28 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ -#include "FatFile.h" -#include "FatFileSystem.h" -//------------------------------------------------------------------------------ -bool FatFile::getSFN(char* name) { - dir_t* dir; - if (!isOpen()) { - DBG_FAIL_MACRO; - goto fail; - } - if (isRoot()) { - name[0] = '/'; - name[1] = '\0'; - return true; - } - // cache entry - dir = cacheDirEntry(FatCache::CACHE_FOR_READ); - if (!dir) { - DBG_FAIL_MACRO; - goto fail; - } - // format name - dirName(dir, name); - return true; - -fail: - return false; -} -//------------------------------------------------------------------------------ -size_t FatFile::printSFN(print_t* pr) { - char name[13]; - if (!getSFN(name)) { - DBG_FAIL_MACRO; - goto fail; - } - return pr->write(name); - -fail: - return 0; -} -#if !USE_LONG_FILE_NAMES -//------------------------------------------------------------------------------ -bool FatFile::getName(char* name, size_t size) { - return size < 13 ? 0 : getSFN(name); -} -//------------------------------------------------------------------------------ -// format directory name field from a 8.3 name string -bool FatFile::parsePathName(const char* path, fname_t* fname, - const char** ptr) { - uint8_t uc = 0; - uint8_t lc = 0; - uint8_t bit = FNAME_FLAG_LC_BASE; - // blank fill name and extension - for (uint8_t i = 0; i < 11; i++) { - fname->sfn[i] = ' '; - } - - for (uint8_t i = 0, n = 7;; path++) { - uint8_t c = *path; - if (c == 0 || isDirSeparator(c)) { - // Done. - break; - } - if (c == '.' && n == 7) { - n = 10; // max index for full 8.3 name - i = 8; // place for extension - - // bit for extension. - bit = FNAME_FLAG_LC_EXT; - } else { - if (!legal83Char(c) || i > n) { - DBG_FAIL_MACRO; - goto fail; - } - if ('a' <= c && c <= 'z') { - c += 'A' - 'a'; - lc |= bit; - } else if ('A' <= c && c <= 'Z') { - uc |= bit; - } - fname->sfn[i++] = c; - } - } - // must have a file name, extension is optional - if (fname->sfn[0] == ' ') { - DBG_FAIL_MACRO; - goto fail; - } - // Set base-name and extension bits. - fname->flags = lc & uc ? 0 : lc; - while (isDirSeparator(*path)) { - path++; - } - *ptr = path; - return true; - -fail: - return false; -} +#define DBG_FILE "FatFileSFN.cpp" +#include "../common/DebugMacros.h" +#include "FatLib.h" //------------------------------------------------------------------------------ // open with filename in fname #define SFN_OPEN_USES_CHKSUM 0 -bool FatFile::open(FatFile* dirFile, fname_t* fname, uint8_t oflag) { +bool FatFile::open(FatFile* dirFile, FatSfn_t* fname, oflag_t oflag) { + uint16_t date; + uint16_t time; + uint8_t ms10; bool emptyFound = false; #if SFN_OPEN_USES_CHKSUM - uint8_t chksum; -#endif + uint8_t checksum; +#endif // SFN_OPEN_USES_CHKSUM uint8_t lfnOrd = 0; - uint16_t emptyIndex; + uint16_t emptyIndex = 0; uint16_t index = 0; - dir_t* dir; - ldir_t* ldir; + DirFat_t* dir; + DirLfn_t* ldir; dirFile->rewind(); - while (1) { - if (!emptyFound) { - emptyIndex = index; - } + while (true) { dir = dirFile->readDirCache(true); if (!dir) { if (dirFile->getError()) { @@ -148,14 +53,16 @@ bool FatFile::open(FatFile* dirFile, fname_t* fname, uint8_t oflag) { // At EOF if no error. break; } - if (dir->name[0] == DIR_NAME_FREE) { - emptyFound = true; - break; - } - if (dir->name[0] == DIR_NAME_DELETED) { + if (dir->name[0] == FAT_NAME_DELETED || dir->name[0] == FAT_NAME_FREE) { + if (!emptyFound) { + emptyIndex = index; + emptyFound = true; + } + if (dir->name[0] == FAT_NAME_FREE) { + break; + } lfnOrd = 0; - emptyFound = true; - } else if (DIR_IS_FILE_OR_SUBDIR(dir)) { + } else if (isFatFileOrSubdir(dir)) { if (!memcmp(fname->sfn, dir->name, 11)) { // don't open existing file if O_EXCL if (oflag & O_EXCL) { @@ -163,7 +70,7 @@ bool FatFile::open(FatFile* dirFile, fname_t* fname, uint8_t oflag) { goto fail; } #if SFN_OPEN_USES_CHKSUM - if (lfnOrd && chksum != lfnChecksum(dir->name)) { + if (lfnOrd && checksum != lfnChecksum(dir->name)) { DBG_FAIL_MACRO; goto fail; } @@ -176,12 +83,12 @@ bool FatFile::open(FatFile* dirFile, fname_t* fname, uint8_t oflag) { } else { lfnOrd = 0; } - } else if (DIR_IS_LONG_NAME(dir)) { - ldir = reinterpret_cast(dir); - if (ldir->ord & LDIR_ORD_LAST_LONG_ENTRY) { - lfnOrd = ldir->ord & 0X1F; + } else if (isFatLongName(dir)) { + ldir = reinterpret_cast(dir); + if (ldir->order & FAT_ORDER_LAST_LONG_ENTRY) { + lfnOrd = ldir->order & 0X1F; #if SFN_OPEN_USES_CHKSUM - chksum = ldir->chksum; + checksum = ldir->checksum; #endif // SFN_OPEN_USES_CHKSUM } } else { @@ -189,8 +96,8 @@ bool FatFile::open(FatFile* dirFile, fname_t* fname, uint8_t oflag) { } index++; } - // don't create unless O_CREAT and O_WRITE - if (!(oflag & O_CREAT) || !(oflag & O_WRITE)) { + // don't create unless O_CREAT and write mode + if (!(oflag & O_CREAT) || !isWriteMode(oflag)) { DBG_FAIL_MACRO; goto fail; } @@ -202,53 +109,182 @@ bool FatFile::open(FatFile* dirFile, fname_t* fname, uint8_t oflag) { goto fail; } } - if (!dirFile->seekSet(32UL*index)) { - DBG_FAIL_MACRO; - goto fail; - } - dir = dirFile->readDirCache(); + dir = reinterpret_cast(dirFile->cacheDir(index)); if (!dir) { DBG_FAIL_MACRO; goto fail; } // initialize as empty file - memset(dir, 0, sizeof(dir_t)); + memset(dir, 0, sizeof(DirFat_t)); memcpy(dir->name, fname->sfn, 11); // Set base-name and extension lower case bits. - dir->reservedNT = (DIR_NT_LC_BASE | DIR_NT_LC_EXT) & fname->flags; + dir->caseFlags = (FAT_CASE_LC_BASE | FAT_CASE_LC_EXT) & fname->flags; - // set timestamps - if (m_dateTime) { + // Set timestamps. + if (FsDateTime::callback) { // call user date/time function - m_dateTime(&dir->creationDate, &dir->creationTime); + FsDateTime::callback(&date, &time, &ms10); + setLe16(dir->createDate, date); + setLe16(dir->createTime, time); + dir->createTimeMs = ms10; } else { - // use default date/time - dir->creationDate = FAT_DEFAULT_DATE; - dir->creationTime = FAT_DEFAULT_TIME; + setLe16(dir->createDate, FS_DEFAULT_DATE); + setLe16(dir->modifyDate, FS_DEFAULT_DATE); + setLe16(dir->accessDate, FS_DEFAULT_DATE); + if (FS_DEFAULT_TIME) { + setLe16(dir->createTime, FS_DEFAULT_TIME); + setLe16(dir->modifyTime, FS_DEFAULT_TIME); + } } - dir->lastAccessDate = dir->creationDate; - dir->lastWriteDate = dir->creationDate; - dir->lastWriteTime = dir->creationTime; - // Force write of entry to device. dirFile->m_vol->cacheDirty(); // open entry in cache. return openCachedEntry(dirFile, index, oflag, 0); -fail: + fail: return false; } //------------------------------------------------------------------------------ -size_t FatFile::printName(print_t* pr) { - return printSFN(pr); +bool FatFile::openExistingSFN(const char* path) { + FatSfn_t fname; + auto vol = FatVolume::cwv(); + while (*path == '/') { + path++; + } + if (*path == 0) { + return openRoot(vol); + } + *this = *vol->vwd(); + do { + if (!parsePathName(path, &fname, &path)) { + DBG_FAIL_MACRO; + goto fail; + } + if (!openSFN(&fname)) { + DBG_FAIL_MACRO; + goto fail; + } + } while (*path); + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +bool FatFile::openSFN(FatSfn_t* fname) { + DirFat_t dir; + DirLfn_t* ldir; + auto vol = m_vol; + uint8_t lfnOrd = 0; + if (!isDir()) { + DBG_FAIL_MACRO; + goto fail; + } + while (true) { + if (read(&dir, 32) != 32) { + DBG_FAIL_MACRO; + goto fail; + } + if (dir.name[0] == 0) { + DBG_FAIL_MACRO; + goto fail; + } + if (isFatFileOrSubdir(&dir) && memcmp(fname->sfn, dir.name, 11) == 0) { + uint16_t dirIndex = (m_curPosition - 32) >> 5; + uint32_t dirCluster = m_firstCluster; + memset(this, 0 , sizeof(FatFile)); + m_attributes = dir.attributes & FS_ATTRIB_COPY; + m_flags = FILE_FLAG_READ; + if (isFatFile(&dir)) { + m_attributes |= FILE_ATTR_FILE; + if (!isReadOnly()) { + m_attributes |= FS_ATTRIB_ARCHIVE; + m_flags |= FILE_FLAG_WRITE; + } + } + m_lfnOrd = lfnOrd; + m_firstCluster = (uint32_t)getLe16(dir.firstClusterHigh) << 16; + m_firstCluster |= getLe16(dir.firstClusterLow); + m_fileSize = getLe32(dir.fileSize); + m_vol = vol; + m_dirCluster = dirCluster; + m_dirSector = m_vol->cacheSectorNumber(); + m_dirIndex = dirIndex; + return true; + } else if (isFatLongName(&dir)) { + ldir = reinterpret_cast(&dir); + if (ldir->order & FAT_ORDER_LAST_LONG_ENTRY) { + lfnOrd = ldir->order & 0X1F; + } + } else { + lfnOrd = 0; + } + } + + fail: + return false; +} +//------------------------------------------------------------------------------ +// format directory name field from a 8.3 name string +bool FatFile::parsePathName(const char* path, FatSfn_t* fname, + const char** ptr) { + uint8_t uc = 0; + uint8_t lc = 0; + uint8_t bit = FNAME_FLAG_LC_BASE; + // blank fill name and extension + for (uint8_t i = 0; i < 11; i++) { + fname->sfn[i] = ' '; + } + for (uint8_t i = 0, n = 7;; path++) { + uint8_t c = *path; + if (c == 0 || isDirSeparator(c)) { + // Done. + break; + } + if (c == '.' && n == 7) { + n = 10; // max index for full 8.3 name + i = 8; // place for extension + + // bit for extension. + bit = FNAME_FLAG_LC_EXT; + } else { + if (sfnReservedChar(c) || i > n) { + DBG_FAIL_MACRO; + goto fail; + } + if ('a' <= c && c <= 'z') { + c += 'A' - 'a'; + lc |= bit; + } else if ('A' <= c && c <= 'Z') { + uc |= bit; + } + fname->sfn[i++] = c; + } + } + // must have a file name, extension is optional + if (fname->sfn[0] == ' ') { + DBG_FAIL_MACRO; + goto fail; + } + // Set base-name and extension bits. + fname->flags = lc & uc ? 0 : lc; + while (isDirSeparator(*path)) { + path++; + } + *ptr = path; + return true; + + fail: + return false; } +#if !USE_LONG_FILE_NAMES //------------------------------------------------------------------------------ bool FatFile::remove() { - dir_t* dir; + DirFat_t* dir; // Can't remove if LFN or not open for write. - if (!isFile() || isLFN() || !(m_flags & O_WRITE)) { + if (!isWritable() || isLFN()) { DBG_FAIL_MACRO; goto fail; } @@ -258,21 +294,22 @@ bool FatFile::remove() { goto fail; } // Cache directory entry. - dir = cacheDirEntry(FatCache::CACHE_FOR_WRITE); + dir = cacheDirEntry(FsCache::CACHE_FOR_WRITE); if (!dir) { DBG_FAIL_MACRO; goto fail; } // Mark entry deleted. - dir->name[0] = DIR_NAME_DELETED; + dir->name[0] = FAT_NAME_DELETED; // Set this file closed. - m_attr = FILE_ATTR_CLOSED; + m_attributes = FILE_ATTR_CLOSED; + m_flags = 0; // Write entry to device. return m_vol->cacheSync(); -fail: + fail: return false; } #endif // !USE_LONG_FILE_NAMES diff --git a/avr/cores/megacommand/SdFat/FatLib/FatFileSystem.h b/avr/cores/megacommand/SdFat/FatLib/FatFileSystem.h deleted file mode 100644 index 0f66d3551..000000000 --- a/avr/cores/megacommand/SdFat/FatLib/FatFileSystem.h +++ /dev/null @@ -1,326 +0,0 @@ -/** - * Copyright (c) 20011-2017 Bill Greiman - * This file is part of the SdFat library for SD memory cards. - * - * MIT License - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ -#ifndef FatFileSystem_h -#define FatFileSystem_h -#include "FatVolume.h" -#include "FatFile.h" -#include "ArduinoStream.h" -#include "ArduinoFiles.h" -/** - * \file - * \brief FatFileSystem class - */ -//------------------------------------------------------------------------------ -/** - * \class FatFileSystem - * \brief Integration class for the FatLib library. - */ -class FatFileSystem : public FatVolume { - public: - /** - * Initialize an FatFileSystem object. - * \param[in] blockDev Device block driver. - * \param[in] part partition to initialize. - * \return The value true is returned for success and - * the value false is returned for failure. - */ - bool begin(BlockDriver* blockDev, uint8_t part = 0) { - m_blockDev = blockDev; - vwd()->close(); - return (part ? init(part) : init(1) || init(0)) - && vwd()->openRoot(this) && FatFile::setCwd(vwd()); - } -#if ENABLE_ARDUINO_FEATURES - /** List the directory contents of the volume working directory to Serial. - * - * \param[in] flags The inclusive OR of - * - * LS_DATE - %Print file modification date - * - * LS_SIZE - %Print file size. - * - * LS_R - Recursive list of subdirectories. - */ - void ls(uint8_t flags = 0) { - ls(&Serial, flags); - } - /** List the directory contents of a directory to Serial. - * - * \param[in] path directory to list. - * - * \param[in] flags The inclusive OR of - * - * LS_DATE - %Print file modification date - * - * LS_SIZE - %Print file size. - * - * LS_R - Recursive list of subdirectories. - */ - void ls(const char* path, uint8_t flags = 0) { - ls(&Serial, path, flags); - } - /** open a file - * - * \param[in] path location of file to be opened. - * \param[in] mode open mode flags. - * \return a File object. - */ - File open(const char *path, uint8_t mode = FILE_READ) { - File tmpFile; - tmpFile.open(vwd(), path, mode); - return tmpFile; - } - /** open a file - * - * \param[in] path location of file to be opened. - * \param[in] mode open mode flags. - * \return a File object. - */ - File open(const String &path, uint8_t mode = FILE_READ) { - return open(path.c_str(), mode ); - } -#endif // ENABLE_ARDUINO_FEATURES - /** Change a volume's working directory to root - * - * Changes the volume's working directory to the SD's root directory. - * Optionally set the current working directory to the volume's - * working directory. - * - * \param[in] set_cwd Set the current working directory to this volume's - * working directory if true. - * - * \return The value true is returned for success and - * the value false is returned for failure. - */ - bool chdir(bool set_cwd = false) { - vwd()->close(); - return vwd()->openRoot(this) && (set_cwd ? FatFile::setCwd(vwd()) : true); - } - /** Change a volume's working directory - * - * Changes the volume working directory to the \a path subdirectory. - * Optionally set the current working directory to the volume's - * working directory. - * - * Example: If the volume's working directory is "/DIR", chdir("SUB") - * will change the volume's working directory from "/DIR" to "/DIR/SUB". - * - * If path is "/", the volume's working directory will be changed to the - * root directory - * - * \param[in] path The name of the subdirectory. - * - * \param[in] set_cwd Set the current working directory to this volume's - * working directory if true. - * - * \return The value true is returned for success and - * the value false is returned for failure. - */ - //---------------------------------------------------------------------------- - bool chdir(const char *path, bool set_cwd = false) { - FatFile dir; - if (path[0] == '/' && path[1] == '\0') { - return chdir(set_cwd); - } - if (!dir.open(vwd(), path, O_READ)) { - goto fail; - } - if (!dir.isDir()) { - goto fail; - } - m_vwd = dir; - if (set_cwd) { - FatFile::setCwd(vwd()); - } - return true; - -fail: - return false; - } - //---------------------------------------------------------------------------- - /** Set the current working directory to a volume's working directory. - * - * This is useful with multiple SD cards. - * - * The current working directory is changed to this - * volume's working directory. - * - * This is like the Windows/DOS \: command. - */ - void chvol() { - FatFile::setCwd(vwd()); - } - //---------------------------------------------------------------------------- - /** - * Test for the existence of a file. - * - * \param[in] path Path of the file to be tested for. - * - * \return true if the file exists else false. - */ - bool exists(const char* path) { - return vwd()->exists(path); - } - //---------------------------------------------------------------------------- - /** List the directory contents of the volume working directory. - * - * \param[in] pr Print stream for list. - * - * \param[in] flags The inclusive OR of - * - * LS_DATE - %Print file modification date - * - * LS_SIZE - %Print file size. - * - * LS_R - Recursive list of subdirectories. - */ - void ls(print_t* pr, uint8_t flags = 0) { - vwd()->ls(pr, flags); - } - //---------------------------------------------------------------------------- - /** List the directory contents of a directory. - * - * \param[in] pr Print stream for list. - * - * \param[in] path directory to list. - * - * \param[in] flags The inclusive OR of - * - * LS_DATE - %Print file modification date - * - * LS_SIZE - %Print file size. - * - * LS_R - Recursive list of subdirectories. - */ - void ls(print_t* pr, const char* path, uint8_t flags) { - FatFile dir; - dir.open(vwd(), path, O_READ); - dir.ls(pr, flags); - } - //---------------------------------------------------------------------------- - /** Make a subdirectory in the volume working directory. - * - * \param[in] path A path with a valid 8.3 DOS name for the subdirectory. - * - * \param[in] pFlag Create missing parent directories if true. - * - * \return The value true is returned for success and - * the value false is returned for failure. - */ - bool mkdir(const char* path, bool pFlag = true) { - FatFile sub; - return sub.mkdir(vwd(), path, pFlag); - } - //---------------------------------------------------------------------------- - /** Remove a file from the volume working directory. - * - * \param[in] path A path with a valid 8.3 DOS name for the file. - * - * \return The value true is returned for success and - * the value false is returned for failure. - */ - bool remove(const char* path) { - return FatFile::remove(vwd(), path); - } - //---------------------------------------------------------------------------- - /** Rename a file or subdirectory. - * - * \param[in] oldPath Path name to the file or subdirectory to be renamed. - * - * \param[in] newPath New path name of the file or subdirectory. - * - * The \a newPath object must not exist before the rename call. - * - * The file to be renamed must not be open. The directory entry may be - * moved and file system corruption could occur if the file is accessed by - * a file object that was opened before the rename() call. - * - * \return The value true is returned for success and - * the value false is returned for failure. - */ - bool rename(const char *oldPath, const char *newPath) { - FatFile file; - if (!file.open(vwd(), oldPath, O_READ)) { - return false; - } - return file.rename(vwd(), newPath); - } - //---------------------------------------------------------------------------- - /** Remove a subdirectory from the volume's working directory. - * - * \param[in] path A path with a valid 8.3 DOS name for the subdirectory. - * - * The subdirectory file will be removed only if it is empty. - * - * \return The value true is returned for success and - * the value false is returned for failure. - */ - bool rmdir(const char* path) { - FatFile sub; - if (!sub.open(vwd(), path, O_READ)) { - return false; - } - return sub.rmdir(); - } - //---------------------------------------------------------------------------- - /** 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] path A path with a valid 8.3 DOS name for the file. - * \param[in] length The desired length for the file. - * - * \return The value true is returned for success and - * the value false is returned for failure. - */ - bool truncate(const char* path, uint32_t length) { - FatFile file; - if (!file.open(vwd(), path, O_WRITE)) { - return false; - } - return file.truncate(length); - } - /** \return a pointer to the FatVolume object. */ - FatVolume* vol() { - return this; - } - /** \return a pointer to the volume working directory. */ - FatFile* vwd() { - return &m_vwd; - } - /** Wipe all data from the volume. You must reinitialize the volume before - * accessing it again. - * \param[in] pr print stream for status dots. - * \return true for success else false. - */ - bool wipe(print_t* pr = 0) { - vwd()->close(); - return FatVolume::wipe(pr); - } - - private: - FatFile m_vwd; -}; -#endif // FatFileSystem_h diff --git a/avr/cores/megacommand/SdFat/FatLib/FatFormatter.cpp b/avr/cores/megacommand/SdFat/FatLib/FatFormatter.cpp new file mode 100644 index 000000000..ad05314be --- /dev/null +++ b/avr/cores/megacommand/SdFat/FatLib/FatFormatter.cpp @@ -0,0 +1,280 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "FatLib.h" +// Set nonzero to use calculated CHS in MBR. Should not be required. +#define USE_LBA_TO_CHS 1 + +// Constants for file system structure optimized for flash. +uint16_t const BU16 = 128; +uint16_t const BU32 = 8192; +// Assume 512 byte sectors. +const uint16_t BYTES_PER_SECTOR = 512; +const uint16_t SECTORS_PER_MB = 0X100000/BYTES_PER_SECTOR; +const uint16_t FAT16_ROOT_ENTRY_COUNT = 512; +const uint16_t FAT16_ROOT_SECTOR_COUNT = + 32*FAT16_ROOT_ENTRY_COUNT/BYTES_PER_SECTOR; +//------------------------------------------------------------------------------ +#define PRINT_FORMAT_PROGRESS 1 +#if !PRINT_FORMAT_PROGRESS +#define writeMsg(str) +#elif defined(__AVR__) +#define writeMsg(str) if (m_pr) m_pr->print(F(str)) +#else // PRINT_FORMAT_PROGRESS +#define writeMsg(str) if (m_pr) m_pr->write(str) +#endif // PRINT_FORMAT_PROGRESS +//------------------------------------------------------------------------------ +bool FatFormatter::format(FsBlockDevice* dev, uint8_t* secBuf, print_t* pr) { + bool rtn; + m_dev = dev; + m_secBuf = secBuf; + m_pr = pr; + m_sectorCount = m_dev->sectorCount(); + m_capacityMB = (m_sectorCount + SECTORS_PER_MB - 1)/SECTORS_PER_MB; + + if (m_capacityMB <= 6) { + writeMsg("Card is too small.\r\n"); + return false; + } else if (m_capacityMB <= 16) { + m_sectorsPerCluster = 2; + } else if (m_capacityMB <= 32) { + m_sectorsPerCluster = 4; + } else if (m_capacityMB <= 64) { + m_sectorsPerCluster = 8; + } else if (m_capacityMB <= 128) { + m_sectorsPerCluster = 16; + } else if (m_capacityMB <= 1024) { + m_sectorsPerCluster = 32; + } else if (m_capacityMB <= 32768) { + m_sectorsPerCluster = 64; + } else { + // SDXC cards + m_sectorsPerCluster = 128; + } + rtn = m_sectorCount < 0X400000 ? makeFat16() : makeFat32(); + if (rtn) { + writeMsg("Format Done\r\n"); + } else { + writeMsg("Format Failed\r\n"); + } + return rtn; +} +//------------------------------------------------------------------------------ +bool FatFormatter::initFatDir(uint8_t fatType, uint32_t sectorCount) { + size_t n; + memset(m_secBuf, 0, BYTES_PER_SECTOR); + writeMsg("Writing FAT "); + for (uint32_t i = 1; i < sectorCount; i++) { + if (!m_dev->writeSector(m_fatStart + i, m_secBuf)) { + return false; + } + if ((i%(sectorCount/32)) == 0) { + writeMsg("."); + } + } + writeMsg("\r\n"); + // Allocate reserved clusters and root for FAT32. + m_secBuf[0] = 0XF8; + n = fatType == 16 ? 4 : 12; + for (size_t i = 1; i < n; i++) { + m_secBuf[i] = 0XFF; + } + return m_dev->writeSector(m_fatStart, m_secBuf) && + m_dev->writeSector(m_fatStart + m_fatSize, m_secBuf); +} +//------------------------------------------------------------------------------ +void FatFormatter::initPbs() { + PbsFat_t* pbs = reinterpret_cast(m_secBuf); + memset(m_secBuf, 0, BYTES_PER_SECTOR); + pbs->jmpInstruction[0] = 0XEB; + pbs->jmpInstruction[1] = 0X76; + pbs->jmpInstruction[2] = 0X90; + for (uint8_t i = 0; i < sizeof(pbs->oemName); i++) { + pbs->oemName[i] = ' '; + } + setLe16(pbs->bpb.bpb16.bytesPerSector, BYTES_PER_SECTOR); + pbs->bpb.bpb16.sectorsPerCluster = m_sectorsPerCluster; + setLe16(pbs->bpb.bpb16.reservedSectorCount, m_reservedSectorCount); + pbs->bpb.bpb16.fatCount = 2; + // skip rootDirEntryCount + // skip totalSectors16 + pbs->bpb.bpb16.mediaType = 0XF8; + // skip sectorsPerFat16 + // skip sectorsPerTrack + // skip headCount + setLe32(pbs->bpb.bpb16.hidddenSectors, m_relativeSectors); + setLe32(pbs->bpb.bpb16.totalSectors32, m_totalSectors); + // skip rest of bpb + setLe16(pbs->signature, PBR_SIGNATURE); +} +//------------------------------------------------------------------------------ +bool FatFormatter::makeFat16() { + uint32_t nc; + uint32_t r; + PbsFat_t* pbs = reinterpret_cast(m_secBuf); + + for (m_dataStart = 2*BU16; ; m_dataStart += BU16) { + nc = (m_sectorCount - m_dataStart)/m_sectorsPerCluster; + m_fatSize = (nc + 2 + (BYTES_PER_SECTOR/2) - 1)/(BYTES_PER_SECTOR/2); + r = BU16 + 1 + 2*m_fatSize + FAT16_ROOT_SECTOR_COUNT; + if (m_dataStart >= r) { + m_relativeSectors = m_dataStart - r + BU16; + break; + } + } + // check valid cluster count for FAT16 volume + if (nc < 4085 || nc >= 65525) { + writeMsg("Bad cluster count\r\n"); + return false; + } + m_reservedSectorCount = 1; + m_fatStart = m_relativeSectors + m_reservedSectorCount; + m_totalSectors = nc*m_sectorsPerCluster + + 2*m_fatSize + m_reservedSectorCount + 32; + if (m_totalSectors < 65536) { + m_partType = 0X04; + } else { + m_partType = 0X06; + } + // write MBR + if (!writeMbr()) { + return false; + } + initPbs(); + setLe16(pbs->bpb.bpb16.rootDirEntryCount, FAT16_ROOT_ENTRY_COUNT); + setLe16(pbs->bpb.bpb16.sectorsPerFat16, m_fatSize); + pbs->bpb.bpb16.physicalDriveNumber = 0X80; + pbs->bpb.bpb16.extSignature = EXTENDED_BOOT_SIGNATURE; + setLe32(pbs->bpb.bpb16.volumeSerialNumber, 1234567); + for (size_t i = 0; i < sizeof(pbs->bpb.bpb16.volumeLabel); i++) { + pbs->bpb.bpb16.volumeLabel[i] = ' '; + } + pbs->bpb.bpb16.volumeType[0] = 'F'; + pbs->bpb.bpb16.volumeType[1] = 'A'; + pbs->bpb.bpb16.volumeType[2] = 'T'; + pbs->bpb.bpb16.volumeType[3] = '1'; + pbs->bpb.bpb16.volumeType[4] = '6'; + if (!m_dev->writeSector(m_relativeSectors, m_secBuf)) { + return false; + } + return initFatDir(16, m_dataStart - m_fatStart); +} +//------------------------------------------------------------------------------ +bool FatFormatter::makeFat32() { + uint32_t nc; + uint32_t r; + PbsFat_t* pbs = reinterpret_cast(m_secBuf); + FsInfo_t* fsi = reinterpret_cast(m_secBuf); + + m_relativeSectors = BU32; + for (m_dataStart = 2*BU32; ; m_dataStart += BU32) { + nc = (m_sectorCount - m_dataStart)/m_sectorsPerCluster; + m_fatSize = (nc + 2 + (BYTES_PER_SECTOR/4) - 1)/(BYTES_PER_SECTOR/4); + r = m_relativeSectors + 9 + 2*m_fatSize; + if (m_dataStart >= r) { + break; + } + } + // error if too few clusters in FAT32 volume + if (nc < 65525) { + writeMsg("Bad cluster count\r\n"); + return false; + } + m_reservedSectorCount = m_dataStart - m_relativeSectors - 2*m_fatSize; + m_fatStart = m_relativeSectors + m_reservedSectorCount; + m_totalSectors = nc*m_sectorsPerCluster + m_dataStart - m_relativeSectors; + // type depends on address of end sector + // max CHS has lba = 16450560 = 1024*255*63 + if ((m_relativeSectors + m_totalSectors) <= 16450560) { + // FAT32 with CHS and LBA + m_partType = 0X0B; + } else { + // FAT32 with only LBA + m_partType = 0X0C; + } + if (!writeMbr()) { + return false; + } + initPbs(); + setLe32(pbs->bpb.bpb32.sectorsPerFat32, m_fatSize); + setLe32(pbs->bpb.bpb32.fat32RootCluster, 2); + setLe16(pbs->bpb.bpb32.fat32FSInfoSector, 1); + setLe16(pbs->bpb.bpb32.fat32BackBootSector, 6); + pbs->bpb.bpb32.physicalDriveNumber = 0X80; + pbs->bpb.bpb32.extSignature = EXTENDED_BOOT_SIGNATURE; + setLe32(pbs->bpb.bpb32.volumeSerialNumber, 1234567); + for (size_t i = 0; i < sizeof(pbs->bpb.bpb32.volumeLabel); i++) { + pbs->bpb.bpb32.volumeLabel[i] = ' '; + } + pbs->bpb.bpb32.volumeType[0] = 'F'; + pbs->bpb.bpb32.volumeType[1] = 'A'; + pbs->bpb.bpb32.volumeType[2] = 'T'; + pbs->bpb.bpb32.volumeType[3] = '3'; + pbs->bpb.bpb32.volumeType[4] = '2'; + if (!m_dev->writeSector(m_relativeSectors, m_secBuf) || + !m_dev->writeSector(m_relativeSectors + 6, m_secBuf)) { + return false; + } + // write extra boot area and backup + memset(m_secBuf, 0 , BYTES_PER_SECTOR); + setLe32(fsi->trailSignature, FSINFO_TRAIL_SIGNATURE); + if (!m_dev->writeSector(m_relativeSectors + 2, m_secBuf) || + !m_dev->writeSector(m_relativeSectors + 8, m_secBuf)) { + return false; + } + // write FSINFO sector and backup + setLe32(fsi->leadSignature, FSINFO_LEAD_SIGNATURE); + setLe32(fsi->structSignature, FSINFO_STRUCT_SIGNATURE); + setLe32(fsi->freeCount, 0XFFFFFFFF); + setLe32(fsi->nextFree, 0XFFFFFFFF); + if (!m_dev->writeSector(m_relativeSectors + 1, m_secBuf) || + !m_dev->writeSector(m_relativeSectors + 7, m_secBuf)) { + return false; + } + return initFatDir(32, 2*m_fatSize + m_sectorsPerCluster); +} +//------------------------------------------------------------------------------ +bool FatFormatter::writeMbr() { + memset(m_secBuf, 0, BYTES_PER_SECTOR); + MbrSector_t* mbr = reinterpret_cast(m_secBuf); + +#if USE_LBA_TO_CHS + lbaToMbrChs(mbr->part->beginCHS, m_capacityMB, m_relativeSectors); + lbaToMbrChs(mbr->part->endCHS, m_capacityMB, + m_relativeSectors + m_totalSectors -1); +#else // USE_LBA_TO_CHS + mbr->part->beginCHS[0] = 1; + mbr->part->beginCHS[1] = 1; + mbr->part->beginCHS[2] = 0; + mbr->part->endCHS[0] = 0XFE; + mbr->part->endCHS[1] = 0XFF; + mbr->part->endCHS[2] = 0XFF; +#endif // USE_LBA_TO_CHS + + mbr->part->type = m_partType; + setLe32(mbr->part->relativeSectors, m_relativeSectors); + setLe32(mbr->part->totalSectors, m_totalSectors); + setLe16(mbr->signature, MBR_SIGNATURE); + return m_dev->writeSector(0, m_secBuf); +} diff --git a/avr/cores/megacommand/SdFat/FatLib/FatFormatter.h b/avr/cores/megacommand/SdFat/FatLib/FatFormatter.h new file mode 100644 index 000000000..a772efa40 --- /dev/null +++ b/avr/cores/megacommand/SdFat/FatLib/FatFormatter.h @@ -0,0 +1,66 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef FatFormatter_h +#define FatFormatter_h +#include "../common/SysCall.h" +#include "../common/FsBlockDevice.h" +/** + * \class FatFormatter + * \brief Format a FAT volume. + */ +class FatFormatter { + public: + /** + * Format a FAT volume. + * + * \param[in] dev Block device for volume. + * \param[in] secBuffer buffer for writing to volume. + * \param[in] pr Print device for progress output. + * + * \return true for success or false for failure. + */ + bool format(FsBlockDevice* dev, uint8_t* secBuffer, print_t* pr = nullptr); + + private: + bool initFatDir(uint8_t fatType, uint32_t sectorCount); + void initPbs(); + bool makeFat16(); + bool makeFat32(); + bool writeMbr(); + uint32_t m_capacityMB; + uint32_t m_dataStart; + uint32_t m_fatSize; + uint32_t m_fatStart; + uint32_t m_relativeSectors; + uint32_t m_sectorCount; + uint32_t m_totalSectors; + FsBlockDevice* m_dev; + print_t* m_pr; + uint8_t* m_secBuf; + uint16_t m_reservedSectorCount; + uint8_t m_partType; + uint8_t m_sectorsPerCluster; +}; +#endif // FatFormatter_h diff --git a/avr/cores/megacommand/SdFat/FatLib/FatLib.h b/avr/cores/megacommand/SdFat/FatLib/FatLib.h index fa45dbebe..5da6708cc 100644 --- a/avr/cores/megacommand/SdFat/FatLib/FatLib.h +++ b/avr/cores/megacommand/SdFat/FatLib/FatLib.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 20011-2017 Bill Greiman + * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -24,15 +24,6 @@ */ #ifndef FatLib_h #define FatLib_h -#include "ArduinoFiles.h" -#include "ArduinoStream.h" -#include "FatFileSystem.h" -#include "FatLibConfig.h" #include "FatVolume.h" -#include "FatFile.h" -#include "StdioStream.h" -#include "fstream.h" -//------------------------------------------------------------------------------ -/** FatFileSystem version YYYYMMDD */ -#define FAT_LIB_VERSION 20150131 +#include "FatFormatter.h" #endif // FatLib_h diff --git a/avr/cores/megacommand/SdFat/FatLib/FatLibConfig.h b/avr/cores/megacommand/SdFat/FatLib/FatLibConfig.h deleted file mode 100644 index 6713a1332..000000000 --- a/avr/cores/megacommand/SdFat/FatLib/FatLibConfig.h +++ /dev/null @@ -1,146 +0,0 @@ -/** - * Copyright (c) 20011-2017 Bill Greiman - * This file is part of the SdFat library for SD memory cards. - * - * MIT License - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ -/** - * \file - * \brief configuration definitions - */ -#ifndef FatLibConfig_h -#define FatLibConfig_h -#include -// Allow this file to override defaults. -#include "SdFatConfig.h" - -#ifdef __AVR__ -#include -#endif // __AVR__ -//------------------------------------------------------------------------------ -/** - * Set USE_LONG_FILE_NAMES nonzero to use long file names (LFN). - * Long File Name are limited to a maximum length of 255 characters. - * - * This implementation allows 7-bit characters in the range - * 0X20 to 0X7E. The following characters are not allowed: - * - * < (less than) - * > (greater than) - * : (colon) - * " (double quote) - * / (forward slash) - * \ (backslash) - * | (vertical bar or pipe) - * ? (question mark) - * * (asterisk) - * - */ -#ifndef USE_LONG_FILE_NAMES -#define USE_LONG_FILE_NAMES 1 -#endif // USE_LONG_FILE_NAMES -//------------------------------------------------------------------------------ -/** - * Set USE_SEPARATE_FAT_CACHE non-zero to use a second 512 byte cache - * for FAT table entries. Improves performance for large writes that - * are not a multiple of 512 bytes. - */ -#ifndef USE_SEPARATE_FAT_CACHE -#ifdef __arm__ -#define USE_SEPARATE_FAT_CACHE 1 -#else // __arm__ -#define USE_SEPARATE_FAT_CACHE 0 -#endif // __arm__ -#endif // USE_SEPARATE_FAT_CACHE -//------------------------------------------------------------------------------ -/** - * Set USE_MULTI_BLOCK_IO non-zero to use multi-block SD read/write. - * - * Don't use mult-block read/write on small AVR boards. - */ -#ifndef USE_MULTI_BLOCK_IO -#if defined(RAMEND) && RAMEND < 3000 -#define USE_MULTI_BLOCK_IO 0 -#else // RAMEND -#define USE_MULTI_BLOCK_IO 1 -#endif // RAMEND -#endif // USE_MULTI_BLOCK_IO -//------------------------------------------------------------------------------ -/** - * Set MAINTAIN_FREE_CLUSTER_COUNT nonzero to keep the count of free clusters - * updated. This will increase the speed of the freeClusterCount() call - * after the first call. Extra flash will be required. - */ -#ifndef MAINTAIN_FREE_CLUSTER_COUNT -#define MAINTAIN_FREE_CLUSTER_COUNT 0 -#endif // MAINTAIN_FREE_CLUSTER_COUNT -//------------------------------------------------------------------------------ -/** - * Set DESTRUCTOR_CLOSES_FILE non-zero to close a file in its destructor. - * - * Causes use of lots of heap in ARM. - */ -#ifndef DESTRUCTOR_CLOSES_FILE -#define DESTRUCTOR_CLOSES_FILE 0 -#endif // DESTRUCTOR_CLOSES_FILE -//------------------------------------------------------------------------------ -/** - * Call flush for endl if ENDL_CALLS_FLUSH is non-zero - * - * The standard for iostreams is to call flush. This is very costly for - * SdFat. Each call to flush causes 2048 bytes of I/O to the SD. - * - * SdFat has a single 512 byte buffer for I/O so it must write the current - * data block to the SD, read the directory block from the SD, update the - * directory entry, write the directory block to the SD and read the data - * block back into the buffer. - * - * The SD flash memory controller is not designed for this many rewrites - * so performance may be reduced by more than a factor of 100. - * - * If ENDL_CALLS_FLUSH is zero, you must call flush and/or close to force - * all data to be written to the SD. - */ -#ifndef ENDL_CALLS_FLUSH -#define ENDL_CALLS_FLUSH 0 -#endif // ENDL_CALLS_FLUSH -//------------------------------------------------------------------------------ -/** - * Allow FAT12 volumes if FAT12_SUPPORT is non-zero. - * FAT12 has not been well tested. - */ -#ifndef FAT12_SUPPORT -#define FAT12_SUPPORT 0 -#endif // FAT12_SUPPORT -//------------------------------------------------------------------------------ -/** - * Enable Extra features for Arduino. - */ -// #define ENABLE_ARDUINO_FEATURES 0 ////////////////////////FIX THIS ///////////////// -#ifndef ENABLE_ARDUINO_FEATURES -#include -#if defined(ARDUINO) || defined(PLATFORM_ID) || defined(DOXYGEN) -#define ENABLE_ARDUINO_FEATURES 1 -#else // #if defined(ARDUINO) || defined(DOXYGEN) -#define ENABLE_ARDUINO_FEATURES 0 -#endif // defined(ARDUINO) || defined(DOXYGEN) -#endif // ENABLE_ARDUINO_FEATURES -#endif // FatLibConfig_h diff --git a/avr/cores/megacommand/SdFat/FatLib/FatName.cpp b/avr/cores/megacommand/SdFat/FatLib/FatName.cpp new file mode 100644 index 000000000..e8e2c0c4b --- /dev/null +++ b/avr/cores/megacommand/SdFat/FatLib/FatName.cpp @@ -0,0 +1,356 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#define DBG_FILE "FatName.cpp" +#include "../common/DebugMacros.h" +#include "../common/FsUtf.h" +#include "FatLib.h" +//------------------------------------------------------------------------------ +uint16_t FatFile::getLfnChar(DirLfn_t* ldir, uint8_t i) { + if (i < 5) { + return getLe16(ldir->unicode1 + 2*i); + } else if (i < 11) { + return getLe16(ldir->unicode2 + 2*i - 10); + } else if (i < 13) { + return getLe16(ldir->unicode3 + 2*i - 22); + } + DBG_HALT_IF(i >= 13); + return 0; +} +//------------------------------------------------------------------------------ +size_t FatFile::getName(char* name, size_t size) { +#if !USE_LONG_FILE_NAMES + return getSFN(name, size); +#elif USE_UTF8_LONG_NAMES + return getName8(name, size); +#else + return getName7(name, size); +#endif // !USE_LONG_FILE_NAMES +} +//------------------------------------------------------------------------------ +size_t FatFile::getName7(char* name, size_t size) { + FatFile dir; + DirLfn_t* ldir; + size_t n = 0; + if (!isOpen()) { + DBG_FAIL_MACRO; + goto fail; + } + if (!isLFN()) { + return getSFN(name, size); + } + if (!dir.openCluster(this)) { + DBG_FAIL_MACRO; + goto fail; + } + for (uint8_t order = 1; order <= m_lfnOrd; order++) { + ldir = reinterpret_cast(dir.cacheDir(m_dirIndex - order)); + if (!ldir) { + DBG_FAIL_MACRO; + goto fail; + } + if (ldir->attributes != FAT_ATTRIB_LONG_NAME || + order != (ldir->order & 0X1F)) { + DBG_FAIL_MACRO; + goto fail; + } + for (uint8_t i = 0; i < 13; i++) { + uint16_t c = getLfnChar(ldir, i); + if (c == 0) { + goto done; + } + if ((n + 1) >= size) { + DBG_FAIL_MACRO; + goto fail; + } + name[n++] = c >= 0X7F ? '?' : c; + } + } + done: + name[n] = 0; + return n; + + fail: + name[0] = '\0'; + return 0; +} +//------------------------------------------------------------------------------ +size_t FatFile::getName8(char* name, size_t size) { + char* end = name + size; + char* str = name; + char* ptr; + FatFile dir; + DirLfn_t* ldir; + uint16_t hs = 0; + uint32_t cp; + if (!isOpen()) { + DBG_FAIL_MACRO; + goto fail; + } + if (!isLFN()) { + return getSFN(name, size); + } + if (!dir.openCluster(this)) { + DBG_FAIL_MACRO; + goto fail; + } + for (uint8_t order = 1; order <= m_lfnOrd; order++) { + ldir = reinterpret_cast(dir.cacheDir(m_dirIndex - order)); + if (!ldir) { + DBG_FAIL_MACRO; + goto fail; + } + if (ldir->attributes != FAT_ATTRIB_LONG_NAME || + order != (ldir->order & 0X1F)) { + DBG_FAIL_MACRO; + goto fail; + } + for (uint8_t i = 0; i < 13; i++) { + uint16_t c = getLfnChar(ldir, i); + if (hs) { + if (!FsUtf::isLowSurrogate(c)) { + DBG_FAIL_MACRO; + goto fail; + } + cp = FsUtf::u16ToCp(hs, c); + hs = 0; + } else if (!FsUtf::isSurrogate(c)) { + if (c == 0) { + goto done; + } + cp = c; + } else if (FsUtf::isHighSurrogate(c)) { + hs = c; + continue; + } else { + DBG_FAIL_MACRO; + goto fail; + } + // Save space for zero byte. + ptr = FsUtf::cpToMb(cp, str, end - 1); + if (!ptr) { + DBG_FAIL_MACRO; + goto fail; + } + str = ptr; + } + } + done: + *str = '\0'; + return str - name; + + fail: + *name = 0; + return 0; +} +//------------------------------------------------------------------------------ +size_t FatFile::getSFN(char* name, size_t size) { + char c; + uint8_t j = 0; + uint8_t lcBit = FAT_CASE_LC_BASE; + uint8_t* ptr; + DirFat_t* dir; + if (!isOpen()) { + DBG_FAIL_MACRO; + goto fail; + } + if (isRoot()) { + if (size < 2) { + DBG_FAIL_MACRO; + goto fail; + } + name[0] = '/'; + name[1] = '\0'; + return 1; + } + // cache entry + dir = cacheDirEntry(FsCache::CACHE_FOR_READ); + if (!dir) { + DBG_FAIL_MACRO; + goto fail; + } + ptr = dir->name; + // format name + for (uint8_t i = 0; i < 12; i++) { + if (i == 8) { + if (*ptr == ' ') { + break; + } + lcBit = FAT_CASE_LC_EXT; + c = '.'; + } else { + c = *ptr++; + if ('A' <= c && c <= 'Z' && (lcBit & dir->caseFlags)) { + c += 'a' - 'A'; + } + if (c == ' ') { + continue; + } + } + if ((j + 1u) >= size) { + DBG_FAIL_MACRO; + goto fail; + } + name[j++] = c; + } + name[j] = '\0'; + return j; + + fail: + name[0] = '\0'; + return 0; +} +//------------------------------------------------------------------------------ +size_t FatFile::printName(print_t* pr) { +#if !USE_LONG_FILE_NAMES + return printSFN(pr); +#elif USE_UTF8_LONG_NAMES + return printName8(pr); +# else // USE_LONG_FILE_NAMES + return printName7(pr); +#endif // !USE_LONG_FILE_NAMES + } +//------------------------------------------------------------------------------ +size_t FatFile::printName7(print_t* pr) { + FatFile dir; + DirLfn_t* ldir; + size_t n = 0; + uint8_t buf[13]; + uint8_t i; + + if (!isOpen()) { + DBG_FAIL_MACRO; + goto fail; + } + if (!isLFN()) { + return printSFN(pr); + } + if (!dir.openCluster(this)) { + DBG_FAIL_MACRO; + goto fail; + } + for (uint8_t order = 1; order <= m_lfnOrd; order++) { + ldir = reinterpret_cast(dir.cacheDir(m_dirIndex - order)); + if (!ldir) { + DBG_FAIL_MACRO; + goto fail; + } + if (ldir->attributes != FAT_ATTRIB_LONG_NAME || + order != (ldir->order & 0X1F)) { + DBG_FAIL_MACRO; + goto fail; + } + for (i = 0; i < 13; i++) { + uint16_t u = getLfnChar(ldir, i); + if (u == 0) { + // End of name. + break; + } + buf[i] = u < 0X7F ? u : '?'; + n++; + } + pr->write(buf, i); + } + return n; + + fail: + return 0; +} +//------------------------------------------------------------------------------ +size_t FatFile::printName8(print_t *pr) { + FatFile dir; + DirLfn_t* ldir; + uint16_t hs = 0; + uint32_t cp; + size_t n = 0; + char buf[5]; + char* end = buf + sizeof(buf); + if (!isOpen()) { + DBG_FAIL_MACRO; + goto fail; + } + if (!isLFN()) { + return printSFN(pr); + } + if (!dir.openCluster(this)) { + DBG_FAIL_MACRO; + goto fail; + } + for (uint8_t order = 1; order <= m_lfnOrd; order++) { + ldir = reinterpret_cast(dir.cacheDir(m_dirIndex - order)); + if (!ldir) { + DBG_FAIL_MACRO; + goto fail; + } + if (ldir->attributes != FAT_ATTRIB_LONG_NAME || + order != (ldir->order & 0X1F)) { + DBG_FAIL_MACRO; + goto fail; + } + for (uint8_t i = 0; i < 13; i++) { + uint16_t c = getLfnChar(ldir, i);; + if (hs) { + if (!FsUtf::isLowSurrogate(c)) { + DBG_FAIL_MACRO; + goto fail; + } + cp = FsUtf::u16ToCp(hs, c); + hs = 0; + } else if (!FsUtf::isSurrogate(c)) { + if (c == 0) { + break; + } + cp = c; + } else if (FsUtf::isHighSurrogate(c)) { + hs = c; + continue; + } else { + DBG_FAIL_MACRO; + goto fail; + } + char* str = FsUtf::cpToMb(cp, buf, end); + if (!str) { + DBG_FAIL_MACRO; + goto fail; + } + n += pr->write(buf, str - buf); + } + } + return n; + + fail: + return 0; +} +//------------------------------------------------------------------------------ +size_t FatFile::printSFN(print_t* pr) { + char name[13]; + if (!getSFN(name, sizeof(name))) { + DBG_FAIL_MACRO; + goto fail; + } + return pr->write(name); + + fail: + return 0; +} diff --git a/avr/cores/megacommand/SdFat/FatLib/FatPartition.cpp b/avr/cores/megacommand/SdFat/FatLib/FatPartition.cpp new file mode 100644 index 000000000..d8716a6a7 --- /dev/null +++ b/avr/cores/megacommand/SdFat/FatLib/FatPartition.cpp @@ -0,0 +1,499 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include +#define DBG_FILE "FatPartition.cpp" +#include "../common/DebugMacros.h" +#include "FatLib.h" +//------------------------------------------------------------------------------ +bool FatPartition::allocateCluster(uint32_t current, uint32_t* next) { + uint32_t find; + bool setStart; + if (m_allocSearchStart < current) { + // Try to keep file contiguous. Start just after current cluster. + find = current; + setStart = false; + } else { + find = m_allocSearchStart; + setStart = true; + } + while (1) { + find++; + if (find > m_lastCluster) { + if (setStart) { + // Can't find space, checked all clusters. + DBG_FAIL_MACRO; + goto fail; + } + find = m_allocSearchStart; + setStart = true; + continue; + } + if (find == current) { + // Can't find space, already searched clusters after current. + DBG_FAIL_MACRO; + goto fail; + } + uint32_t f; + int8_t fg = fatGet(find, &f); + if (fg < 0) { + DBG_FAIL_MACRO; + goto fail; + } + if (fg && f == 0) { + break; + } + } + if (setStart) { + m_allocSearchStart = find; + } + // Mark end of chain. + if (!fatPutEOC(find)) { + DBG_FAIL_MACRO; + goto fail; + } + if (current) { + // Link clusters. + if (!fatPut(current, find)) { + DBG_FAIL_MACRO; + goto fail; + } + } + updateFreeClusterCount(-1); + *next = find; + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +// find a contiguous group of clusters +bool FatPartition::allocContiguous(uint32_t count, uint32_t* firstCluster) { + // flag to save place to start next search + bool setStart = true; + // start of group + uint32_t bgnCluster; + // end of group + uint32_t endCluster; + // Start at cluster after last allocated cluster. + endCluster = bgnCluster = m_allocSearchStart + 1; + + // search the FAT for free clusters + while (1) { + if (endCluster > m_lastCluster) { + // Can't find space. + DBG_FAIL_MACRO; + goto fail; + } + uint32_t f; + int8_t fg = fatGet(endCluster, &f); + if (fg < 0) { + DBG_FAIL_MACRO; + goto fail; + } + if (f || fg == 0) { + // don't update search start if unallocated clusters before endCluster. + if (bgnCluster != endCluster) { + setStart = false; + } + // cluster in use try next cluster as bgnCluster + bgnCluster = endCluster + 1; + } else if ((endCluster - bgnCluster + 1) == count) { + // done - found space + break; + } + endCluster++; + } + // Remember possible next free cluster. + if (setStart) { + m_allocSearchStart = endCluster; + } + // mark end of chain + if (!fatPutEOC(endCluster)) { + DBG_FAIL_MACRO; + goto fail; + } + // link clusters + while (endCluster > bgnCluster) { + if (!fatPut(endCluster - 1, endCluster)) { + DBG_FAIL_MACRO; + goto fail; + } + endCluster--; + } + // Maintain count of free clusters. + updateFreeClusterCount(-count); + + // return first cluster number to caller + *firstCluster = bgnCluster; + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +// Fetch a FAT entry - return -1 error, 0 EOC, else 1. +int8_t FatPartition::fatGet(uint32_t cluster, uint32_t* value) { + uint32_t sector; + uint32_t next; + uint8_t* pc; + + // error if reserved cluster of beyond FAT + if (cluster < 2 || cluster > m_lastCluster) { + DBG_FAIL_MACRO; + goto fail; + } + + if (fatType() == 32) { + sector = m_fatStartSector + (cluster >> (m_bytesPerSectorShift - 2)); + pc = fatCachePrepare(sector, FsCache::CACHE_FOR_READ); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + uint16_t offset = (cluster << 2) & m_sectorMask; + next = getLe32(pc + offset); + } else if (fatType() == 16) { + cluster &= 0XFFFF; + sector = m_fatStartSector + (cluster >> (m_bytesPerSectorShift - 1) ); + pc = fatCachePrepare(sector, FsCache::CACHE_FOR_READ); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + uint16_t offset = (cluster << 1) & m_sectorMask; + next = getLe16(pc + offset); + } else if (FAT12_SUPPORT && fatType() == 12) { + uint16_t index = cluster; + index += index >> 1; + sector = m_fatStartSector + (index >> m_bytesPerSectorShift); + pc = fatCachePrepare(sector, FsCache::CACHE_FOR_READ); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + index &= m_sectorMask; + uint16_t tmp = pc[index]; + index++; + if (index == m_bytesPerSector) { + pc = fatCachePrepare(sector + 1, FsCache::CACHE_FOR_READ); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + index = 0; + } + tmp |= pc[index] << 8; + next = cluster & 1 ? tmp >> 4 : tmp & 0XFFF; + } else { + DBG_FAIL_MACRO; + goto fail; + } + if (isEOC(next)) { + return 0; + } + *value = next; + return 1; + + fail: + return -1; +} +//------------------------------------------------------------------------------ +// Store a FAT entry +bool FatPartition::fatPut(uint32_t cluster, uint32_t value) { + uint32_t sector; + uint8_t* pc; + + // error if reserved cluster of beyond FAT + if (cluster < 2 || cluster > m_lastCluster) { + DBG_FAIL_MACRO; + goto fail; + } + + if (fatType() == 32) { + sector = m_fatStartSector + (cluster >> (m_bytesPerSectorShift - 2)); + pc = fatCachePrepare(sector, FsCache::CACHE_FOR_WRITE); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + uint16_t offset = (cluster << 2) & m_sectorMask; + setLe32(pc + offset, value); + return true; + } + + if (fatType() == 16) { + cluster &= 0XFFFF; + sector = m_fatStartSector + (cluster >> (m_bytesPerSectorShift - 1) ); + pc = fatCachePrepare(sector, FsCache::CACHE_FOR_WRITE); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + uint16_t offset = (cluster << 1) & m_sectorMask; + setLe16(pc + offset, value); + return true; + } + + if (FAT12_SUPPORT && fatType() == 12) { + uint16_t index = cluster; + index += index >> 1; + sector = m_fatStartSector + (index >> m_bytesPerSectorShift); + pc = fatCachePrepare(sector, FsCache::CACHE_FOR_WRITE); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + index &= m_sectorMask; + uint8_t tmp = value; + if (cluster & 1) { + tmp = (pc[index] & 0XF) | tmp << 4; + } + pc[index] = tmp; + + index++; + if (index == m_bytesPerSector) { + sector++; + index = 0; + pc = fatCachePrepare(sector, FsCache::CACHE_FOR_WRITE); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + } + tmp = value >> 4; + if (!(cluster & 1)) { + tmp = ((pc[index] & 0XF0)) | tmp >> 4; + } + pc[index] = tmp; + return true; + } else { + DBG_FAIL_MACRO; + goto fail; + } + + fail: + return false; +} +//------------------------------------------------------------------------------ +// free a cluster chain +bool FatPartition::freeChain(uint32_t cluster) { + uint32_t next; + int8_t fg; + do { + fg = fatGet(cluster, &next); + if (fg < 0) { + DBG_FAIL_MACRO; + goto fail; + } + // free cluster + if (!fatPut(cluster, 0)) { + DBG_FAIL_MACRO; + goto fail; + } + // Add one to count of free clusters. + updateFreeClusterCount(1); + if (cluster < m_allocSearchStart) { + m_allocSearchStart = cluster - 1; + } + cluster = next; + } while (fg); + + return true; + + fail: + return false; +} +//------------------------------------------------------------------------------ +int32_t FatPartition::freeClusterCount() { +#if MAINTAIN_FREE_CLUSTER_COUNT + if (m_freeClusterCount >= 0) { + return m_freeClusterCount; + } +#endif // MAINTAIN_FREE_CLUSTER_COUNT + uint32_t free = 0; + uint32_t sector; + uint32_t todo = m_lastCluster + 1; + uint16_t n; + + if (FAT12_SUPPORT && fatType() == 12) { + for (unsigned i = 2; i < todo; i++) { + uint32_t c; + int8_t fg = fatGet(i, &c); + if (fg < 0) { + DBG_FAIL_MACRO; + goto fail; + } + if (fg && c == 0) { + free++; + } + } + } else if (fatType() == 16 || fatType() == 32) { + sector = m_fatStartSector; + while (todo) { + uint8_t* pc = fatCachePrepare(sector++, FsCache::CACHE_FOR_READ); + if (!pc) { + DBG_FAIL_MACRO; + goto fail; + } + n = fatType() == 16 ? m_bytesPerSector/2 : m_bytesPerSector/4; + if (todo < n) { + n = todo; + } + if (fatType() == 16) { + uint16_t* p16 = reinterpret_cast(pc); + for (uint16_t i = 0; i < n; i++) { + if (p16[i] == 0) { + free++; + } + } + } else { + uint32_t* p32 = reinterpret_cast(pc); + for (uint16_t i = 0; i < n; i++) { + if (p32[i] == 0) { + free++; + } + } + } + todo -= n; + } + } else { + // invalid FAT type + DBG_FAIL_MACRO; + goto fail; + } + setFreeClusterCount(free); + return free; + + fail: + return -1; +} +//------------------------------------------------------------------------------ +bool FatPartition::init(FsBlockDevice* dev, uint8_t part, uint32_t volStart) { + uint32_t clusterCount; + uint32_t totalSectors; + m_blockDev = dev; + pbs_t* pbs; + BpbFat32_t* bpb; + MbrSector_t* mbr; + uint8_t tmp; + m_fatType = 0; + m_allocSearchStart = 1; + m_cache.init(dev); +#if USE_SEPARATE_FAT_CACHE + m_fatCache.init(dev); +#endif // USE_SEPARATE_FAT_CACHE + // if part == 0 assume super floppy with FAT boot sector in sector zero + // if part > 0 assume mbr volume with partition table + if (part) { + if (part > 4) { + DBG_FAIL_MACRO; + goto fail; + } + mbr = reinterpret_cast + (dataCachePrepare(0, FsCache::CACHE_FOR_READ)); + if (!mbr) { + DBG_FAIL_MACRO; + goto fail; + } + MbrPart_t* mp = mbr->part + part - 1; + if (mp->type == 0 || (mp->boot != 0 && mp->boot != 0X80)) { + DBG_FAIL_MACRO; + goto fail; + } + volStart = getLe32(mp->relativeSectors); + } + pbs = reinterpret_cast + (dataCachePrepare(volStart, FsCache::CACHE_FOR_READ)); + if (!pbs) { + DBG_FAIL_MACRO; + goto fail; + } + bpb = reinterpret_cast(pbs->bpb); + if (bpb->fatCount != 2 || getLe16(bpb->bytesPerSector) != m_bytesPerSector) { + DBG_FAIL_MACRO; + goto fail; + } + m_sectorsPerCluster = bpb->sectorsPerCluster; + m_clusterSectorMask = m_sectorsPerCluster - 1; + // determine shift that is same as multiply by m_sectorsPerCluster + m_sectorsPerClusterShift = 0; + for (tmp = 1; m_sectorsPerCluster != tmp; tmp <<= 1) { + if (tmp == 0) { + DBG_FAIL_MACRO; + goto fail; + } + m_sectorsPerClusterShift++; + } + m_sectorsPerFat = getLe16(bpb->sectorsPerFat16); + if (m_sectorsPerFat == 0) { + m_sectorsPerFat = getLe32(bpb->sectorsPerFat32); + } + m_fatStartSector = volStart + getLe16(bpb->reservedSectorCount); + + // count for FAT16 zero for FAT32 + m_rootDirEntryCount = getLe16(bpb->rootDirEntryCount); + + // directory start for FAT16 dataStart for FAT32 + m_rootDirStart = m_fatStartSector + 2 * m_sectorsPerFat; + // data start for FAT16 and FAT32 + m_dataStartSector = m_rootDirStart + + ((FS_DIR_SIZE*m_rootDirEntryCount + m_bytesPerSector - 1)/m_bytesPerSector); + + // total sectors for FAT16 or FAT32 + totalSectors = getLe16(bpb->totalSectors16); + if (totalSectors == 0) { + totalSectors = getLe32(bpb->totalSectors32); + } + // total data sectors + clusterCount = totalSectors - (m_dataStartSector - volStart); + + // divide by cluster size to get cluster count + clusterCount >>= m_sectorsPerClusterShift; + m_lastCluster = clusterCount + 1; + + // Indicate unknown number of free clusters. + setFreeClusterCount(-1); + // FAT type is determined by cluster count + if (clusterCount < 4085) { + m_fatType = 12; + if (!FAT12_SUPPORT) { + DBG_FAIL_MACRO; + goto fail; + } + } else if (clusterCount < 65525) { + m_fatType = 16; + } else { + m_rootDirStart = getLe32(bpb->fat32RootCluster); + m_fatType = 32; + } + m_cache.setMirrorOffset(m_sectorsPerFat); +#if USE_SEPARATE_FAT_CACHE + m_fatCache.setMirrorOffset(m_sectorsPerFat); +#endif // USE_SEPARATE_FAT_CACHE + return true; + + fail: + return false; +} diff --git a/avr/cores/megacommand/SdFat/FatLib/FatPartition.h b/avr/cores/megacommand/SdFat/FatLib/FatPartition.h new file mode 100644 index 000000000..cb7b03fc0 --- /dev/null +++ b/avr/cores/megacommand/SdFat/FatLib/FatPartition.h @@ -0,0 +1,295 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef FatPartition_h +#define FatPartition_h +/** + * \file + * \brief FatPartition class + */ +#include +#include "../common/SysCall.h" +#include "../common/FsBlockDevice.h" +#include "../common/FsCache.h" +#include "../common/FsStructs.h" + +/** Type for FAT12 partition */ +const uint8_t FAT_TYPE_FAT12 = 12; + +/** Type for FAT12 partition */ +const uint8_t FAT_TYPE_FAT16 = 16; + +/** Type for FAT12 partition */ +const uint8_t FAT_TYPE_FAT32 = 32; + +//============================================================================== +/** + * \class FatPartition + * \brief Access FAT16 and FAT32 partitions on raw file devices. + */ +class FatPartition { + public: + /** Create an instance of FatPartition + */ + FatPartition() {} + + /** \return The shift count required to multiply by bytesPerCluster. */ + uint8_t bytesPerClusterShift() const { + return m_sectorsPerClusterShift + m_bytesPerSectorShift; + } + /** \return Number of bytes in a cluster. */ + uint16_t bytesPerCluster() const { + return m_bytesPerSector << m_sectorsPerClusterShift; + } + /** \return Number of bytes per sector. */ + uint16_t bytesPerSector() const { + return m_bytesPerSector; + } + /** \return The shift count required to multiply by bytesPerCluster. */ + uint8_t bytesPerSectorShift() const { + return m_bytesPerSectorShift; + } + /** \return Number of directory entries per sector. */ + uint16_t dirEntriesPerCluster() const { + return m_sectorsPerCluster*(m_bytesPerSector/FS_DIR_SIZE); + } + /** \return Mask for sector offset. */ + uint16_t sectorMask() const { + return m_sectorMask; + } + /** \return The volume's cluster size in sectors. */ + uint8_t sectorsPerCluster() const { + return m_sectorsPerCluster; + } +#ifndef DOXYGEN_SHOULD_SKIP_THIS + uint8_t __attribute__((error("use sectorsPerCluster()"))) blocksPerCluster(); +#endif // DOXYGEN_SHOULD_SKIP_THIS + /** \return The number of sectors in one FAT. */ + uint32_t sectorsPerFat() const { + return m_sectorsPerFat; + } + /** Clear the cache and returns a pointer to the cache. Not for normal apps. + * \return A pointer to the cache buffer or zero if an error occurs. + */ + uint8_t* cacheClear() { + return m_cache.clear(); + } + /** \return The total number of clusters in the volume. */ + uint32_t clusterCount() const { + return m_lastCluster - 1; + } + /** \return The shift count required to multiply by sectorsPerCluster. */ + uint8_t sectorsPerClusterShift() const { + return m_sectorsPerClusterShift; + } + /** \return The logical sector number for the start of file data. */ + uint32_t dataStartSector() const { + return m_dataStartSector; + } + /** End access to volume + * \return pointer to sector size buffer for format. + */ + uint8_t* end() { + m_fatType = 0; + return cacheClear(); + } + /** \return The number of File Allocation Tables. */ + uint8_t fatCount() const { + return 2; + } + /** \return The logical sector number for the start of the first FAT. */ + uint32_t fatStartSector() const { + return m_fatStartSector; + } + /** \return The FAT type of the volume. Values are 12, 16 or 32. */ + uint8_t fatType() const { + return m_fatType; + } + /** \return free cluster count or -1 if an error occurs. */ + int32_t freeClusterCount(); + /** Initialize a FAT partition. + * + * \param[in] dev FsBlockDevice for this partition. + * \param[in] part The partition to be used. Legal values for \a part are + * 1-4 to use the corresponding partition on a device formatted with + * a MBR, Master Boot Record, or zero if the device is formatted as + * a super floppy with the FAT boot sector in sector volStart. + * \param[in] volStart location of volume if part is zero. + * + * \return true for success or false for failure. + */ + bool init(FsBlockDevice* dev, uint8_t part = 1, uint32_t volStart = 0); + /** \return The number of entries in the root directory for FAT16 volumes. */ + uint16_t rootDirEntryCount() const { + return m_rootDirEntryCount; + } + /** \return The logical sector number for the start of the root directory + on FAT16 volumes or the first cluster number on FAT32 volumes. */ + uint32_t rootDirStart() const { + return m_rootDirStart; + } + /** \return The number of sectors in the volume */ + uint32_t volumeSectorCount() const { + return sectorsPerCluster()*clusterCount(); + } + /** Debug access to FAT table + * + * \param[in] n cluster number. + * \param[out] v value of entry + * \return -1 error, 0 EOC, else 1. + */ + int8_t dbgFat(uint32_t n, uint32_t* v) { + return fatGet(n, v); + } + /** + * Check for FsBlockDevice busy. + * + * \return true if busy else false. + */ + bool isBusy() {return m_blockDev->isBusy();} + //---------------------------------------------------------------------------- +#ifndef DOXYGEN_SHOULD_SKIP_THIS + bool dmpDirSector(print_t* pr, uint32_t sector); + void dmpFat(print_t* pr, uint32_t start, uint32_t count); + bool dmpRootDir(print_t* pr, uint32_t n = 0); + void dmpSector(print_t* pr, uint32_t sector, uint8_t bits = 8); +#endif // DOXYGEN_SHOULD_SKIP_THIS + //---------------------------------------------------------------------------- + private: + /** FatFile allowed access to private members. */ + friend class FatFile; + //---------------------------------------------------------------------------- + static const uint8_t m_bytesPerSectorShift = 9; + static const uint16_t m_bytesPerSector = 1 << m_bytesPerSectorShift; + static const uint16_t m_sectorMask = m_bytesPerSector - 1; + //---------------------------------------------------------------------------- + FsBlockDevice* m_blockDev; // sector device + uint8_t m_sectorsPerCluster; // Cluster size in sectors. + uint8_t m_clusterSectorMask; // Mask to extract sector of cluster. + uint8_t m_sectorsPerClusterShift; // Cluster count to sector count shift. + uint8_t m_fatType = 0; // Volume type (12, 16, OR 32). + uint16_t m_rootDirEntryCount; // Number of entries in FAT16 root dir. + uint32_t m_allocSearchStart; // Start cluster for alloc search. + uint32_t m_sectorsPerFat; // FAT size in sectors + uint32_t m_dataStartSector; // First data sector number. + uint32_t m_fatStartSector; // Start sector for first FAT. + uint32_t m_lastCluster; // Last cluster number in FAT. + uint32_t m_rootDirStart; // Start sector FAT16, cluster FAT32. + //---------------------------------------------------------------------------- + // sector I/O functions. + bool cacheSafeRead(uint32_t sector, uint8_t* dst) { + return m_cache.cacheSafeRead(sector, dst); + } + bool cacheSafeRead(uint32_t sector, uint8_t* dst, size_t count) { + return m_cache.cacheSafeRead(sector, dst, count); + } + bool cacheSafeWrite(uint32_t sector, const uint8_t* dst) { + return m_cache.cacheSafeWrite(sector, dst); + } + bool cacheSafeWrite(uint32_t sector, const uint8_t* dst, size_t count) { + return m_cache.cacheSafeWrite(sector, dst, count); + } + bool syncDevice() { + return m_blockDev->syncDevice(); + } +#if MAINTAIN_FREE_CLUSTER_COUNT + int32_t m_freeClusterCount; // Count of free clusters in volume. + void setFreeClusterCount(int32_t value) { + m_freeClusterCount = value; + } + void updateFreeClusterCount(int32_t change) { + if (m_freeClusterCount >= 0) { + m_freeClusterCount += change; + } + } +#else // MAINTAIN_FREE_CLUSTER_COUNT + void setFreeClusterCount(int32_t value) { + (void)value; + } + void updateFreeClusterCount(int32_t change) { + (void)change; + } +#endif // MAINTAIN_FREE_CLUSTER_COUNT +// sector caches + FsCache m_cache; + bool cachePrepare(uint32_t sector, uint8_t option) { + return m_cache.prepare(sector, option); + } + FsCache* dataCache() {return &m_cache;} +#if USE_SEPARATE_FAT_CACHE + FsCache m_fatCache; + uint8_t* fatCachePrepare(uint32_t sector, uint8_t options) { + options |= FsCache::CACHE_STATUS_MIRROR_FAT; + return m_fatCache.prepare(sector, options); + } + bool cacheSync() { + return m_cache.sync() && m_fatCache.sync() && syncDevice(); + } +#else // USE_SEPARATE_FAT_CACHE + uint8_t* fatCachePrepare(uint32_t sector, uint8_t options) { + options |= FsCache::CACHE_STATUS_MIRROR_FAT; + return dataCachePrepare(sector, options); + } + bool cacheSync() { + return m_cache.sync() && syncDevice(); + } +#endif // USE_SEPARATE_FAT_CACHE + uint8_t* dataCachePrepare(uint32_t sector, uint8_t options) { + return m_cache.prepare(sector, options); + } + void cacheInvalidate() { + m_cache.invalidate(); + } + bool cacheSyncData() { + return m_cache.sync(); + } + uint8_t* cacheAddress() { + return m_cache.cacheBuffer(); + } + uint32_t cacheSectorNumber() { + return m_cache.sector(); + } + void cacheDirty() { + m_cache.dirty(); + } + //---------------------------------------------------------------------------- + bool allocateCluster(uint32_t current, uint32_t* next); + bool allocContiguous(uint32_t count, uint32_t* firstCluster); + uint8_t sectorOfCluster(uint32_t position) const { + return (position >> 9) & m_clusterSectorMask; + } + uint32_t clusterStartSector(uint32_t cluster) const { + return m_dataStartSector + ((cluster - 2) << m_sectorsPerClusterShift); + } + int8_t 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 freeChain(uint32_t cluster); + bool isEOC(uint32_t cluster) const { + return cluster > m_lastCluster; + } +}; +#endif // FatPartition diff --git a/avr/cores/megacommand/SdFat/FatLib/FatStructs.h b/avr/cores/megacommand/SdFat/FatLib/FatStructs.h deleted file mode 100644 index 112cbb6e1..000000000 --- a/avr/cores/megacommand/SdFat/FatLib/FatStructs.h +++ /dev/null @@ -1,882 +0,0 @@ -/** - * Copyright (c) 20011-2017 Bill Greiman - * This file is part of the SdFat library for SD memory cards. - * - * MIT License - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ -#ifndef FatStructs_h -#define FatStructs_h -/** - * \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 */ -const uint8_t BOOTSIG0 = 0X55; -/** Value for byte 511 of boot block or MBR */ -const uint8_t BOOTSIG1 = 0XAA; -/** Value for bootSignature field int FAT/FAT32 boot sector */ -const uint8_t EXTENDED_BOOT_SIG = 0X29; -//------------------------------------------------------------------------------ -/** - * \struct partitionTable - * \brief MBR partition table entry - * - * A partition table entry for a MBR formatted storage device. - * The MBR partition table has four entries. - */ -struct partitionTable { - /** - * Boot Indicator . Indicates whether the volume is the active - * partition. Legal values include: 0X00. Do not use for booting. - * 0X80 Active partition. - */ - 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. - */ - 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. - */ - unsigned beginSector : 6; - /** 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. - */ - uint8_t beginCylinderLow; - /** - * 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. - */ - 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. - */ - unsigned endSector : 6; - /** High bits of end cylinder */ - unsigned endCylinderHigh : 2; - /** - * 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; -} __attribute__((packed)); -/** Type name for partitionTable */ -typedef struct partitionTable part_t; -//------------------------------------------------------------------------------ -/** - * \struct masterBootRecord - * - * \brief Master Boot Record - * - * The first block of a storage device that is formatted with a MBR. - */ -struct masterBootRecord { - /** 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; -} __attribute__((packed)); -/** Type name for masterBootRecord */ -typedef struct masterBootRecord mbr_t; -//------------------------------------------------------------------------------ -/** - * \struct biosParmBlock - * - * \brief BIOS parameter block - * - * The BIOS parameter block describes the physical layout of a FAT volume. - */ -struct biosParmBlock { - /** - * Count of bytes per sector. This value may take on only the - * following values: 512, 1024, 2048 or 4096 - */ - uint16_t bytesPerSector; - /** - * Number of sectors per allocation unit. This value must be a - * power of 2 that is greater than 0. The legal values are - * 1, 2, 4, 8, 16, 32, 64, and 128. - */ - uint8_t sectorsPerCluster; - /** - * Number of sectors before the first FAT. - * This value must not be zero. - */ - uint16_t reservedSectorCount; - /** The count of FAT data structures on the volume. This field should - * always contain the value 2 for any FAT volume of any type. - */ - 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. - */ - 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). - */ - 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. - */ - 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. - */ - uint16_t sectorsPerFat16; - /** Sectors per track for interrupt 0x13. Not used otherwise. */ - uint16_t sectorsPerTrtack; - /** 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. - */ - 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. - */ - uint32_t totalSectors32; - /** - * 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. - */ - uint16_t fat32Flags; - /** - * 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. - */ - uint32_t fat32RootCluster; - /** - * 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. - */ - uint16_t fat32BackBootBlock; - /** - * Reserved for future expansion. Code that formats FAT32 volumes - * should always set all of the bytes of this field to 0. - */ - uint8_t fat32Reserved[12]; -} __attribute__((packed)); -/** Type name for biosParmBlock */ -typedef struct biosParmBlock bpb_t; -//------------------------------------------------------------------------------ -/** - * \struct fat_boot - * - * \brief Boot sector for a FAT12/FAT16 volume. - * - */ -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 non-executable bytes. - */ - uint8_t jump[3]; - /** - * 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. - */ - 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. - */ - 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. - */ - uint16_t reservedSectorCount; - /** - * 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. - */ - uint16_t rootDirEntryCount; - /** - * This field is the old 16-bit total count of sectors on the volume. - * This count includes the count of all sectors in all four regions - * of the volume. This field can be 0; if it is 0, then totalSectors32 - * must be non-zero. For FAT32 volumes, this field must be 0. For - * FAT12 and FAT16 volumes, this field contains the sector count, and - * totalSectors32 is 0 if the total sector count fits - * (is less than 0x10000). - */ - uint16_t totalSectors16; - /** - * This dates back to the old MS-DOS 1.x media determination and is - * no longer usually used for anything. 0xF8 is the standard value - * for fixed (non-removable) media. For removable media, 0xF0 is - * frequently used. Legal values are 0xF0 or 0xF8-0xFF. - */ - 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. - */ - 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. - */ - uint32_t hidddenSectors; - /** - * This field is the new 32-bit total count of sectors on the volume. - * This count includes the count of all sectors in all four regions - * of the volume. This field can be 0; if it is 0, then - * totalSectors16 must be non-zero. - */ - 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. - */ - 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. - */ - 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. - */ - char volumeLabel[11]; - /** - * 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; -} __attribute__((packed)); -/** Type name for FAT Boot Sector */ -typedef struct fat_boot fat_boot_t; -//------------------------------------------------------------------------------ -/** - * \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 non-executable bytes. - */ - uint8_t jump[3]; - /** - * 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. - */ - 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. - */ - uint8_t sectorsPerCluster; - /** - * 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. - */ - uint8_t fatCount; - /** - * FAT12/FAT16 only. For FAT32 volumes, this field must be set to 0. - */ - uint16_t rootDirEntryCount; - /** - * 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 (non-removable) 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. - */ - 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. - */ - uint32_t hidddenSectors; - /** - * Contains the total number of sectors in the FAT32 volume. - */ - uint32_t totalSectors32; - /** - * 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. - */ - uint16_t fat32Flags; - /** - * 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. - */ - uint32_t fat32RootCluster; - /** - * Sector number of FSINFO structure in the reserved area of the - * FAT32 volume. Usually 1. - */ - uint16_t fat32FSInfo; - /** - * If non-zero, indicates the sector number in the reserved area - * of the volume of a copy of the boot record. Usually 6. - * No value other than 6 is recommended. - */ - uint16_t fat32BackBootBlock; - /** - * 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. - */ - 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. - */ - 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. - */ - char volumeLabel[11]; - /** - * 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; -} __attribute__((packed)); -/** Type name for FAT32 Boot Sector */ -typedef struct fat32_boot fat32_boot_t; -//------------------------------------------------------------------------------ -/** Lead signature for a FSINFO sector */ -const uint32_t FSINFO_LEAD_SIG = 0x41615252; -/** Struct signature for a FSINFO sector */ -const uint32_t FSINFO_STRUCT_SIG = 0x61417272; -/** - * \struct fat32_fsinfo - * - * \brief FSINFO sector for a FAT32 volume. - * - */ -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; - /** - * Contains the last known free cluster count on the volume. - * If the value is 0xFFFFFFFF, then the free count is unknown - * and must be computed. Any other value can be used, but is - * not necessarily correct. It should be range checked at least - * to make sure it is <= volume cluster count. - */ - uint32_t freeCount; - /** - * This is a hint for the FAT driver. It indicates the cluster - * number at which the driver should start looking for free clusters. - * If the value is 0xFFFFFFFF, then there is no hint and the driver - * 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]; -} __attribute__((packed)); -/** Type name for FAT32 FSINFO Sector */ -typedef struct fat32_fsinfo fat32_fsinfo_t; -//------------------------------------------------------------------------------ -// End Of Chain values for FAT entries -/** FAT12 end of chain value used by Microsoft. */ -const uint16_t FAT12EOC = 0XFFF; -/** Minimum value for FAT12 EOC. Use to test for EOC. */ -const uint16_t FAT12EOC_MIN = 0XFF8; -/** FAT16 end of chain value used by Microsoft. */ -const uint16_t FAT16EOC = 0XFFFF; -/** Minimum value for FAT16 EOC. Use to test for EOC. */ -const uint16_t FAT16EOC_MIN = 0XFFF8; -/** FAT32 end of chain value used by Microsoft. */ -const uint32_t FAT32EOC = 0X0FFFFFFF; -/** Minimum value for FAT32 EOC. Use to test for EOC. */ -const uint32_t FAT32EOC_MIN = 0X0FFFFFF8; -/** Mask a for FAT32 entry. Entries are 28 bits. */ -const uint32_t FAT32MASK = 0X0FFFFFFF; -//------------------------------------------------------------------------------ -/** - * \struct directoryEntry - * \brief FAT short directory entry - * - * Short means short 8.3 name, not the entry size. - * - * Date Format. A FAT directory entry date stamp is a 16-bit field that is - * basically a date relative to the MS-DOS epoch of 01/01/1980. Here is the - * format (bit 0 is the LSB of the 16-bit word, bit 15 is the MSB of the - * 16-bit word): - * - * Bits 9-15: Count of years from 1980, valid value range 0-127 - * inclusive (1980-2107). - * - * Bits 5-8: Month of year, 1 = January, valid value range 1-12 inclusive. - * - * Bits 0-4: Day of month, valid value range 1-31 inclusive. - * - * Time Format. A FAT directory entry time stamp is a 16-bit field that has - * a granularity of 2 seconds. Here is the format (bit 0 is the LSB of the - * 16-bit word, bit 15 is the MSB of the 16-bit word). - * - * Bits 11-15: Hours, valid value range 0-23 inclusive. - * - * Bits 5-10: Minutes, valid value range 0-59 inclusive. - * - * Bits 0-4: 2-second count, valid value range 0-29 inclusive (0 - 58 seconds). - * - * The valid time range is from Midnight 00:00:00 to 23:59:58. - */ -struct directoryEntry { - /** Short 8.3 name. - * - * The first eight bytes contain the file name with blank fill. - * The last three bytes contain the file extension with blank fill. - */ - 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_. - */ - 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. - */ - uint8_t reservedNT; - /** - * The granularity of the seconds part of creationTime is 2 seconds - * so this field is a count of tenths of a second and its valid - * value range is 0-199 inclusive. (WHG note - seems to be hundredths) - */ - 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 lastAccessDate; - /** - * 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; -} __attribute__((packed)); -/** Type name for directoryEntry */ -typedef struct directoryEntry dir_t; -//------------------------------------------------------------------------------ -// Definitions for directory entries -// -/** escape for name[0] = 0XE5 */ -const uint8_t DIR_NAME_0XE5 = 0X05; -/** name[0] value for entry that is free after being "deleted" */ -const uint8_t DIR_NAME_DELETED = 0XE5; -/** name[0] value for entry that is free and no allocated entries follow */ -const uint8_t DIR_NAME_FREE = 0X00; -/** file is read-only */ -const uint8_t DIR_ATT_READ_ONLY = 0X01; -/** File should e hidden in directory listings */ -const uint8_t DIR_ATT_HIDDEN = 0X02; -/** Entry is for a system file */ -const uint8_t DIR_ATT_SYSTEM = 0X04; -/** Directory entry contains the volume label */ -const uint8_t DIR_ATT_VOLUME_ID = 0X08; -/** Entry is for a directory */ -const uint8_t DIR_ATT_DIRECTORY = 0X10; -/** Old DOS archive bit for backup support */ -const uint8_t DIR_ATT_ARCHIVE = 0X20; -/** Test value for long name entry. Test is - (d->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME. */ -const uint8_t DIR_ATT_LONG_NAME = 0X0F; -/** Test mask for long name entry */ -const uint8_t DIR_ATT_LONG_NAME_MASK = 0X3F; -/** defined attribute bits */ -const uint8_t DIR_ATT_DEFINED_BITS = 0X3F; - -/** Mask for file/subdirectory tests */ -const uint8_t DIR_ATT_FILE_TYPE_MASK = (DIR_ATT_VOLUME_ID | DIR_ATT_DIRECTORY); - -/** Filename base-name is all lower case */ -const uint8_t DIR_NT_LC_BASE = 0X08; -/** Filename extension is all lower case.*/ -const uint8_t DIR_NT_LC_EXT = 0X10; - - -/** 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. - */ -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 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. - */ -static inline uint8_t DIR_IS_FILE_OR_SUBDIR(const dir_t* dir) { - return (dir->attributes & DIR_ATT_VOLUME_ID) == 0; -} -/** 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. - */ -static inline uint8_t DIR_IS_LONG_NAME(const dir_t* dir) { - return dir->attributes == DIR_ATT_LONG_NAME; -} -/** Directory entry is hidden - * \param[in] dir Pointer to a directory entry. - * - * \return true if the entry is hidden else false. - */ -static inline uint8_t DIR_IS_HIDDEN(const dir_t* dir) { - return dir->attributes & DIR_ATT_HIDDEN; -} -/** 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. - */ -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 system type - * \param[in] dir Pointer to a directory entry. - * - * \return true if the entry is system else false. - */ -static inline uint8_t DIR_IS_SYSTEM(const dir_t* dir) { - return dir->attributes & DIR_ATT_SYSTEM; -} -/** 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 - * \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 - * \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 - * \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 - * \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 - * \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 - * \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 - * 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 */ -const uint16_t FAT_DEFAULT_DATE = ((2000 - 1980) << 9) | (1 << 5) | 1; -/** Default time for file timestamp is 1 am */ -const uint16_t FAT_DEFAULT_TIME = (1 << 11); -//------------------------------------------------------------------------------ -/** Dimension of first name field in long directory entry */ -const uint8_t LDIR_NAME1_DIM = 5; -/** Dimension of first name field in long directory entry */ -const uint8_t LDIR_NAME2_DIM = 6; -/** Dimension of first name field in long directory entry */ -const uint8_t LDIR_NAME3_DIM = 2; -/** - * \struct longDirectoryEntry - * \brief FAT long directory entry - */ -struct longDirectoryEntry { - /** - * The order of this entry in the sequence of long dir entries - * associated with the short dir entry at the end of the long dir set. - * - * If masked with 0X40 (LAST_LONG_ENTRY), this indicates the - * entry is the last long dir entry in a set of long dir entries. - * All valid sets of long dir entries must begin with an entry having - * this mask. - */ - uint8_t ord; - /** Characters 1-5 of the long-name sub-component in this entry. */ - uint16_t name1[LDIR_NAME1_DIM]; - /** Attributes - must be ATTR_LONG_NAME */ - uint8_t attr; - /** - * If zero, indicates a directory entry that is a sub-component of a - * long name. NOTE: Other values reserved for future extensions. - * - * Non-zero implies other directory entry types. - */ - uint8_t type; - /** - * Checksum of name in the short dir entry at the end of the - * long dir set. - */ - uint8_t chksum; - /** Characters 6-11 of the long-name sub-component in this entry. */ - uint16_t name2[LDIR_NAME2_DIM]; - /** Must be ZERO. This is an artifact of the FAT "first cluster" */ - uint16_t mustBeZero; - /** Characters 12 and 13 of the long-name sub-component in this entry. */ - uint16_t name3[LDIR_NAME3_DIM]; -} __attribute__((packed)); -/** Type name for longDirectoryEntry */ -typedef struct longDirectoryEntry ldir_t; -/** - * Ord mast that indicates the entry is the last long dir entry in a - * set of long dir entries. All valid sets of long dir entries must - * begin with an entry having this mask. - */ -const uint8_t LDIR_ORD_LAST_LONG_ENTRY = 0X40; -#endif // FatStructs_h diff --git a/avr/cores/megacommand/SdFat/FatLib/FatVolume.cpp b/avr/cores/megacommand/SdFat/FatLib/FatVolume.cpp index ace0862c8..9e673f685 100644 --- a/avr/cores/megacommand/SdFat/FatLib/FatVolume.cpp +++ b/avr/cores/megacommand/SdFat/FatLib/FatVolume.cpp @@ -1,5 +1,5 @@ /** - * Copyright (c) 20011-2017 Bill Greiman + * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -22,586 +22,24 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ -#include -#include "FatVolume.h" +#define DBG_FILE "FatVolume.cpp" +#include "../common/DebugMacros.h" +#include "FatLib.h" +FatVolume* FatVolume::m_cwv = nullptr; //------------------------------------------------------------------------------ -cache_t* FatCache::read(uint32_t lbn, uint8_t option) { - if (m_lbn != lbn) { - if (!sync()) { - DBG_FAIL_MACRO; - goto fail; - } - if (!(option & CACHE_OPTION_NO_READ)) { - if (!m_vol->readBlock(lbn, m_block.data)) { - DBG_FAIL_MACRO; - goto fail; - } - } - m_status = 0; - m_lbn = lbn; - } - m_status |= option & CACHE_STATUS_MASK; - return &m_block; - -fail: - - return 0; -} -//------------------------------------------------------------------------------ -bool FatCache::sync() { - if (m_status & CACHE_STATUS_DIRTY) { - if (!m_vol->writeBlock(m_lbn, m_block.data)) { - DBG_FAIL_MACRO; - goto fail; - } - // mirror second FAT - if (m_status & CACHE_STATUS_MIRROR_FAT) { - uint32_t lbn = m_lbn + m_vol->blocksPerFat(); - if (!m_vol->writeBlock(lbn, m_block.data)) { - DBG_FAIL_MACRO; - goto fail; - } - } - m_status &= ~CACHE_STATUS_DIRTY; - } - return true; - -fail: - return false; -} -//------------------------------------------------------------------------------ -bool FatVolume::allocateCluster(uint32_t current, uint32_t* next) { - uint32_t find = current ? current : m_allocSearchStart; - uint32_t start = find; - while (1) { - find++; - // If at end of FAT go to beginning of FAT. - if (find > m_lastCluster) { - find = 2; - } - uint32_t f; - int8_t fg = fatGet(find, &f); - if (fg < 0) { - DBG_FAIL_MACRO; - goto fail; - } - if (fg && f == 0) { - break; - } - if (find == start) { - // Can't find space checked all clusters. - DBG_FAIL_MACRO; - goto fail; - } - } - // mark end of chain - if (!fatPutEOC(find)) { - DBG_FAIL_MACRO; - goto fail; - } - if (current) { - // link clusters - if (!fatPut(current, find)) { - DBG_FAIL_MACRO; - goto fail; - } - } else { - // Remember place for search start. - m_allocSearchStart = find; - } - updateFreeClusterCount(-1); - *next = find; - return true; - -fail: - return false; -} -//------------------------------------------------------------------------------ -// find a contiguous group of clusters -bool FatVolume::allocContiguous(uint32_t count, uint32_t* firstCluster) { - // flag to save place to start next search - bool setStart = true; - // start of group - uint32_t bgnCluster; - // end of group - uint32_t endCluster; - // Start at cluster after last allocated cluster. - uint32_t startCluster = m_allocSearchStart; - endCluster = bgnCluster = startCluster + 1; - - // search the FAT for free clusters - while (1) { - // If past end - start from beginning of FAT. - if (endCluster > m_lastCluster) { - bgnCluster = endCluster = 2; - } - uint32_t f; - int8_t fg = fatGet(endCluster, &f); - if (fg < 0) { - DBG_FAIL_MACRO; - goto fail; - } - if (f || fg == 0) { - // cluster in use try next cluster as bgnCluster - bgnCluster = endCluster + 1; - - // don't update search start if unallocated clusters before endCluster. - if (bgnCluster != endCluster) { - setStart = false; - } - } else if ((endCluster - bgnCluster + 1) == count) { - // done - found space - break; - } - // Can't find space if all clusters checked. - if (startCluster == endCluster) { - DBG_FAIL_MACRO; - goto fail; - } - endCluster++; - } - // remember possible next free cluster - if (setStart) { - m_allocSearchStart = endCluster + 1; - } - - // mark end of chain - if (!fatPutEOC(endCluster)) { - DBG_FAIL_MACRO; - goto fail; - } - // link clusters - while (endCluster > bgnCluster) { - if (!fatPut(endCluster - 1, endCluster)) { - DBG_FAIL_MACRO; - goto fail; - } - endCluster--; - } - // Maintain count of free clusters. - updateFreeClusterCount(-count); - - // return first cluster number to caller - *firstCluster = bgnCluster; - return true; - -fail: - return false; -} -//------------------------------------------------------------------------------ -uint32_t FatVolume::clusterFirstBlock(uint32_t cluster) const { - return m_dataStartBlock + ((cluster - 2) << m_clusterSizeShift); -} -//------------------------------------------------------------------------------ -// Fetch a FAT entry - return -1 error, 0 EOC, else 1. -int8_t FatVolume::fatGet(uint32_t cluster, uint32_t* value) { - uint32_t lba; - uint32_t next; - cache_t* pc; - - // error if reserved cluster of beyond FAT - if (cluster < 2 || cluster > m_lastCluster) { +bool FatVolume::chdir(const char *path) { + FatFile dir; + if (!dir.open(vwd(), path, O_RDONLY)) { DBG_FAIL_MACRO; goto fail; } - - if (fatType() == 32) { - lba = m_fatStartBlock + (cluster >> 7); - pc = cacheFetchFat(lba, FatCache::CACHE_FOR_READ); - if (!pc) { - DBG_FAIL_MACRO; - goto fail; - } - next = pc->fat32[cluster & 0X7F] & FAT32MASK; - goto done; - } - if (fatType() == 16) { - lba = m_fatStartBlock + ((cluster >> 8) & 0XFF); - pc = cacheFetchFat(lba, FatCache::CACHE_FOR_READ); - if (!pc) { - DBG_FAIL_MACRO; - goto fail; - } - next = pc->fat16[cluster & 0XFF]; - goto done; - } - if (FAT12_SUPPORT && fatType() == 12) { - uint16_t index = cluster; - index += index >> 1; - lba = m_fatStartBlock + (index >> 9); - pc = cacheFetchFat(lba, FatCache::CACHE_FOR_READ); - if (!pc) { - DBG_FAIL_MACRO; - goto fail; - } - index &= 0X1FF; - uint16_t tmp = pc->data[index]; - index++; - if (index == 512) { - pc = cacheFetchFat(lba + 1, FatCache::CACHE_FOR_READ); - if (!pc) { - DBG_FAIL_MACRO; - goto fail; - } - index = 0; - } - tmp |= pc->data[index] << 8; - next = cluster & 1 ? tmp >> 4 : tmp & 0XFFF; - goto done; - } else { + if (!dir.isDir()) { DBG_FAIL_MACRO; goto fail; } -done: - if (isEOC(next)) { - return 0; - } - *value = next; - return 1; - -fail: - return -1; -} -//------------------------------------------------------------------------------ -// Store a FAT entry -bool FatVolume::fatPut(uint32_t cluster, uint32_t value) { - uint32_t lba; - cache_t* pc; - - // error if reserved cluster of beyond FAT - if (cluster < 2 || cluster > m_lastCluster) { - DBG_FAIL_MACRO; - goto fail; - } - - if (fatType() == 32) { - lba = m_fatStartBlock + (cluster >> 7); - pc = cacheFetchFat(lba, FatCache::CACHE_FOR_WRITE); - if (!pc) { - DBG_FAIL_MACRO; - goto fail; - } - pc->fat32[cluster & 0X7F] = value; - return true; - } - - if (fatType() == 16) { - lba = m_fatStartBlock + ((cluster >> 8) & 0XFF); - pc = cacheFetchFat(lba, FatCache::CACHE_FOR_WRITE); - if (!pc) { - DBG_FAIL_MACRO; - goto fail; - } - pc->fat16[cluster & 0XFF] = value; - return true; - } - - if (FAT12_SUPPORT && fatType() == 12) { - uint16_t index = cluster; - index += index >> 1; - lba = m_fatStartBlock + (index >> 9); - pc = cacheFetchFat(lba, FatCache::CACHE_FOR_WRITE); - if (!pc) { - DBG_FAIL_MACRO; - goto fail; - } - index &= 0X1FF; - uint8_t tmp = value; - if (cluster & 1) { - tmp = (pc->data[index] & 0XF) | tmp << 4; - } - pc->data[index] = tmp; - - index++; - if (index == 512) { - lba++; - index = 0; - pc = cacheFetchFat(lba, FatCache::CACHE_FOR_WRITE); - if (!pc) { - DBG_FAIL_MACRO; - goto fail; - } - } - tmp = value >> 4; - if (!(cluster & 1)) { - tmp = ((pc->data[index] & 0XF0)) | tmp >> 4; - } - pc->data[index] = tmp; - return true; - } else { - DBG_FAIL_MACRO; - goto fail; - } - -fail: - return false; -} -//------------------------------------------------------------------------------ -// free a cluster chain -bool FatVolume::freeChain(uint32_t cluster) { - uint32_t next; - int8_t fg; - do { - fg = fatGet(cluster, &next); - if (fg < 0) { - DBG_FAIL_MACRO; - goto fail; - } - // free cluster - if (!fatPut(cluster, 0)) { - DBG_FAIL_MACRO; - goto fail; - } - // Add one to count of free clusters. - updateFreeClusterCount(1); - - if (cluster < m_allocSearchStart) { - m_allocSearchStart = cluster; - } - cluster = next; - } while (fg); - - return true; - -fail: - return false; -} -//------------------------------------------------------------------------------ -int32_t FatVolume::freeClusterCount() { -#if MAINTAIN_FREE_CLUSTER_COUNT - if (m_freeClusterCount >= 0) { - return m_freeClusterCount; - } -#endif // MAINTAIN_FREE_CLUSTER_COUNT - uint32_t free = 0; - uint32_t lba; - uint32_t todo = m_lastCluster + 1; - uint16_t n; - - if (FAT12_SUPPORT && fatType() == 12) { - for (unsigned i = 2; i < todo; i++) { - uint32_t c; - int8_t fg = fatGet(i, &c); - if (fg < 0) { - DBG_FAIL_MACRO; - goto fail; - } - if (fg && c == 0) { - free++; - } - } - } else if (fatType() == 16 || fatType() == 32) { - lba = m_fatStartBlock; - while (todo) { - cache_t* pc = cacheFetchFat(lba++, FatCache::CACHE_FOR_READ); - if (!pc) { - DBG_FAIL_MACRO; - goto fail; - } - n = fatType() == 16 ? 256 : 128; - if (todo < n) { - n = todo; - } - if (fatType() == 16) { - for (uint16_t i = 0; i < n; i++) { - if (pc->fat16[i] == 0) { - free++; - } - } - } else { - for (uint16_t i = 0; i < n; i++) { - if (pc->fat32[i] == 0) { - free++; - } - } - } - todo -= n; - } - } else { - // invalid FAT type - DBG_FAIL_MACRO; - goto fail; - } - setFreeClusterCount(free); - return free; - -fail: - return -1; -} -//------------------------------------------------------------------------------ -bool FatVolume::init(uint8_t part) { - uint32_t clusterCount; - uint32_t totalBlocks; - uint32_t volumeStartBlock = 0; - fat32_boot_t* fbs; - cache_t* pc; - uint8_t tmp; - m_fatType = 0; - m_allocSearchStart = 1; - m_cache.init(this); -#if USE_SEPARATE_FAT_CACHE - m_fatCache.init(this); -#endif // USE_SEPARATE_FAT_CACHE - // 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) { - DBG_FAIL_MACRO; - goto fail; - } - pc = cacheFetchData(0, FatCache::CACHE_FOR_READ); - if (!pc) { - DBG_FAIL_MACRO; - goto fail; - } - part_t* p = &pc->mbr.part[part - 1]; - if ((p->boot & 0X7F) != 0 || p->firstSector == 0) { - // not a valid partition - DBG_FAIL_MACRO; - goto fail; - } - volumeStartBlock = p->firstSector; - } - pc = cacheFetchData(volumeStartBlock, FatCache::CACHE_FOR_READ); - if (!pc) { - DBG_FAIL_MACRO; - goto fail; - } - fbs = &(pc->fbs32); - if (fbs->bytesPerSector != 512 || - fbs->fatCount != 2 || - fbs->reservedSectorCount == 0) { - // not valid FAT volume - DBG_FAIL_MACRO; - goto fail; - } - m_blocksPerCluster = fbs->sectorsPerCluster; - m_clusterBlockMask = m_blocksPerCluster - 1; - // determine shift that is same as multiply by m_blocksPerCluster - m_clusterSizeShift = 0; - for (tmp = 1; m_blocksPerCluster != tmp; tmp <<= 1, m_clusterSizeShift++) { - if (tmp == 0) { - DBG_FAIL_MACRO; - goto fail; - } - } - m_blocksPerFat = fbs->sectorsPerFat16 ? - fbs->sectorsPerFat16 : fbs->sectorsPerFat32; - - m_fatStartBlock = volumeStartBlock + fbs->reservedSectorCount; - - // count for FAT16 zero for FAT32 - m_rootDirEntryCount = fbs->rootDirEntryCount; - - // directory start for FAT16 dataStart for FAT32 - m_rootDirStart = m_fatStartBlock + 2 * m_blocksPerFat; - // data start for FAT16 and FAT32 - m_dataStartBlock = m_rootDirStart + ((32 * fbs->rootDirEntryCount + 511)/512); - - // total blocks for FAT16 or FAT32 - totalBlocks = fbs->totalSectors16 ? - fbs->totalSectors16 : fbs->totalSectors32; - // total data blocks - clusterCount = totalBlocks - (m_dataStartBlock - volumeStartBlock); - - // divide by cluster size to get cluster count - clusterCount >>= m_clusterSizeShift; - m_lastCluster = clusterCount + 1; - - // Indicate unknown number of free clusters. - setFreeClusterCount(-1); - // FAT type is determined by cluster count - if (clusterCount < 4085) { - m_fatType = 12; - if (!FAT12_SUPPORT) { - DBG_FAIL_MACRO; - goto fail; - } - } else if (clusterCount < 65525) { - m_fatType = 16; - } else { - m_rootDirStart = fbs->fat32RootCluster; - m_fatType = 32; - } - return true; - -fail: - return false; -} -//------------------------------------------------------------------------------ -bool FatVolume::wipe(print_t* pr) { - cache_t* cache; - uint16_t count; - uint32_t lbn; - if (!fatType()) { - DBG_FAIL_MACRO; - goto fail; - } - cache = cacheClear(); - if (!cache) { - DBG_FAIL_MACRO; - goto fail; - } - memset(cache->data, 0, 512); - // Zero root. - if (fatType() == 32) { - lbn = clusterFirstBlock(m_rootDirStart); - count = m_blocksPerCluster; - } else { - lbn = m_rootDirStart; - count = m_rootDirEntryCount/16; - } - for (uint32_t n = 0; n < count; n++) { - if (!writeBlock(lbn + n, cache->data)) { - DBG_FAIL_MACRO; - goto fail; - } - } - // Clear FATs. - count = 2*m_blocksPerFat; - lbn = m_fatStartBlock; - for (uint32_t nb = 0; nb < count; nb++) { - if (pr && (nb & 0XFF) == 0) { - pr->write('.'); - } - if (!writeBlock(lbn + nb, cache->data)) { - DBG_FAIL_MACRO; - goto fail; - } - } - // Reserve first two clusters. - if (fatType() == 32) { - cache->fat32[0] = 0x0FFFFFF8; - cache->fat32[1] = 0x0FFFFFFF; - } else if (fatType() == 16) { - cache->fat16[0] = 0XFFF8; - cache->fat16[1] = 0XFFFF; - } else if (FAT12_SUPPORT && fatType() == 12) { - cache->fat32[0] = 0XFFFFF8; - } else { - DBG_FAIL_MACRO; - goto fail; - } - if (!writeBlock(m_fatStartBlock, cache->data) || - !writeBlock(m_fatStartBlock + m_blocksPerFat, cache->data)) { - DBG_FAIL_MACRO; - goto fail; - } - if (fatType() == 32) { - // Reserve root cluster. - if (!fatPutEOC(m_rootDirStart) || !cacheSync()) { - DBG_FAIL_MACRO; - goto fail; - } - } - if (pr) { - pr->write('\r'); - pr->write('\n'); - } - m_fatType = 0; + m_vwd = dir; return true; -fail: - m_fatType = 0; + fail: return false; } diff --git a/avr/cores/megacommand/SdFat/FatLib/FatVolume.h b/avr/cores/megacommand/SdFat/FatLib/FatVolume.h index a1fc70d5b..4c950b10c 100644 --- a/avr/cores/megacommand/SdFat/FatLib/FatVolume.h +++ b/avr/cores/megacommand/SdFat/FatLib/FatVolume.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 20011-2017 Bill Greiman + * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -24,352 +24,336 @@ */ #ifndef FatVolume_h #define FatVolume_h +#include "FatFile.h" /** * \file * \brief FatVolume class */ -#include -#include "FatLibConfig.h" -#include "FatStructs.h" -#include "BlockDriver.h" //------------------------------------------------------------------------------ -#ifndef DOXYGEN_SHOULD_SKIP_THIS -/** Macro for debug. */ -#define DEBUG_MODE 0 -#if DEBUG_MODE -#define DBG_FAIL_MACRO Serial.print(F(__FILE__)); Serial.println(__LINE__); -#define DBG_PRINT_IF(b) if (b) {Serial.println(F(#b)); DBG_FAIL_MACRO;} -#define DBG_HALT_IF(b) if (b) {Serial.println(F(#b));\ - DBG_FAIL_MACRO; while (1);} -#else // DEBUG_MODE -#define DBG_FAIL_MACRO -#define DBG_PRINT_IF(b) -#define DBG_HALT_IF(b) -#endif // DEBUG_MODE -#endif // DOXYGEN_SHOULD_SKIP_THIS -//------------------------------------------------------------------------------ -#if ENABLE_ARDUINO_FEATURES -/** Use Print for Arduino */ -typedef Print print_t; -#else // ENABLE_ARDUINO_FEATURES -/** - * \class CharWriter - * \brief Character output - often serial port. - */ -class CharWriter { - public: - virtual size_t write(char c) = 0; - virtual size_t write(const char* s) = 0; -}; -typedef CharWriter print_t; -#endif // ENABLE_ARDUINO_FEATURES -//------------------------------------------------------------------------------ -// Forward declaration of FatVolume. -class FatVolume; -//------------------------------------------------------------------------------ -/** - * \brief Cache for an raw 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; -}; -//============================================================================== -/** - * \class FatCache - * \brief Block cache. - */ -class FatCache { - public: - /** Cached block is dirty */ - static const uint8_t CACHE_STATUS_DIRTY = 1; - /** Cashed block is FAT entry and must be mirrored in second FAT. */ - static const uint8_t CACHE_STATUS_MIRROR_FAT = 2; - /** Cache block status bits */ - static const uint8_t CACHE_STATUS_MASK - = CACHE_STATUS_DIRTY | CACHE_STATUS_MIRROR_FAT; - /** Sync existing block but do not read new block. */ - static const uint8_t CACHE_OPTION_NO_READ = 4; - /** Cache block for read. */ - static const uint8_t CACHE_FOR_READ = 0; - /** Cache block for write. */ - static const uint8_t CACHE_FOR_WRITE = CACHE_STATUS_DIRTY; - /** Reserve cache block for write - do not read from block device. */ - static const uint8_t CACHE_RESERVE_FOR_WRITE - = CACHE_STATUS_DIRTY | CACHE_OPTION_NO_READ; - /** \return Cache block address. */ - cache_t* block() { - return &m_block; - } - /** Set current block dirty. */ - void dirty() { - m_status |= CACHE_STATUS_DIRTY; - } - /** Initialize the cache. - * \param[in] vol FatVolume that owns this FatCache. - */ - void init(FatVolume *vol) { - m_vol = vol; - invalidate(); - } - /** Invalidate current cache block. */ - void invalidate() { - m_status = 0; - m_lbn = 0XFFFFFFFF; - } - /** \return dirty status */ - bool isDirty() { - return m_status & CACHE_STATUS_DIRTY; - } - /** \return Logical block number for cached block. */ - uint32_t lbn() { - return m_lbn; - } - /** Read a block into the cache. - * \param[in] lbn Block to read. - * \param[in] option mode for cached block. - * \return Address of cached block. */ - cache_t* read(uint32_t lbn, uint8_t option); - /** Write current block if dirty. - * \return true for success else false. - */ - bool sync(); - - private: - uint8_t m_status; - FatVolume* m_vol; - uint32_t m_lbn; - cache_t m_block; -}; -//============================================================================== /** * \class FatVolume - * \brief Access FAT16 and FAT32 volumes on raw file devices. + * \brief Integration class for the FatLib library. */ -class FatVolume { +class FatVolume : public FatPartition { public: - /** Create an instance of FatVolume + /** Get file's user settable attributes. + * \param[in] path path to file. + * \return user settable file attributes for success else -1. */ - FatVolume() : m_fatType(0) {} - - /** \return The volume's cluster size in blocks. */ - uint8_t blocksPerCluster() const { - return m_blocksPerCluster; - } - /** \return The number of blocks in one FAT. */ - uint32_t blocksPerFat() const { - return m_blocksPerFat; - } - /** Clear the cache and returns a pointer to the cache. Not for normal apps. - * \return A pointer to the cache buffer or zero if an error occurs. + int attrib(const char* path) { + File32 tmpFile; + return tmpFile.open(this, path, O_RDONLY) ? tmpFile.attrib() : -1; + } + /** Set file's user settable attributes. + * \param[in] path path to file. + * \param[in] bits bit-wise or of selected attributes: FS_ATTRIB_READ_ONLY, + * FS_ATTRIB_HIDDEN, FS_ATTRIB_SYSTEM, FS_ATTRIB_ARCHIVE. + * + * \return true for success or false for failure. + */ + bool attrib(const char* path, uint8_t bits) { + File32 tmpFile; + return tmpFile.open(this, path, O_RDONLY) ? tmpFile.attrib(bits) : false; + } + /** + * Initialize an FatVolume object. + * \param[in] dev Device block driver. + * \param[in] setCwv Set current working volume if true. + * \param[in] part partition to initialize. + * \param[in] volStart Start sector of volume if part is zero. + * \return true for success or false for failure. */ - cache_t* cacheClear() { - if (!cacheSync()) { - return 0; + bool begin(FsBlockDevice* dev, bool setCwv = true, + uint8_t part = 1, uint32_t volStart = 0) { + if (!init(dev, part, volStart)) { + return false; } - m_cache.invalidate(); - return m_cache.block(); - } - /** \return The total number of clusters in the volume. */ - uint32_t clusterCount() const { - return m_lastCluster - 1; - } - /** \return The shift count required to multiply by blocksPerCluster. */ - uint8_t clusterSizeShift() const { - return m_clusterSizeShift; - } - /** \return The logical block number for the start of file data. */ - uint32_t dataStartBlock() const { - return m_dataStartBlock; - } - /** \return The number of File Allocation Tables. */ - uint8_t fatCount() { - return 2; + if (!chdir()) { + return false; + } + if (setCwv || !m_cwv) { + m_cwv = this; + } + return true; } - /** \return The logical block number for the start of the first FAT. */ - uint32_t fatStartBlock() const { - return m_fatStartBlock; + /** Change global current working volume to this volume. */ + void chvol() {m_cwv = this;} + + /** + * Set volume working directory to root. + * \return true for success or false for failure. + */ + bool chdir() { + m_vwd.close(); + return m_vwd.openRoot(this); + } + /** + * Set volume working directory. + * \param[in] path Path for volume working directory. + * \return true for success or false for failure. + */ + bool chdir(const char *path); + //---------------------------------------------------------------------------- + /** + * Test for the existence of a file. + * + * \param[in] path Path of the file to be tested for. + * + * \return true if the file exists else false. + */ + bool exists(const char* path) { + FatFile tmp; + return tmp.open(this, path, O_RDONLY); } - /** \return The FAT type of the volume. Values are 12, 16 or 32. */ - uint8_t fatType() const { - return m_fatType; + //---------------------------------------------------------------------------- + /** List the directory contents of the volume root directory. + * + * \param[in] pr Print stream for list. + * + * \param[in] flags The inclusive OR of + * + * LS_DATE - %Print file modification date + * + * LS_SIZE - %Print file size. + * + * LS_R - Recursive list of subdirectories. + * + * \return true for success or false for failure. + */ + bool ls(print_t* pr, uint8_t flags = 0) { + return m_vwd.ls(pr, flags); } - /** Volume free space in clusters. + //---------------------------------------------------------------------------- + /** List the contents of a directory. + * + * \param[in] pr Print stream for list. + * + * \param[in] path directory to list. * - * \return Count of free clusters for success or -1 if an error occurs. + * \param[in] flags The inclusive OR of + * + * LS_DATE - %Print file modification date + * + * LS_SIZE - %Print file size. + * + * LS_R - Recursive list of subdirectories. + * + * \return true for success or false for failure. */ - int32_t freeClusterCount(); - /** Initialize a FAT volume. Try partition one first then try super - * floppy format. + bool ls(print_t* pr, const char* path, uint8_t flags) { + FatFile dir; + return dir.open(this, path, O_RDONLY) && dir.ls(pr, flags); + } + //---------------------------------------------------------------------------- + /** Make a subdirectory in the volume root directory. + * + * \param[in] path A path with a valid name for the subdirectory. * - * \return The value true is returned for success and - * the value false is returned for failure. + * \param[in] pFlag Create missing parent directories if true. + * + * \return true for success or false for failure. */ - bool init() { - return init(1) || init(0); + bool mkdir(const char* path, bool pFlag = true) { + FatFile sub; + return sub.mkdir(vwd(), path, pFlag); } - /** Initialize a FAT volume. - - * \param[in] part The partition to be used. Legal values for \a part are - * 1-4 to use the corresponding partition on a device formatted with - * a MBR, Master Boot Record, or zero if the device is formatted as - * a super floppy with the FAT boot sector in block zero. + //---------------------------------------------------------------------------- + /** open a file * - * \return The value true is returned for success and - * the value false is returned for failure. + * \param[in] path location of file to be opened. + * \param[in] oflag open flags. + * \return a File32 object. */ - bool init(uint8_t part); - /** \return The number of entries in the root directory for FAT16 volumes. */ - uint16_t rootDirEntryCount() const { - return m_rootDirEntryCount; + File32 open(const char *path, oflag_t oflag = O_RDONLY) { + File32 tmpFile; + tmpFile.open(this, path, oflag); + return tmpFile; } - /** \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 m_rootDirStart; + //---------------------------------------------------------------------------- + /** Remove a file from the volume root directory. + * + * \param[in] path A path with a valid name for the file. + * + * \return true for success or false for failure. + */ + bool remove(const char* path) { + FatFile tmp; + return tmp.open(this, path, O_WRONLY) && tmp.remove(); } - /** \return The number of blocks in the volume */ - uint32_t volumeBlockCount() const { - return blocksPerCluster()*clusterCount(); + //---------------------------------------------------------------------------- + /** Rename a file or subdirectory. + * + * \param[in] oldPath Path name to the file or subdirectory to be renamed. + * + * \param[in] newPath New path name of the file or subdirectory. + * + * The \a newPath object must not exist before the rename call. + * + * The file to be renamed must not be open. The directory entry may be + * moved and file system corruption could occur if the file is accessed by + * a file object that was opened before the rename() call. + * + * \return true for success or false for failure. + */ + bool rename(const char *oldPath, const char *newPath) { + FatFile file; + return file.open(vwd(), oldPath, O_RDONLY) && file.rename(vwd(), newPath); } - /** Wipe all data from the volume. - * \param[in] pr print stream for status dots. - * \return true for success else false. + //---------------------------------------------------------------------------- + /** Remove a subdirectory from the volume's working directory. + * + * \param[in] path A path with a valid name for the subdirectory. + * + * The subdirectory file will be removed only if it is empty. + * + * \return true for success or false for failure. */ - bool wipe(print_t* pr = 0); - /** Debug access to FAT table + bool rmdir(const char* path) { + FatFile sub; + return sub.open(this, path, O_RDONLY) && sub.rmdir(); + } + //---------------------------------------------------------------------------- + /** Truncate a file to a specified length. The current file position + * will be at the new EOF. * - * \param[in] n cluster number. - * \param[out] v value of entry - * \return true for success or false for failure + * \param[in] path A path with a valid name for the file. + * \param[in] length The desired length for the file. + * + * \return true for success or false for failure. */ - int8_t dbgFat(uint32_t n, uint32_t* v) { - return fatGet(n, v); + bool truncate(const char* path, uint32_t length) { + FatFile file; + return file.open(this, path, O_WRONLY) && file.truncate(length); } -//------------------------------------------------------------------------------ - private: - // Allow FatFile and FatCache access to FatVolume private functions. - friend class FatCache; - friend class FatFile; - friend class FatFileSystem; -//------------------------------------------------------------------------------ - BlockDriver* m_blockDev; // block device - uint8_t m_blocksPerCluster; // Cluster size in blocks. - uint8_t m_clusterBlockMask; // Mask to extract block of cluster. - uint8_t m_clusterSizeShift; // Cluster count to block count shift. - uint8_t m_fatType; // Volume type (12, 16, OR 32). - uint16_t m_rootDirEntryCount; // Number of entries in FAT16 root dir. - uint32_t m_allocSearchStart; // Start cluster for alloc search. - uint32_t m_blocksPerFat; // FAT size in blocks - uint32_t m_dataStartBlock; // First data block number. - uint32_t m_fatStartBlock; // Start block for first FAT. - uint32_t m_lastCluster; // Last cluster number in FAT. - uint32_t m_rootDirStart; // Start block for FAT16, cluster for FAT32. -//------------------------------------------------------------------------------ - // block I/O functions. - bool readBlock(uint32_t block, uint8_t* dst) { - return m_blockDev->readBlock(block, dst); +#if ENABLE_ARDUINO_SERIAL + /** List the directory contents of the root directory to Serial. + * + * \param[in] flags The inclusive OR of + * + * LS_DATE - %Print file modification date + * + * LS_SIZE - %Print file size. + * + * LS_R - Recursive list of subdirectories. + * + * \return true for success or false for failure. + */ + bool ls(uint8_t flags = 0) { + return ls(&Serial, flags); } - bool syncBlocks() { - return m_blockDev->syncBlocks(); + /** List the directory contents of a directory to Serial. + * + * \param[in] path directory to list. + * + * \param[in] flags The inclusive OR of + * + * LS_DATE - %Print file modification date + * + * LS_SIZE - %Print file size. + * + * LS_R - Recursive list of subdirectories. + * + * \return true for success or false for failure. + */ + bool ls(const char* path, uint8_t flags = 0) { + return ls(&Serial, path, flags); + } +#endif // ENABLE_ARDUINO_SERIAL +#if ENABLE_ARDUINO_STRING + //---------------------------------------------------------------------------- + /** + * Set volume working directory. + * \param[in] path Path for volume working directory. + * \return true for success or false for failure. + */ + bool chdir(const String& path) { + return chdir(path.c_str()); } - bool writeBlock(uint32_t block, const uint8_t* src) { - return m_blockDev->writeBlock(block, src); + /** + * Test for the existence of a file. + * + * \param[in] path Path of the file to be tested for. + * + * \return true if the file exists else false. + */ + bool exists(const String& path) { + return exists(path.c_str()); } -#if USE_MULTI_BLOCK_IO - bool readBlocks(uint32_t block, uint8_t* dst, size_t nb) { - return m_blockDev->readBlocks(block, dst, nb); + /** Make a subdirectory in the volume root directory. + * + * \param[in] path A path with a valid name for the subdirectory. + * + * \param[in] pFlag Create missing parent directories if true. + * + * \return true for success or false for failure. + */ + bool mkdir(const String& path, bool pFlag = true) { + return mkdir(path.c_str(), pFlag); } - bool writeBlocks(uint32_t block, const uint8_t* src, size_t nb) { - return m_blockDev->writeBlocks(block, src, nb); + /** open a file + * + * \param[in] path location of file to be opened. + * \param[in] oflag open flags. + * \return a File32 object. + */ + File32 open(const String& path, oflag_t oflag = O_RDONLY) { + return open(path.c_str(), oflag ); } -#endif // USE_MULTI_BLOCK_IO -#if MAINTAIN_FREE_CLUSTER_COUNT - int32_t m_freeClusterCount; // Count of free clusters in volume. - void setFreeClusterCount(int32_t value) { - m_freeClusterCount = value; + /** Remove a file from the volume root directory. + * + * \param[in] path A path with a valid name for the file. + * + * \return true for success or false for failure. + */ + bool remove(const String& path) { + return remove(path.c_str()); } - void updateFreeClusterCount(int32_t change) { - if (m_freeClusterCount >= 0) { - m_freeClusterCount += change; - } + /** Rename a file or subdirectory. + * + * \param[in] oldPath Path name to the file or subdirectory to be renamed. + * + * \param[in] newPath New path name of the file or subdirectory. + * + * The \a newPath object must not exist before the rename call. + * + * The file to be renamed must not be open. The directory entry may be + * moved and file system corruption could occur if the file is accessed by + * a file object that was opened before the rename() call. + * + * \return true for success or false for failure. + */ + bool rename(const String& oldPath, const String& newPath) { + return rename(oldPath.c_str(), newPath.c_str()); } -#else // MAINTAIN_FREE_CLUSTER_COUNT - void setFreeClusterCount(int32_t value) { - (void)value; + /** Remove a subdirectory from the volume's working directory. + * + * \param[in] path A path with a valid name for the subdirectory. + * + * The subdirectory file will be removed only if it is empty. + * + * \return true for success or false for failure. + */ + bool rmdir(const String& path) { + return rmdir(path.c_str()); } - void updateFreeClusterCount(int32_t change) { - (void)change; + /** Truncate a file to a specified length. The current file position + * will be at the new EOF. + * + * \param[in] path A path with a valid name for the file. + * \param[in] length The desired length for the file. + * + * \return true for success or false for failure. + */ + bool truncate(const String& path, uint32_t length) { + return truncate(path.c_str(), length); } -#endif // MAINTAIN_FREE_CLUSTER_COUNT +#endif // ENABLE_ARDUINO_STRING -// block caches - FatCache m_cache; -#if USE_SEPARATE_FAT_CACHE - FatCache m_fatCache; - cache_t* cacheFetchFat(uint32_t blockNumber, uint8_t options) { - return m_fatCache.read(blockNumber, - options | FatCache::CACHE_STATUS_MIRROR_FAT); - } - bool cacheSync() { - return m_cache.sync() && m_fatCache.sync() && syncBlocks(); - } -#else // - cache_t* cacheFetchFat(uint32_t blockNumber, uint8_t options) { - return cacheFetchData(blockNumber, - options | FatCache::CACHE_STATUS_MIRROR_FAT); - } - bool cacheSync() { - return m_cache.sync() && syncBlocks(); - } -#endif // USE_SEPARATE_FAT_CACHE - cache_t* cacheFetchData(uint32_t blockNumber, uint8_t options) { - return m_cache.read(blockNumber, options); - } - void cacheInvalidate() { - m_cache.invalidate(); - } - bool cacheSyncData() { - return m_cache.sync(); - } - cache_t *cacheAddress() { - return m_cache.block(); - } - uint32_t cacheBlockNumber() { - return m_cache.lbn(); - } - void cacheDirty() { - m_cache.dirty(); - } -//------------------------------------------------------------------------------ - bool allocateCluster(uint32_t current, uint32_t* next); - bool allocContiguous(uint32_t count, uint32_t* firstCluster); - uint8_t blockOfCluster(uint32_t position) const { - return (position >> 9) & m_clusterBlockMask; - } - uint32_t clusterFirstBlock(uint32_t cluster) const; - int8_t 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 freeChain(uint32_t cluster); - bool isEOC(uint32_t cluster) const { - return cluster > m_lastCluster; - } + private: + friend FatFile; + static FatVolume* cwv() {return m_cwv;} + FatFile* vwd() {return &m_vwd;} + static FatVolume* m_cwv; + FatFile m_vwd; }; -#endif // FatVolume +#endif // FatVolume_h diff --git a/avr/cores/megacommand/SdFat/FreeStack.cpp b/avr/cores/megacommand/SdFat/FreeStack.cpp new file mode 100644 index 000000000..d90959e93 --- /dev/null +++ b/avr/cores/megacommand/SdFat/FreeStack.cpp @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#define FREE_STACK_CPP +#include "FreeStack.h" +#if defined(HAS_UNUSED_STACK) && HAS_UNUSED_STACK +//------------------------------------------------------------------------------ +inline char* stackBegin() { +#if defined(__AVR__) + return __brkval ? __brkval : &__bss_end; +#elif defined(__IMXRT1062__) + return reinterpret_cast(&_ebss); +#elif defined(__arm__) + return reinterpret_cast(sbrk(0)); +#else // defined(__AVR__) +#error "undefined stackBegin" +#endif // defined(__AVR__) +} +//------------------------------------------------------------------------------ +inline char* stackPointer() { +#if defined(__AVR__) + return reinterpret_cast(SP); +#elif defined(__arm__) + register uint32_t sp asm("sp"); + return reinterpret_cast(sp); +#else // defined(__AVR__) +#error "undefined stackPointer" +#endif // defined(__AVR__) +} +//------------------------------------------------------------------------------ +/** Stack fill pattern. */ +const char FILL = 0x55; +void FillStack() { + char* p = stackBegin(); + char* top = stackPointer(); + while (p < top) { + *p++ = FILL; + } +} +//------------------------------------------------------------------------------ +// May fail if malloc or new is used. +int UnusedStack() { + char* h = stackBegin(); + char* top = stackPointer(); + int n; + + for (n = 0; (h + n) < top; n++) { + if (h[n] != FILL) { + if (n >= 16) { + break; + } + // Attempt to skip used heap. + h += n; + n = 0; + } + } + return n; +} +#endif // defined(HAS_UNUSED_STACK) && HAS_UNUSED_STACK diff --git a/avr/cores/megacommand/SdFat/FreeStack.h b/avr/cores/megacommand/SdFat/FreeStack.h index 1f563e78f..c37da898d 100644 --- a/avr/cores/megacommand/SdFat/FreeStack.h +++ b/avr/cores/megacommand/SdFat/FreeStack.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 20011-2017 Bill Greiman + * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -28,7 +28,11 @@ * \file * \brief FreeStack() function. */ +#include #if defined(__AVR__) || defined(DOXYGEN) +#include +/** Indicate FillStack() and UnusedStack() are available. */ +#define HAS_UNUSED_STACK 1 /** boundary between stack and heap. */ extern char *__brkval; /** End of bss section.*/ @@ -36,26 +40,55 @@ extern char __bss_end; /** Amount of free stack space. * \return The number of free bytes. */ -static int FreeStack() { +inline int FreeStack() { char* sp = reinterpret_cast(SP); return __brkval ? sp - __brkval : sp - &__bss_end; -// char top; -// return __brkval ? &top - __brkval : &top - &__bss_end; } +#elif defined(ARDUINO_ARCH_APOLLO3) +#define HAS_UNUSED_STACK 0 #elif defined(PLATFORM_ID) // Particle board -static int FreeStack() { +#include "Arduino.h" +inline int FreeStack() { return System.freeMemory(); } +#elif defined(__IMXRT1062__) +#define HAS_UNUSED_STACK 1 +extern uint8_t _ebss; +inline int FreeStack() { + register uint32_t sp asm("sp"); + return reinterpret_cast(sp) - reinterpret_cast(&_ebss); +} #elif defined(__arm__) +#define HAS_UNUSED_STACK 1 extern "C" char* sbrk(int incr); -static int FreeStack() { - char top = 't'; - return &top - reinterpret_cast(sbrk(0)); +inline int FreeStack() { + register uint32_t sp asm("sp"); + return reinterpret_cast(sp) - reinterpret_cast(sbrk(0)); } -#else +#else // defined(__AVR__) || defined(DOXYGEN) +#ifndef FREE_STACK_CPP #warning FreeStack is not defined for this system. -static int FreeStack() { +#endif // FREE_STACK_CPP +inline int FreeStack() { return 0; } -#endif +#endif // defined(__AVR__) || defined(DOXYGEN) +#if defined(HAS_UNUSED_STACK) || defined(DOXYGEN) +/** Fill stack with 0x55 pattern */ +void FillStack(); +/** + * Determine the amount of unused stack. + * + * FillStack() must be called to fill the stack with a 0x55 pattern. + * + * UnusedStack() may fail if malloc() or new is use. + * + * \return number of bytes with 0x55 pattern. + */ +int UnusedStack(); +#else // HAS_UNUSED_STACK +#define HAS_UNUSED_STACK 0 +inline void FillStack() {} +inline int UnusedStack() {return 0;} +#endif // defined(HAS_UNUSED_STACK) #endif // FreeStack_h diff --git a/avr/cores/megacommand/SdFat/FsLib/FsFile.cpp b/avr/cores/megacommand/SdFat/FsLib/FsFile.cpp new file mode 100644 index 000000000..3c813274c --- /dev/null +++ b/avr/cores/megacommand/SdFat/FsLib/FsFile.cpp @@ -0,0 +1,219 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "FsLib.h" +//------------------------------------------------------------------------------ +FsBaseFile::FsBaseFile(const FsBaseFile& from) { + m_fFile = nullptr; + m_xFile = nullptr; + if (from.m_fFile) { + m_fFile = new (m_fileMem) FatFile; + *m_fFile = *from.m_fFile; + } else if (from.m_xFile) { + m_xFile = new (m_fileMem) ExFatFile; + *m_xFile = *from.m_xFile; + } +} +//------------------------------------------------------------------------------ +FsBaseFile& FsBaseFile::operator=(const FsBaseFile& from) { + if (this == &from) {return *this;} + close(); + if (from.m_fFile) { + m_fFile = new (m_fileMem) FatFile; + *m_fFile = *from.m_fFile; + } else if (from.m_xFile) { + m_xFile = new (m_fileMem) ExFatFile; + *m_xFile = *from.m_xFile; + } + return *this; +} +//------------------------------------------------------------------------------ +bool FsBaseFile::close() { + bool rtn = m_fFile ? m_fFile->close() : m_xFile ? m_xFile->close() : true; + m_fFile = nullptr; + m_xFile = nullptr; + return rtn; +} +//------------------------------------------------------------------------------ +bool FsBaseFile::mkdir(FsBaseFile* dir, const char* path, bool pFlag) { + close(); + if (dir->m_fFile) { + m_fFile = new (m_fileMem) FatFile; + if (m_fFile->mkdir(dir->m_fFile, path, pFlag)) { + return true; + } + m_fFile = nullptr; + } else if (dir->m_xFile) { + m_xFile = new (m_fileMem) ExFatFile; + if (m_xFile->mkdir(dir->m_xFile, path, pFlag)) { + return true; + } + m_xFile = nullptr; + } + return false; +} +//------------------------------------------------------------------------------ +bool FsBaseFile::open(FsVolume* vol, const char* path, oflag_t oflag) { + if (!vol) { + return false; + } + close(); + if (vol->m_fVol) { + m_fFile = new (m_fileMem) FatFile; + if (m_fFile && m_fFile->open(vol->m_fVol, path, oflag)) { + return true; + } + m_fFile = nullptr; + } else if (vol->m_xVol) { + m_xFile = new (m_fileMem) ExFatFile; + if (m_xFile && m_xFile->open(vol->m_xVol, path, oflag)) { + return true; + } + m_xFile = nullptr; + } + return false; +} +//------------------------------------------------------------------------------ +bool FsBaseFile::open(FsBaseFile* dir, const char* path, oflag_t oflag) { + close(); + if (dir->m_fFile) { + m_fFile = new (m_fileMem) FatFile; + if (m_fFile->open(dir->m_fFile, path, oflag)) { + return true; + } + m_fFile = nullptr; + } else if (dir->m_xFile) { + m_xFile = new (m_fileMem) ExFatFile; + if (m_xFile->open(dir->m_xFile, path, oflag)) { + return true; + } + m_xFile = nullptr; + } + return false; +} +//------------------------------------------------------------------------------ +bool FsBaseFile::open(FsBaseFile* dir, uint32_t index, oflag_t oflag) { + close(); + if (dir->m_fFile) { + m_fFile = new (m_fileMem) FatFile; + if (m_fFile->open(dir->m_fFile, index, oflag)) { + return true; + } + m_fFile = nullptr; + } else if (dir->m_xFile) { + m_xFile = new (m_fileMem) ExFatFile; + if (m_xFile->open(dir->m_xFile, index, oflag)) { + return true; + } + m_xFile = nullptr; + } + return false; +} +//------------------------------------------------------------------------------ +bool FsBaseFile::openCwd() { + close(); + if (FsVolume::m_cwv && FsVolume::m_cwv->m_fVol) { + m_fFile = new (m_fileMem) FatFile; + if (m_fFile->openCwd()) { + return true; + } + m_fFile = nullptr; + } else if (FsVolume::m_cwv && FsVolume::m_cwv->m_xVol) { + m_xFile = new (m_fileMem) ExFatFile; + if (m_xFile->openCwd()) { + return true; + } + m_xFile = nullptr; + } + return false; +} +//------------------------------------------------------------------------------ +bool FsBaseFile::openNext(FsBaseFile* dir, oflag_t oflag) { + close(); + if (dir->m_fFile) { + m_fFile = new (m_fileMem) FatFile; + if (m_fFile->openNext(dir->m_fFile, oflag)) { + return true; + } + m_fFile = nullptr; + } else if (dir->m_xFile) { + m_xFile = new (m_fileMem) ExFatFile; + if (m_xFile->openNext(dir->m_xFile, oflag)) { + return true; + } + m_xFile = nullptr; + } + return false; +} +//------------------------------------------------------------------------------ +bool FsBaseFile::openRoot(FsVolume* vol) { + if (!vol) { + return false; + } + close(); + if (vol->m_fVol) { + m_fFile = new (m_fileMem) FatFile; + if (m_fFile && m_fFile->openRoot(vol->m_fVol)) { + return true; + } + m_fFile = nullptr; + } else if (vol->m_xVol) { + m_xFile = new (m_fileMem) ExFatFile; + if (m_xFile && m_xFile->openRoot(vol->m_xVol)) { + return true; + } + m_xFile = nullptr; + } + return false; +} +//------------------------------------------------------------------------------ +bool FsBaseFile::remove() { + if (m_fFile) { + if (m_fFile->remove()) { + m_fFile = nullptr; + return true; + } + } else if (m_xFile) { + if (m_xFile->remove()) { + m_xFile = nullptr; + return true; + } + } + return false; +} +//------------------------------------------------------------------------------ +bool FsBaseFile::rmdir() { + if (m_fFile) { + if (m_fFile->rmdir()) { + m_fFile = nullptr; + return true; + } + } else if (m_xFile) { + if (m_xFile->rmdir()) { + m_xFile = nullptr; + return true; + } + } + return false; +} diff --git a/avr/cores/megacommand/SdFat/FsLib/FsFile.h b/avr/cores/megacommand/SdFat/FsLib/FsFile.h new file mode 100644 index 000000000..608160654 --- /dev/null +++ b/avr/cores/megacommand/SdFat/FsLib/FsFile.h @@ -0,0 +1,858 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef FsFile_h +#define FsFile_h +/** + * \file + * \brief FsBaseFile include file. + */ +#include "FsNew.h" +#include "FatLib/FatLib.h" +#include "ExFatLib/ExFatLib.h" +/** + * \class FsBaseFile + * \brief FsBaseFile class. + */ +class FsBaseFile { + public: + /** Create an instance. */ + FsBaseFile() {} + /** Create a file object and open it in the current working directory. + * + * \param[in] path A path for a file to be opened. + * + * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive + * OR of open flags. see FatFile::open(FatFile*, const char*, uint8_t). + */ + FsBaseFile(const char* path, oflag_t oflag) { + open(path, oflag); + } + + ~FsBaseFile() {close();} + /** Copy constructor. + * + * \param[in] from Object used to initialize this instance. + */ + FsBaseFile(const FsBaseFile& from); + /** Copy assignment operator + * \param[in] from Object used to initialize this instance. + * \return assigned object. + */ + FsBaseFile& operator=(const FsBaseFile& from); + /** The parenthesis operator. + * + * \return true if a file is open. + */ + operator bool() const {return isOpen();} + /** + * \return user settable file attributes for success else -1. + */ + int attrib() { + return m_fFile ? m_fFile->attrib() : + m_xFile ? m_xFile->attrib() : -1; + } + /** Set file attributes + * + * \param[in] bits bit-wise or of selected attributes: FS_ATTRIB_READ_ONLY, + * FS_ATTRIB_HIDDEN, FS_ATTRIB_SYSTEM, FS_ATTRIB_ARCHIVE. + * + * \note attrib() will fail for set read-only if the file is open for write. + * \return true for success or false for failure. + */ + bool attrib(uint8_t bits) { + return m_fFile ? m_fFile->attrib(bits) : + m_xFile ? m_xFile->attrib(bits) : false; + } + /** \return number of bytes available from the current position to EOF + * or INT_MAX if more than INT_MAX bytes are available. + */ + int available() const { + return m_fFile ? m_fFile->available() : + m_xFile ? m_xFile->available() : 0; + } + /** \return The number of bytes available from the current position + * to EOF for normal files. Zero is returned for directory files. + */ + uint64_t available64() const { + return m_fFile ? m_fFile->available32() : + m_xFile ? m_xFile->available64() : 0; + } + /** Clear writeError. */ + void clearWriteError() { + if (m_fFile) m_fFile->clearWriteError(); + if (m_xFile) m_xFile->clearWriteError(); + } + /** Close a file and force cached data and directory information + * to be written to the storage device. + * + * \return true for success or false for failure. + */ + bool close(); + /** Check for contiguous file and return its raw sector range. + * + * \param[out] bgnSector the first sector address for the file. + * \param[out] endSector the last sector address for the file. + * + * Set contiguous flag for FAT16/FAT32 files. + * Parameters may be nullptr. + * + * \return true for success or false for failure. + */ + bool contiguousRange(uint32_t* bgnSector, uint32_t* endSector) { + return m_fFile ? m_fFile->contiguousRange(bgnSector, endSector) : + m_xFile ? m_xFile->contiguousRange(bgnSector, endSector) : false; + } + /** \return The current cluster number for a file or directory. */ + uint32_t curCluster() const { + return m_fFile ? m_fFile->curCluster() : + m_xFile ? m_xFile->curCluster() : 0; + } + /** \return The current position for a file or directory. */ + uint64_t curPosition() const { + return m_fFile ? m_fFile->curPosition() : + m_xFile ? m_xFile->curPosition() : 0; + } + /** \return Directory entry index. */ + uint32_t dirIndex() const { + return m_fFile ? m_fFile->dirIndex() : + m_xFile ? m_xFile->dirIndex() : 0; + } + /** Test for the existence of a file in a directory + * + * \param[in] path Path of the file to be tested for. + * + * The calling instance must be an open directory file. + * + * dirFile.exists("TOFIND.TXT") searches for "TOFIND.TXT" in the directory + * dirFile. + * + * \return true if the file exists else false. + */ + bool exists(const char* path) { + return m_fFile ? m_fFile->exists(path) : + m_xFile ? m_xFile->exists(path) : false; + } + /** get position for streams + * \param[out] pos struct to receive position + */ + void fgetpos(fspos_t* pos) const { + if (m_fFile) m_fFile->fgetpos(pos); + if (m_xFile) m_xFile->fgetpos(pos); + } + /** + * Get a string from a file. + * + * fgets() reads bytes from a file into the array pointed to by \a str, until + * \a num - 1 bytes are read, or a delimiter is read and transferred to \a str, + * or end-of-file is encountered. The string is then terminated + * with a null byte. + * + * fgets() deletes CR, '\\r', from the string. This insures only a '\\n' + * terminates the string for Windows text files which use CRLF for newline. + * + * \param[out] str Pointer to the array where the string is stored. + * \param[in] num Maximum number of characters to be read + * (including the final null byte). Usually the length + * of the array \a str is used. + * \param[in] delim Optional set of delimiters. The default is "\n". + * + * \return For success fgets() returns the length of the string in \a str. + * If no data is read, fgets() returns zero for EOF or -1 if an error occurred. + */ + int fgets(char* str, int num, char* delim = nullptr) { + return m_fFile ? m_fFile->fgets(str, num, delim) : + m_xFile ? m_xFile->fgets(str, num, delim) : -1; + } + /** \return The total number of bytes in a file. */ + uint64_t fileSize() const { + return m_fFile ? m_fFile->fileSize() : + m_xFile ? m_xFile->fileSize() : 0; + } + /** \return Address of first sector or zero for empty file. */ + uint32_t firstSector() const { + return m_fFile ? m_fFile->firstSector() : + m_xFile ? m_xFile->firstSector() : 0; + } + /** Ensure that any bytes written to the file are saved to the SD card. */ + void flush() {sync();} + /** set position for streams + * \param[in] pos struct with value for new position + */ + void fsetpos(const fspos_t* pos) { + if (m_fFile) m_fFile->fsetpos(pos); + if (m_xFile) m_xFile->fsetpos(pos); + } + /** Get a file's access date and time. + * + * \param[out] pdate Packed date for directory entry. + * \param[out] ptime Packed time for directory entry. + * + * \return true for success or false for failure. + */ + bool getAccessDateTime(uint16_t* pdate, uint16_t* ptime) { + return m_fFile ? m_fFile->getAccessDateTime(pdate, ptime) : + m_xFile ? m_xFile->getAccessDateTime(pdate, ptime) : false; + } + /** Get a file's create date and time. + * + * \param[out] pdate Packed date for directory entry. + * \param[out] ptime Packed time for directory entry. + * + * \return true for success or false for failure. + */ + bool getCreateDateTime(uint16_t* pdate, uint16_t* ptime) { + return m_fFile ? m_fFile->getCreateDateTime(pdate, ptime) : + m_xFile ? m_xFile->getCreateDateTime(pdate, ptime) : false; + } + /** \return All error bits. */ + uint8_t getError() const { + return m_fFile ? m_fFile->getError() : + m_xFile ? m_xFile->getError() : 0XFF; + } + /** Get a file's Modify date and time. + * + * \param[out] pdate Packed date for directory entry. + * \param[out] ptime Packed time for directory entry. + * + * \return true for success or false for failure. + */ + bool getModifyDateTime(uint16_t* pdate, uint16_t* ptime) { + return m_fFile ? m_fFile->getModifyDateTime(pdate, ptime) : + m_xFile ? m_xFile->getModifyDateTime(pdate, ptime) : false; + } + /** + * Get a file's name followed by a zero byte. + * + * \param[out] name An array of characters for the file's name. + * \param[in] len The size of the array in bytes. The array + * must be at least 13 bytes long. The file's name will be + * truncated if the file's name is too long. + * \return The length of the returned string. + */ + size_t getName(char* name, size_t len) { + *name = 0; + return m_fFile ? m_fFile->getName(name, len) : + m_xFile ? m_xFile->getName(name, len) : 0; + } + + /** \return value of writeError */ + bool getWriteError() const { + return m_fFile ? m_fFile->getWriteError() : + m_xFile ? m_xFile->getWriteError() : true; + } + /** + * Check for FsBlockDevice busy. + * + * \return true if busy else false. + */ + bool isBusy() { + return m_fFile ? m_fFile->isBusy() : + m_xFile ? m_xFile->isBusy() : true; + } + /** \return True if the file is contiguous. */ + bool isContiguous() const { +#if USE_FAT_FILE_FLAG_CONTIGUOUS + return m_fFile ? m_fFile->isContiguous() : + m_xFile ? m_xFile->isContiguous() : false; +#else // USE_FAT_FILE_FLAG_CONTIGUOUS + return m_xFile ? m_xFile->isContiguous() : false; +#endif // USE_FAT_FILE_FLAG_CONTIGUOUS + } + /** \return True if this is a directory else false. */ + bool isDir() const { + return m_fFile ? m_fFile->isDir() : + m_xFile ? m_xFile->isDir() : false; + } + /** This function reports if the current file is a directory or not. + * \return true if the file is a directory. + */ + bool isDirectory() const {return isDir();} + /** \return True if this is a normal file. */ + bool isFile() const { + return m_fFile ? m_fFile->isFile() : + m_xFile ? m_xFile->isFile() : false; + } + /** \return True if this is a normal file or sub-directory. */ + bool isFileOrSubDir() const { + return m_fFile ? m_fFile->isFileOrSubDir() : + m_xFile ? m_xFile->isFileOrSubDir() : false; + } + /** \return True if this is a hidden file else false. */ + bool isHidden() const { + return m_fFile ? m_fFile->isHidden() : + m_xFile ? m_xFile->isHidden() : false; + } + /** \return True if this is an open file/directory else false. */ + bool isOpen() const {return m_fFile || m_xFile;} + /** \return True file is readable. */ + bool isReadable() const { + return m_fFile ? m_fFile->isReadable() : + m_xFile ? m_xFile->isReadable() : false; + } + /** \return True if file is read-only */ + bool isReadOnly() const { + return m_fFile ? m_fFile->isReadOnly() : + m_xFile ? m_xFile->isReadOnly() : false; + } + /** \return True if this is a sub-directory file else false. */ + bool isSubDir() const { + return m_fFile ? m_fFile->isSubDir() : + m_xFile ? m_xFile->isSubDir() : false; + } + /** \return True file is writable. */ + bool isWritable() const { + return m_fFile ? m_fFile->isWritable() : + m_xFile ? m_xFile->isWritable() : false; + } +#if ENABLE_ARDUINO_SERIAL + /** List directory contents. + * + * \param[in] flags The inclusive OR of + * + * LS_DATE - %Print file modification date + * + * LS_SIZE - %Print file size. + * + * LS_R - Recursive list of subdirectories. + */ + bool ls(uint8_t flags) { + return ls(&Serial, flags); + } + /** List directory contents. */ + bool ls() { + return ls(&Serial); + } +#endif // ENABLE_ARDUINO_SERIAL + /** List directory contents. + * + * \param[in] pr Print object. + * + * \return true for success or false for failure. + */ + bool ls(print_t* pr) { + return m_fFile ? m_fFile->ls(pr) : + m_xFile ? m_xFile->ls(pr) : false; + } + /** List directory contents. + * + * \param[in] pr Print object. + * \param[in] flags The inclusive OR of + * + * LS_DATE - %Print file modification date + * + * LS_SIZE - %Print file size. + * + * LS_R - Recursive list of subdirectories. + * + * \return true for success or false for failure. + */ + bool ls(print_t* pr, uint8_t flags) { + return m_fFile ? m_fFile->ls(pr, flags) : + m_xFile ? m_xFile->ls(pr, flags) : false; + } + /** Make a new directory. + * + * \param[in] dir An open FatFile 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. + * + * \param[in] pFlag Create missing parent directories if true. + * + * \return true for success or false for failure. + */ + bool mkdir(FsBaseFile* dir, const char* path, bool pFlag = true); + /** Open a file or directory by name. + * + * \param[in] dir An open file 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. + * + * \param[in] oflag Values for \a oflag are constructed by a + * bitwise-inclusive OR of flags from the following list + * + * O_RDONLY - Open for reading only.. + * + * O_READ - Same as O_RDONLY. + * + * O_WRONLY - Open for writing only. + * + * O_WRITE - Same as O_WRONLY. + * + * O_RDWR - Open for reading and writing. + * + * O_APPEND - If set, the file offset shall be set to the end of the + * file prior to each write. + * + * O_AT_END - Set the initial position at the end of the file. + * + * O_CREAT - If the file exists, this flag has no effect except as noted + * under O_EXCL below. Otherwise, the file shall be created + * + * O_EXCL - If O_CREAT and O_EXCL are set, open() shall fail if the file exists. + * + * O_TRUNC - If the file exists and is a regular file, and the file is + * successfully opened and is not read only, its length shall be truncated to 0. + * + * WARNING: A given file must not be opened by more than one file object + * or file corruption may occur. + * + * \note Directory files must be opened read only. Write and truncation is + * not allowed for directory files. + * + * \return true for success or false for failure. + */ + bool open(FsBaseFile* dir, const char* path, oflag_t oflag = O_RDONLY); + /** Open a file by index. + * + * \param[in] dir An open FsFile instance for the directory. + * + * \param[in] index The \a index of the directory entry for the file to be + * opened. The value for \a index is (directory file position)/32. + * + * \param[in] oflag bitwise-inclusive OR of open flags. + * See see FsFile::open(FsFile*, const char*, uint8_t). + * + * See open() by path for definition of flags. + * \return true for success or false for failure. + */ + bool open(FsBaseFile* dir, uint32_t index, oflag_t oflag = O_RDONLY); + /** Open a file or directory by name. + * + * \param[in] vol Volume where the file is located. + * + * \param[in] path A path for a file to be opened. + * + * \param[in] oflag Values for \a oflag are constructed by a + * bitwise-inclusive OR of open flags. + * + * \return true for success or false for failure. + */ + bool open(FsVolume* vol, const char* path, oflag_t oflag = O_RDONLY); + /** Open a file or directory by name. + * + * \param[in] path A path for a file to be opened. + * + * \param[in] oflag Values for \a oflag are constructed by a + * bitwise-inclusive OR of open flags. + * + * \return true for success or false for failure. + */ + bool open(const char* path, oflag_t oflag = O_RDONLY) { + return FsVolume::m_cwv && open(FsVolume::m_cwv, path, oflag); + } + /** Open a file or directory by index in the current working directory. + * + * \param[in] index The \a index of the directory entry for the file to be + * opened. The value for \a index is (directory file position)/32. + * + * \param[in] oflag Values for \a oflag are constructed by a + * bitwise-inclusive OR of open flags. + * + * \return true for success or false for failure. + */ + bool open(uint32_t index, oflag_t oflag = O_RDONLY) { + FsBaseFile cwd; + return cwd.openCwd() && open(&cwd, index, oflag); + } + /** Open the current working directory. + * + * \return true for success or false for failure. + */ + bool openCwd(); + /** Opens the next file or folder in a directory. + * \param[in] dir directory containing files. + * \param[in] oflag open flags. + * \return a file object. + */ + bool openNext(FsBaseFile* dir, oflag_t oflag = O_RDONLY); + /** Open a volume's root directory. + * + * \param[in] vol The SdFs volume containing the root directory to be opened. + * + * \return true for success or false for failure. + */ + bool openRoot(FsVolume* vol); + /** \return the current file position. */ + uint64_t position() const {return curPosition();} + /** Return the next available byte without consuming it. + * + * \return The byte if no error and not at eof else -1; + */ + int peek() { + return m_fFile ? m_fFile->peek() : + m_xFile ? m_xFile->peek() : -1; + } + /** Allocate contiguous clusters to an empty file. + * + * The file must be empty with no clusters allocated. + * + * The file will contain uninitialized data for FAT16/FAT32 files. + * exFAT files will have zero validLength and dataLength will equal + * the requested length. + * + * \param[in] length size of the file in bytes. + * \return true for success or false for failure. + */ + bool preAllocate(uint64_t length) { + return m_fFile ? length < (1ULL << 32) && m_fFile->preAllocate(length) : + m_xFile ? m_xFile->preAllocate(length) : false; + } + /** Print a file's access date and time + * + * \param[in] pr Print stream for output. + * + * \return true for success or false for failure. + */ + size_t printAccessDateTime(print_t* pr) { + return m_fFile ? m_fFile->printAccessDateTime(pr) : + m_xFile ? m_xFile->printAccessDateTime(pr) : 0; + } + /** Print a file's creation date and time + * + * \param[in] pr Print stream for output. + * + * \return true for success or false for failure. + */ + size_t printCreateDateTime(print_t* pr) { + return m_fFile ? m_fFile->printCreateDateTime(pr) : + m_xFile ? m_xFile->printCreateDateTime(pr) : 0; + } + /** Print a number followed by a field terminator. + * \param[in] value The number to be printed. + * \param[in] term The field terminator. Use '\\n' for CR LF. + * \param[in] prec Number of digits after decimal point. + * \return The number of bytes written or -1 if an error occurs. + */ + size_t printField(double value, char term, uint8_t prec = 2) { + return m_fFile ? m_fFile->printField(value, term, prec) : + m_xFile ? m_xFile->printField(value, term, prec) : 0; + } + /** Print a number followed by a field terminator. + * \param[in] value The number to be printed. + * \param[in] term The field terminator. Use '\\n' for CR LF. + * \param[in] prec Number of digits after decimal point. + * \return The number of bytes written or -1 if an error occurs. + */ + size_t printField(float value, char term, uint8_t prec = 2) { + return printField(static_cast(value), term, prec); + } + /** Print a number followed by a field terminator. + * \param[in] value The number to be printed. + * \param[in] term The field terminator. Use '\\n' for CR LF. + * \return The number of bytes written or -1 if an error occurs. + */ + template + size_t printField(Type value, char term) { + return m_fFile ? m_fFile->printField(value, term) : + m_xFile ? m_xFile->printField(value, term) : 0; + } + /** Print a file's size. + * + * \param[in] pr Print stream for output. + * + * \return The number of characters printed is returned + * for success and zero is returned for failure. + */ + size_t printFileSize(print_t* pr) { + return m_fFile ? m_fFile->printFileSize(pr) : + m_xFile ? m_xFile->printFileSize(pr) : 0; + } + /** Print a file's modify date and time + * + * \param[in] pr Print stream for output. + * + * \return true for success or false for failure. + */ + size_t printModifyDateTime(print_t* pr) { + return m_fFile ? m_fFile->printModifyDateTime(pr) : + m_xFile ? m_xFile->printModifyDateTime(pr) : 0; + } + /** Print a file's name + * + * \param[in] pr Print stream for output. + * + * \return true for success or false for failure. + */ + size_t printName(print_t* pr) { + return m_fFile ? m_fFile->printName(pr) : + m_xFile ? m_xFile->printName(pr) : 0; + } + /** Read the next byte from a file. + * + * \return For success return the next byte in the file as an int. + * If an error occurs or end of file is reached return -1. + */ + int read() { + uint8_t b; + return read(&b, 1) == 1 ? b : -1; + } + /** Read data from a file starting at the current position. + * + * \param[out] buf Pointer to the location that will receive the data. + * + * \param[in] count Maximum number of bytes to read. + * + * \return For success read() returns the number of bytes read. + * A value less than \a count, including zero, will be returned + * if end of file is reached. + * If an error occurs, read() returns -1. Possible errors include + * read() called before a file has been opened, corrupt file system + * or an I/O error occurred. + */ + int read(void* buf, size_t count) { + return m_fFile ? m_fFile->read(buf, count) : + m_xFile ? m_xFile->read(buf, count) : -1; + } + /** Remove a file. + * + * The directory entry and all data for the file are deleted. + * + * \note This function should not be used to delete the 8.3 version of a + * file that has a long name. For example if a file has the long name + * "New Text Document.txt" you should not delete the 8.3 name "NEWTEX~1.TXT". + * + * \return true for success or false for failure. + */ + bool remove(); + /** Remove a file. + * + * The directory entry and all data for the file are deleted. + * + * \param[in] path Path for the file to be removed. + * + * Example use: dirFile.remove(filenameToRemove); + * + * \note This function should not be used to delete the 8.3 version of a + * file that has a long name. For example if a file has the long name + * "New Text Document.txt" you should not delete the 8.3 name "NEWTEX~1.TXT". + * + * \return true for success or false for failure. + */ + bool remove(const char* path) { + return m_fFile ? m_fFile->remove(path) : + m_xFile ? m_xFile->remove(path) : false; + } + /** Rename a file or subdirectory. + * + * \param[in] newPath New path name for the file/directory. + * + * \return true for success or false for failure. + */ + bool rename(const char* newPath) { + return m_fFile ? m_fFile->rename(newPath) : + m_xFile ? m_xFile->rename(newPath) : false; + } + /** Rename a file or subdirectory. + * + * \param[in] dir Directory for the new path. + * \param[in] newPath New path name for the file/directory. + * + * \return true for success or false for failure. + */ + bool rename(FsBaseFile* dir, const char* newPath) { + return m_fFile && dir->m_fFile ? m_fFile->rename(dir->m_fFile, newPath) : + m_xFile && dir->m_xFile ? m_xFile->rename(dir->m_xFile, newPath) : + false; + } + /** Set the file's current position to zero. */ + void rewind() { + if (m_fFile) m_fFile->rewind(); + if (m_xFile) m_xFile->rewind(); + } + /** Rewind a file if it is a directory */ + void rewindDirectory() { + if (isDir()) rewind(); + } + /** Remove a directory file. + * + * The directory file will be removed only if it is empty and is not the + * root directory. rmdir() follows DOS and Windows and ignores the + * read-only attribute for the directory. + * + * \note This function should not be used to delete the 8.3 version of a + * directory that has a long name. For example if a directory has the + * long name "New folder" you should not delete the 8.3 name "NEWFOL~1". + * + * \return true for success or false for failure. + */ + bool rmdir(); + /** Seek to a new position in the file, which must be between + * 0 and the size of the file (inclusive). + * + * \param[in] pos the new file position. + * \return true for success or false for failure. + */ + bool seek(uint64_t pos) {return seekSet(pos);} + /** 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(int64_t offset) { + return seekSet(curPosition() + offset); + } + /** Set the files position to end-of-file + \a offset. See seekSet(). + * Can't be used for directory files since file size is not defined. + * \param[in] offset The new position in bytes from end-of-file. + * \return true for success or false for failure. + */ + bool seekEnd(int64_t offset = 0) { + return seekSet(fileSize() + offset); + } + /** Sets a file's position. + * + * \param[in] pos The new position in bytes from the beginning of the file. + * + * \return true for success or false for failure. + */ + bool seekSet(uint64_t pos) { + return m_fFile ? pos < (1ULL << 32) && m_fFile->seekSet(pos) : + m_xFile ? m_xFile->seekSet(pos) : false; + } + /** \return the file's size. */ + uint64_t size() const {return fileSize();} + /** The sync() call causes all modified data and directory fields + * to be written to the storage device. + * + * \return true for success or false for failure. + */ + bool sync() { + return m_fFile ? m_fFile->sync() : + m_xFile ? m_xFile->sync() : false; + } + /** Set a file's timestamps in its directory entry. + * + * \param[in] flags Values for \a flags are constructed by a bitwise-inclusive + * OR of flags from the following list + * + * T_ACCESS - Set the file's last access date and time. + * + * T_CREATE - Set the file's creation date and time. + * + * T_WRITE - Set the file's last write/modification date and time. + * + * \param[in] year Valid range 1980 - 2107 inclusive. + * + * \param[in] month Valid range 1 - 12 inclusive. + * + * \param[in] day Valid range 1 - 31 inclusive. + * + * \param[in] hour Valid range 0 - 23 inclusive. + * + * \param[in] minute Valid range 0 - 59 inclusive. + * + * \param[in] second Valid range 0 - 59 inclusive + * + * \note It is possible to set an invalid date since there is no check for + * the number of days in a month. + * + * \note + * Modify and access timestamps may be overwritten if a date time callback + * function has been set by dateTimeCallback(). + * + * \return true for success or false for failure. + */ + bool timestamp(uint8_t flags, uint16_t year, uint8_t month, uint8_t day, + uint8_t hour, uint8_t minute, uint8_t second) { + return m_fFile ? + m_fFile->timestamp(flags, year, month, day, hour, minute, second) : + m_xFile ? + m_xFile->timestamp(flags, year, month, day, hour, minute, second) : + false; + } + /** Truncate a file to the current position. + * + * \return true for success or false for failure. + */ + bool truncate() { + return m_fFile ? m_fFile->truncate() : + m_xFile ? m_xFile->truncate() : false; + } + /** Truncate a file to a specified length. + * The current file position will be set to end of file. + * + * \param[in] length The desired length for the file. + * + * \return true for success or false for failure. + */ + bool truncate(uint64_t length) { + return m_fFile ? length < (1ULL << 32) && m_fFile->truncate(length) : + m_xFile ? m_xFile->truncate(length) : false; + } + /** Write a string to a file. Used by the Arduino Print class. + * \param[in] str Pointer to the string. + * Use getWriteError to check for errors. + * \return count of characters written for success or -1 for failure. + */ + size_t write(const char* str) { + return write(str, strlen(str)); + } + /** Write a byte to a file. Required by the Arduino Print class. + * \param[in] b the byte to be written. + * Use getWriteError to check for errors. + * \return 1 for success and 0 for failure. + */ + size_t write(uint8_t b) {return write(&b, 1);} + /** Write data to an open file. + * + * \note Data is moved to the cache but may not be written to the + * storage device until sync() is called. + * + * \param[in] buf Pointer to the location of the data to be written. + * + * \param[in] count Number of bytes to write. + * + * \return For success write() returns the number of bytes written, always + * \a nbyte. If an error occurs, write() returns zero and writeError is set. + */ + size_t write(const void* buf, size_t count) { + return m_fFile ? m_fFile->write(buf, count) : + m_xFile ? m_xFile->write(buf, count) : 0; + } + + private: + newalign_t m_fileMem[FS_ALIGN_DIM(ExFatFile, FatFile)]; + FatFile* m_fFile = nullptr; + ExFatFile* m_xFile = nullptr; +}; +/** + * \class FsFile + * \brief FsBaseFile file with Arduino Stream. + */ +class FsFile : public StreamFile { + public: + /** Opens the next file or folder in a directory. + * + * \param[in] oflag open flags. + * \return a FatStream object. + */ + FsFile openNextFile(oflag_t oflag = O_RDONLY) { + FsFile tmpFile; + tmpFile.openNext(this, oflag); + return tmpFile; + } +}; +#endif // FsFile_h diff --git a/avr/cores/megacommand/SdFat/FsLib/FsFormatter.h b/avr/cores/megacommand/SdFat/FsLib/FsFormatter.h new file mode 100644 index 000000000..a96ec6775 --- /dev/null +++ b/avr/cores/megacommand/SdFat/FsLib/FsFormatter.h @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef FsFormatter_h +#define FsFormatter_h +#include "FatLib/FatLib.h" +#include "ExFatLib/ExFatLib.h" +/** + * \class FsFormatter + * \brief Format a exFAT/FAT volume. + */ +class FsFormatter { + public: + /** + * Format a FAT volume. + * + * \param[in] dev Block device for volume. + * \param[in] secBuffer buffer for writing to volume. + * \param[in] pr Print device for progress output. + * + * \return true for success or false for failure. + */ + bool format(FsBlockDevice* dev, uint8_t* secBuffer, print_t* pr = nullptr) { + uint32_t sectorCount = dev->sectorCount(); + if (sectorCount == 0) { + return false; + } + return sectorCount <= 67108864 ? + m_fFmt.format(dev, secBuffer, pr) : + m_xFmt.format(dev, secBuffer, pr); + } + private: + FatFormatter m_fFmt; + ExFatFormatter m_xFmt; +}; +#endif // FsFormatter_h diff --git a/avr/cores/megacommand/SdFat/FsLib/FsLib.h b/avr/cores/megacommand/SdFat/FsLib/FsLib.h new file mode 100644 index 000000000..51764df32 --- /dev/null +++ b/avr/cores/megacommand/SdFat/FsLib/FsLib.h @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef FsLib_h +#define FsLib_h +/** + * \file + * \brief FsLib include file. + */ +#include "FsVolume.h" +#include "FsFile.h" +#include "FsFormatter.h" +#endif // FsLib_h diff --git a/avr/cores/megacommand/SdFat/FsLib/FsNew.cpp b/avr/cores/megacommand/SdFat/FsLib/FsNew.cpp new file mode 100644 index 000000000..b9063978a --- /dev/null +++ b/avr/cores/megacommand/SdFat/FsLib/FsNew.cpp @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "FsNew.h" +void* operator new(size_t size, newalign_t* ptr) { + (void)size; + return ptr; +} diff --git a/avr/cores/megacommand/SdFat/FsLib/FsNew.h b/avr/cores/megacommand/SdFat/FsLib/FsNew.h new file mode 100644 index 000000000..20bb3b145 --- /dev/null +++ b/avr/cores/megacommand/SdFat/FsLib/FsNew.h @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef FsNew_h +#define FsNew_h +#include +#include + +/** 32-bit alignment */ +typedef uint32_t newalign_t; + +/** Size required for exFAT or FAT class. */ +#define FS_SIZE(etype, ftype) \ + (sizeof(ftype) < sizeof(etype) ? sizeof(etype) : sizeof(ftype)) + +/** Dimension of aligned area. */ +#define NEW_ALIGN_DIM(n) \ + (((size_t)(n) + sizeof(newalign_t) - 1U)/sizeof(newalign_t)) + +/** Dimension of aligned area for etype or ftype class. */ +#define FS_ALIGN_DIM(etype, ftype) NEW_ALIGN_DIM(FS_SIZE(etype, ftype)) + +/** Custom new placement operator */ +void* operator new(size_t size, newalign_t* ptr); +#endif // FsNew_h diff --git a/avr/cores/megacommand/SdFat/FsLib/FsVolume.cpp b/avr/cores/megacommand/SdFat/FsLib/FsVolume.cpp new file mode 100644 index 000000000..fc8a3c036 --- /dev/null +++ b/avr/cores/megacommand/SdFat/FsLib/FsVolume.cpp @@ -0,0 +1,66 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "FsLib.h" +FsVolume* FsVolume::m_cwv = nullptr; +//------------------------------------------------------------------------------ +bool FsVolume::begin(FsBlockDevice* blockDev, bool setCwv, + uint8_t part, uint32_t volStart) { + m_blockDev = blockDev; + m_fVol = nullptr; + m_xVol = new (m_volMem) ExFatVolume; + if (m_xVol && m_xVol->begin(m_blockDev, false, part, volStart)) { + goto done; + } + m_xVol = nullptr; + m_fVol = new (m_volMem) FatVolume; + if (m_fVol && m_fVol->begin(m_blockDev, false, part, volStart)) { + goto done; + } + m_fVol = nullptr; + return false; + + done: + if (setCwv || !m_cwv) { + m_cwv = this; + } + return true; +} +//------------------------------------------------------------------------------ +bool FsVolume::ls(print_t* pr, const char* path, uint8_t flags) { + FsBaseFile dir; + return dir.open(this, path, O_RDONLY) && dir.ls(pr, flags); +} +//------------------------------------------------------------------------------ +FsFile FsVolume::open(const char *path, oflag_t oflag) { + FsFile tmpFile; + tmpFile.open(this, path, oflag); + return tmpFile; +} +#if ENABLE_ARDUINO_STRING +//------------------------------------------------------------------------------ +FsFile FsVolume::open(const String &path, oflag_t oflag) { + return open(path.c_str(), oflag ); +} +#endif // ENABLE_ARDUINO_STRING diff --git a/avr/cores/megacommand/SdFat/FsLib/FsVolume.h b/avr/cores/megacommand/SdFat/FsLib/FsVolume.h new file mode 100644 index 000000000..f4f17d0bd --- /dev/null +++ b/avr/cores/megacommand/SdFat/FsLib/FsVolume.h @@ -0,0 +1,410 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef FsVolume_h +#define FsVolume_h +/** + * \file + * \brief FsVolume include file. + */ +#include "FsNew.h" +#include "../FatLib/FatLib.h" +#include "../ExFatLib/ExFatLib.h" + +class FsFile; +/** + * \class FsVolume + * \brief FsVolume class. + */ +class FsVolume { + public: + FsVolume() {} + + ~FsVolume() {end();} + /** Get file's user settable attributes. + * \param[in] path path to file. + * \return user settable file attributes for success else -1. + */ + int attrib(const char* path) { + return m_fVol ? m_fVol->attrib(path) : + m_xVol ? m_xVol->attrib(path) : -1; + } + /** Set file's user settable attributes. + * \param[in] path path to file. + * \param[in] bits bit-wise or of selected attributes: FS_ATTRIB_READ_ONLY, + * FS_ATTRIB_HIDDEN, FS_ATTRIB_SYSTEM, FS_ATTRIB_ARCHIVE. + * + * \return true for success or false for failure. + */ + bool attrib(const char* path, uint8_t bits) { + return m_fVol ? m_fVol->attrib(path, bits) : + m_xVol ? m_xVol->attrib(path, bits) : false; + } + /** + * Initialize an FatVolume object. + * \param[in] blockDev Device block driver. + * \param[in] setCwv Set current working volume if true. + * \param[in] part partition to initialize. + * \param[in] volStart Start sector of volume if part is zero. + * \return true for success or false for failure. + */ + bool begin(FsBlockDevice* blockDev, bool setCwv = true, uint8_t + part = 1, uint32_t volStart = 0); +#ifndef DOXYGEN_SHOULD_SKIP_THIS + uint32_t __attribute__((error("use sectorsPerCluster()"))) blocksPerCluster(); +#endif // DOXYGEN_SHOULD_SKIP_THIS + /** \return the number of bytes in a cluster. */ + uint32_t bytesPerCluster() const { + return m_fVol ? m_fVol->bytesPerCluster() : + m_xVol ? m_xVol->bytesPerCluster() : 0; + } + /** + * Set volume working directory to root. + * \return true for success or false for failure. + */ + bool chdir() { + return m_fVol ? m_fVol->chdir() : + m_xVol ? m_xVol->chdir() : false; + } + /** + * Set volume working directory. + * \param[in] path Path for volume working directory. + * \return true for success or false for failure. + */ + bool chdir(const char* path) { + return m_fVol ? m_fVol->chdir(path) : + m_xVol ? m_xVol->chdir(path) : false; + } + /** Change global working volume to this volume. */ + void chvol() {m_cwv = this;} + /** \return The total number of clusters in the volume. */ + uint32_t clusterCount() const { + return m_fVol ? m_fVol->clusterCount() : + m_xVol ? m_xVol->clusterCount() : 0; + } + /** \return The logical sector number for the start of file data. */ + uint32_t dataStartSector() const { + return m_fVol ? m_fVol->dataStartSector() : + m_xVol ? m_xVol->clusterHeapStartSector() : 0; + } + /** End access to volume + * \return pointer to sector size buffer for format. + */ + uint8_t* end() { + m_fVol = nullptr; + m_xVol = nullptr; + static_assert(sizeof(m_volMem) >= 512, "m_volMem too small"); + return reinterpret_cast(m_volMem); + } + /** Test for the existence of a file in a directory + * + * \param[in] path Path of the file to be tested for. + * + * \return true if the file exists else false. + */ + bool exists(const char* path) { + return m_fVol ? m_fVol->exists(path) : + m_xVol ? m_xVol->exists(path) : false; + } + /** \return The logical sector number for the start of the first FAT. */ + uint32_t fatStartSector() const { + return m_fVol ? m_fVol->fatStartSector() : + m_xVol ? m_xVol->fatStartSector() : 0; + } + /** \return Partition type, FAT_TYPE_EXFAT, FAT_TYPE_FAT32, + * FAT_TYPE_FAT16, or zero for error. + */ + uint8_t fatType() const { + return m_fVol ? m_fVol->fatType() : + m_xVol ? m_xVol->fatType() : 0; + } + /** \return free cluster count or -1 if an error occurs. */ + int32_t freeClusterCount() const { + return m_fVol ? m_fVol->freeClusterCount() : + m_xVol ? m_xVol->freeClusterCount() : -1; + } + /** + * Check for device busy. + * + * \return true if busy else false. + */ + bool isBusy() { + return m_fVol ? m_fVol->isBusy() : + m_xVol ? m_xVol->isBusy() : false; + } + /** List directory contents. + * + * \param[in] pr Print object. + * + * \return true for success or false for failure. + */ + bool ls(print_t* pr) { + return m_fVol ? m_fVol->ls(pr) : + m_xVol ? m_xVol->ls(pr) : false; + } + /** List directory contents. + * + * \param[in] pr Print object. + * \param[in] flags The inclusive OR of + * + * LS_DATE - %Print file modification date + * + * LS_SIZE - %Print file size. + * + * LS_R - Recursive list of subdirectories. + * + * \return true for success or false for failure. + */ + bool ls(print_t* pr, uint8_t flags) { + return m_fVol ? m_fVol->ls(pr, flags) : + m_xVol ? m_xVol->ls(pr, flags) : false; + } + /** List the directory contents of a directory. + * + * \param[in] pr Print stream for list. + * + * \param[in] path directory to list. + * + * \param[in] flags The inclusive OR of + * + * LS_DATE - %Print file modification date + * + * LS_SIZE - %Print file size. + * + * LS_R - Recursive list of subdirectories. + * + * \return true for success or false for failure. + */ + bool ls(print_t* pr, const char* path, uint8_t flags); + /** Make a subdirectory in the volume root directory. + * + * \param[in] path A path with a valid 8.3 DOS name for the subdirectory. + * + * \param[in] pFlag Create missing parent directories if true. + * + * \return true for success or false for failure. + */ + bool mkdir(const char *path, bool pFlag = true) { + return m_fVol ? m_fVol->mkdir(path, pFlag) : + m_xVol ? m_xVol->mkdir(path, pFlag) : false; + } + /** open a file + * + * \param[in] path location of file to be opened. + * \param[in] oflag open flags. + * \return a FsBaseFile object. + */ + FsFile open(const char* path, oflag_t oflag = O_RDONLY); + /** Remove a file from the volume root directory. + * + * \param[in] path A path with a valid 8.3 DOS name for the file. + * + * \return true for success or false for failure. + */ + bool remove(const char *path) { + return m_fVol ? m_fVol->remove(path) : + m_xVol ? m_xVol->remove(path) : false; + } + /** Rename a file or subdirectory. + * + * \param[in] oldPath Path name to the file or subdirectory to be renamed. + * + * \param[in] newPath New path name of the file or subdirectory. + * + * The \a newPath object must not exist before the rename call. + * + * The file to be renamed must not be open. The directory entry may be + * moved and file system corruption could occur if the file is accessed by + * a file object that was opened before the rename() call. + * + * \return true for success or false for failure. + */ + bool rename(const char *oldPath, const char *newPath) { + return m_fVol ? m_fVol->rename(oldPath, newPath) : + m_xVol ? m_xVol->rename(oldPath, newPath) : false; + } + /** Remove a subdirectory from the volume's root directory. + * + * \param[in] path A path with a valid 8.3 DOS name for the subdirectory. + * + * The subdirectory file will be removed only if it is empty. + * + * \return true for success or false for failure. + */ + bool rmdir(const char *path) { + return m_fVol ? m_fVol->rmdir(path) : + m_xVol ? m_xVol->rmdir(path) : false; + } + /** \return The volume's cluster size in sectors. */ + uint32_t sectorsPerCluster() const { + return m_fVol ? m_fVol->sectorsPerCluster() : + m_xVol ? m_xVol->sectorsPerCluster() : 0; + } +#if ENABLE_ARDUINO_SERIAL + /** List directory contents. + * \return true for success or false for failure. + */ + bool ls() { + return ls(&Serial); + } + /** List directory contents. + * + * \param[in] flags The inclusive OR of + * + * LS_DATE - %Print file modification date + * + * LS_SIZE - %Print file size. + * + * LS_R - Recursive list of subdirectories. + * + * \return true for success or false for failure. + */ + bool ls(uint8_t flags) { + return ls(&Serial, flags); + } + /** List the directory contents of a directory to Serial. + * + * \param[in] path directory to list. + * + * \param[in] flags The inclusive OR of + * + * LS_DATE - %Print file modification date + * + * LS_SIZE - %Print file size. + * + * LS_R - Recursive list of subdirectories. + * + * \return true for success or false for failure. + * + * \return true for success or false for failure. + */ + bool ls(const char* path, uint8_t flags = 0) { + return ls(&Serial, path, flags); + } +#endif // ENABLE_ARDUINO_SERIAL +#if ENABLE_ARDUINO_STRING + /** + * Set volume working directory. + * \param[in] path Path for volume working directory. + * \return true for success or false for failure. + */ + bool chdir(const String& path) { + return chdir(path.c_str()); + } + /** Test for the existence of a file in a directory + * + * \param[in] path Path of the file to be tested for. + * + * \return true if the file exists else false. + */ + bool exists(const String &path) { + return exists(path.c_str()); + } + /** Make a subdirectory in the volume root directory. + * + * \param[in] path A path with a valid 8.3 DOS name for the subdirectory. + * + * \param[in] pFlag Create missing parent directories if true. + * + * \return true for success or false for failure. + */ + bool mkdir(const String &path, bool pFlag = true) { + return mkdir(path.c_str(), pFlag); + } + /** open a file + * + * \param[in] path location of file to be opened. + * \param[in] oflag open flags. + * \return a FsBaseFile object. + */ + FsFile open(const String &path, oflag_t oflag = O_RDONLY); + /** Remove a file from the volume root directory. + * + * \param[in] path A path with a valid 8.3 DOS name for the file. + * + * \return true for success or false for failure. + */ + bool remove(const String &path) { + return remove(path.c_str()); + } + /** Rename a file or subdirectory. + * + * \param[in] oldPath Path name to the file or subdirectory to be renamed. + * + * \param[in] newPath New path name of the file or subdirectory. + * + * The \a newPath object must not exist before the rename call. + * + * The file to be renamed must not be open. The directory entry may be + * moved and file system corruption could occur if the file is accessed by + * a file object that was opened before the rename() call. + * + * \return true for success or false for failure. + */ + bool rename(const String& oldPath, const String& newPath) { + return rename(oldPath.c_str(), newPath.c_str()); + } + /** Remove a subdirectory from the volume's root directory. + * + * \param[in] path A path with a valid 8.3 DOS name for the subdirectory. + * + * The subdirectory file will be removed only if it is empty. + * + * \return true for success or false for failure. + */ + bool rmdir(const String &path) { + return rmdir(path.c_str()); + } + /** Rename a file or subdirectory. + * + * \param[in] oldPath Path name to the file or subdirectory to be renamed. + * + * \param[in] newPath New path name of the file or subdirectory. + * + * The \a newPath object must not exist before the rename call. + * + * The file to be renamed must not be open. The directory entry may be + * moved and file system corruption could occur if the file is accessed by + * a file object that was opened before the rename() call. + * + * \return true for success or false for failure. + */ +#endif // ENABLE_ARDUINO_STRING + + protected: + newalign_t m_volMem[FS_ALIGN_DIM(ExFatVolume, FatVolume)]; + + private: + /** FsBaseFile allowed access to private members. */ + friend class FsBaseFile; + static FsVolume* cwv() {return m_cwv;} + FsVolume(const FsVolume& from); + FsVolume& operator=(const FsVolume& from); + + static FsVolume* m_cwv; + FatVolume* m_fVol = nullptr; + ExFatVolume* m_xVol = nullptr; + FsBlockDevice* m_blockDev; +}; +#endif // FsVolume_h diff --git a/avr/cores/megacommand/SdFat/MinimumSerial.cpp b/avr/cores/megacommand/SdFat/MinimumSerial.cpp index 42d00733b..6ea0ff0b1 100644 --- a/avr/cores/megacommand/SdFat/MinimumSerial.cpp +++ b/avr/cores/megacommand/SdFat/MinimumSerial.cpp @@ -1,5 +1,5 @@ /** - * Copyright (c) 20011-2017 Bill Greiman + * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -22,7 +22,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ -#include "SysCall.h" +#include "common/SysCall.h" #if defined(UDR0) || defined(DOXYGEN) #include "MinimumSerial.h" const uint16_t MIN_2X_BAUD = F_CPU/(4*(2*0XFFF + 1)) + 1; diff --git a/avr/cores/megacommand/SdFat/MinimumSerial.h b/avr/cores/megacommand/SdFat/MinimumSerial.h index 5a700b893..e794c5b9c 100644 --- a/avr/cores/megacommand/SdFat/MinimumSerial.h +++ b/avr/cores/megacommand/SdFat/MinimumSerial.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 20011-2017 Bill Greiman + * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -28,16 +28,16 @@ */ #ifndef MinimumSerial_h #define MinimumSerial_h -#include "SysCall.h" +#include "common/SysCall.h" //============================================================================== /** * \class MinimumSerial * \brief mini serial class for the %SdFat library. */ -class MinimumSerial : public Print { +class MinimumSerial : public print_t { public: /** \return true for hardware serial */ - operator bool() { return true; } + operator bool() {return true;} /** * \return one if data is available. */ @@ -62,6 +62,6 @@ class MinimumSerial : public Print { * \return 1 */ size_t write(uint8_t b); - using Print::write; + using print_t::write; }; #endif // MinimumSerial_h diff --git a/avr/cores/megacommand/SdFat/RingBuf.h b/avr/cores/megacommand/SdFat/RingBuf.h new file mode 100644 index 000000000..5615b42b7 --- /dev/null +++ b/avr/cores/megacommand/SdFat/RingBuf.h @@ -0,0 +1,366 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef RingBuf_h +#define RingBuf_h +/** + * \file + * \brief Ring buffer for data loggers. + */ +#include "common/SysCall.h" +#include "common/FmtNumber.h" + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +// Teensy 3.5/3.6 has hard fault at 0x20000000 for unaligned memcpy. +#if defined(__MK64FX512__) || defined(__MK66FX1M0__) +inline bool is_aligned(const void* ptr, uintptr_t alignment) { + auto iptr = reinterpret_cast(ptr); + return !(iptr % alignment); +} +inline void memcpyBuf(void* dst, const void* src, size_t len) { + const uint8_t* b = reinterpret_cast(0X20000000UL); + uint8_t* d = reinterpret_cast(dst); + const uint8_t *s = reinterpret_cast(src); + if ((is_aligned(d, 4) && is_aligned(s, 4) && (len & 3) == 0) || + !((d < b && b <= (d + len)) || (s < b && b <= (s + len)))) { + memcpy(dst, src, len); + } else { + while (len--) { + *d++ = *s++; + } + } +} +#else // defined(__MK64FX512__) || defined(__MK66FX1M0__) +inline void memcpyBuf(void* dst, const void* src, size_t len) { + memcpy(dst, src, len); +} +#endif // defined(__MK64FX512__) || defined(__MK66FX1M0__) +#endif // DOXYGEN_SHOULD_SKIP_THIS +/** + * \class RingBuf + * \brief Ring buffer for data loggers. + * + * This ring buffer may be used in ISRs. bytesFreeIsr(), bytesUsedIsr(), + * memcopyIn(), and memcopyOut() are ISR callable. For ISR use call + * memcopyIn() in the ISR and use writeOut() in non-interrupt code + * to write data to a file. readIn() and memcopyOut can be use in a + * similar way to provide file data to an ISR. + * + * Print into a RingBuf in an ISR should also work but has not been verified. + */ +template +class RingBuf : public Print { + public: + /** + * RingBuf Constructor. + */ + RingBuf() {} + /** + * Initialize RingBuf. + * \param[in] file Underlying file. + */ + void begin(F* file) { + m_file = file; + m_count = 0; + m_head = 0; + m_tail = 0; + clearWriteError(); + } + /** + * + * \return the RingBuf free space in bytes. Not ISR callable. + */ + size_t bytesFree() const { + size_t count; + noInterrupts(); + count = m_count; + interrupts(); + return Size - count; + } + /** + * \return the RingBuf free space in bytes. ISR callable. + */ + size_t bytesFreeIsr() const { + return Size - m_count; + } + /** + * \return the RingBuf used space in bytes. Not ISR callable. + */ + size_t bytesUsed() const { + size_t count; + noInterrupts(); + count = m_count; + interrupts(); + return count; + } + /** + * \return the RingBuf used space in bytes. ISR callable. + */ + size_t bytesUsedIsr() const { + return m_count; + } + /** + * Copy data to the RingBuf from buf. + * The number of bytes copied may be less than count if + * count is greater than bytesFree. + * + * This function may be used in an ISR with writeOut() + * in non-interrupt code. + * + * \param[in] buf Location of data to be copied. + * \param[in] count number of bytes to be copied. + * \return Number of bytes actually copied. + */ + size_t memcpyIn(const void* buf, size_t count) { + const uint8_t* src = (const uint8_t*)buf; + size_t n = Size - m_count; + if (count > n) { + count = n; + } + size_t nread = 0; + while (nread != count) { + n = minSize(Size - m_head, count - nread); + memcpyBuf(m_buf + m_head, src + nread, n); + m_head = advance(m_head, n); + nread += n; + } + m_count += nread; + return nread; + } + /** + * Copy date from the RingBuf to buf. + * The number of bytes copied may be less than count if + * bytesUsed is less than count. + * + * This function may be used in an ISR with readIn() in + * non-interrupt code. + * + * \param[out] buf Location to receive the data. + * \param[in] count number of bytes to be copied. + * \return Number of bytes actually copied. + */ + size_t memcpyOut(void* buf, size_t count) { + uint8_t* dst = reinterpret_cast(buf); + size_t nwrite = 0; + size_t n = m_count; + if (count > n) { + count = n; + } + while (nwrite != count) { + n = minSize(Size - m_tail, count - nwrite); + memcpyBuf(dst + nwrite, m_buf + m_tail, n); + m_tail = advance(m_tail, n); + nwrite += n; + } + m_count -= nwrite; + return nwrite; + } + /** Print a number followed by a field terminator. + * \param[in] value The number to be printed. + * \param[in] term The field terminator. Use '\\n' for CR LF. + * \param[in] prec Number of digits after decimal point. + * \return The number of bytes written. + */ + size_t printField(double value, char term, uint8_t prec = 2) { + char buf[24]; + char* str = buf + sizeof(buf); + if (term) { + *--str = term; + if (term == '\n') { + *--str = '\r'; + } + } + str = fmtDouble(str, value, prec, false); + return write(str, buf + sizeof(buf) - str); + } + /** Print a number followed by a field terminator. + * \param[in] value The number to be printed. + * \param[in] term The field terminator. Use '\\n' for CR LF. + * \param[in] prec Number of digits after decimal point. + * \return The number of bytes written or -1 if an error occurs. + */ + size_t printField(float value, char term, uint8_t prec = 2) { + return printField(static_cast(value), term, prec); + } + /** Print a number followed by a field terminator. + * \param[in] value The number to be printed. + * \param[in] term The field terminator. Use '\\n' for CR LF. + * \return The number of bytes written or -1 if an error occurs. + */ + template + size_t printField(Type value, char term) { + char sign = 0; + char buf[3*sizeof(Type) + 3]; + char* str = buf + sizeof(buf); + + if (term) { + *--str = term; + if (term == '\n') { + *--str = '\r'; + } + } + if (value < 0) { + value = -value; + sign = '-'; + } + if (sizeof(Type) < 4) { + str = fmtBase10(str, (uint16_t)value); + } else { + str = fmtBase10(str, (uint32_t)value); + } + if (sign) { + *--str = sign; + } + return write((const uint8_t*)str, &buf[sizeof(buf)] - str); + } + /** + * Read data into the RingBuf from the underlying file. + * the number of bytes read may be less than count if + * bytesFree is less than count. + * + * This function may be used in non-interrupt code with + * memcopyOut() in an ISR. + * + * \param[in] count number of bytes to be read. + * \return Number of bytes actually read. + */ + size_t readIn(size_t count) { + size_t nread = 0; + size_t n = bytesFree(); // Protected from interrupts. + if (count > n) { + count = n; + } + while (nread != count) { + n = minSize(Size - m_head, count - nread); + if ((size_t)m_file->read(m_buf + m_head, n) != n) { + return nread; + } + m_head = advance(m_head, n); + nread += n; + } + noInterrupts(); + m_count += nread; + interrupts(); + return nread; + } + /** + * Write all data in the RingBuf to the underlying file. + * \return true for success. + */ + bool sync() { + size_t n = bytesUsed(); + return writeOut(n) == n; + } + /** + * Copy data to the RingBuf from buf. + * + * The number of bytes copied may be less than count if + * count is greater than bytesFree. + * Use getWriteError() to check for print errors and + * clearWriteError() to clear error. + * + * \param[in] buf Location of data to be written. + * \param[in] count number of bytes to be written. + * \return Number of bytes actually written. + */ + size_t write(const void* buf, size_t count) { + if (count > bytesFree()) { + setWriteError(); + } + return memcpyIn(buf, count); + } + /** + * Copy str to RingBuf. + * + * \param[in] str Location of data to be written. + * \return Number of bytes actually written. + */ + size_t write(const char* str) { + return Print::write(str); + } + /** + * Override virtual function in Print for efficiency. + * + * \param[in] buf Location of data to be written. + * \param[in] count number of bytes to be written. + * \return Number of bytes actually written. + */ + size_t write(const uint8_t* buf, size_t count) override { + return write((const void*)buf, count); + } + /** + * Required function for Print. + * \param[in] data Byte to be written. + * \return Number of bytes actually written. + */ + size_t write(uint8_t data) override { + return write(&data, 1); + } + /** + * Write data to file from RingBuf buffer. + * \param[in] count number of bytes to be written. + * + * The number of bytes written may be less than count if + * bytesUsed is less than count or if an error occurs. + * + * This function may be used in non-interrupt code with + * memcopyIn() in an ISR. + * + * \return Number of bytes actually written. + */ + size_t writeOut(size_t count) { + size_t n = bytesUsed(); // Protected from interrupts; + if (count > n) { + count = n; + } + size_t nwrite = 0; + while (nwrite != count) { + n = minSize(Size - m_tail, count - nwrite); + if (m_file->write(m_buf + m_tail, n) != n) { + break; + } + m_tail = advance(m_tail, n); + nwrite += n; + } + noInterrupts(); + m_count -= nwrite; + interrupts(); + return nwrite; + } + + private: + uint8_t __attribute__((aligned(4))) m_buf[Size]; + F* m_file = nullptr; + volatile size_t m_count; + size_t m_head; + size_t m_tail; + + size_t advance(size_t index, size_t n) { + index += n; + return index < Size ? index : index - Size; + } + // avoid macro MIN + size_t minSize(size_t a, size_t b) {return a < b ? a : b;} +}; +#endif // RingBuf_h diff --git a/avr/cores/megacommand/SdFat/SdCard/CPPLINT.cfg b/avr/cores/megacommand/SdFat/SdCard/CPPLINT.cfg new file mode 100644 index 000000000..7c360c78a --- /dev/null +++ b/avr/cores/megacommand/SdFat/SdCard/CPPLINT.cfg @@ -0,0 +1 @@ +exclude_files=SdioTeensy.h \ No newline at end of file diff --git a/avr/cores/megacommand/SdFat/SdCard/SdCard.h b/avr/cores/megacommand/SdFat/SdCard/SdCard.h new file mode 100644 index 000000000..4c3fadb30 --- /dev/null +++ b/avr/cores/megacommand/SdFat/SdCard/SdCard.h @@ -0,0 +1,82 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef SdCard_h +#define SdCard_h +#include "SdioCard.h" +#include "SdSpiCard.h" +#if HAS_SDIO_CLASS +typedef SdCardInterface SdCard; +#else // HAS_SDIO_CLASS +typedef SdSpiCard SdCard; +#endif // HAS_SDIO_CLASS +/** Determine card configuration type. + * + * \param[in] cfg Card configuration. + * \return true if SPI. + */ +inline bool isSpi(SdSpiConfig cfg) {(void)cfg; return true;} +/** Determine card configuration type. + * + * \param[in] cfg Card configuration. + * \return true if SPI. + */ +inline bool isSpi(SdioConfig cfg) {(void)cfg; return false;} +/** + * \class SdCardFactory + * \brief Setup a SPI card or SDIO card. + */ +class SdCardFactory { + public: + /** Initialize SPI card. + * + * \param[in] config SPI configuration. + * \return generic card pointer. + */ + SdCard* newCard(SdSpiConfig config) { + m_spiCard.begin(config); + return &m_spiCard; + } + /** Initialize SDIO card. + * + * \param[in] config SDIO configuration. + * \return generic card pointer or nullptr if SDIO is not supported. + */ + SdCard* newCard(SdioConfig config) { +#if HAS_SDIO_CLASS + m_sdioCard.begin(config); + return &m_sdioCard; +#else // HAS_SDIO_CLASS + (void)config; + return nullptr; +#endif // HAS_SDIO_CLASS + } + + private: +#if HAS_SDIO_CLASS + SdioCard m_sdioCard; +#endif // HAS_SDIO_CLASS + SdSpiCard m_spiCard; +}; +#endif // SdCard_h diff --git a/avr/cores/megacommand/SdFat/SdCard/SdCardInfo.cpp b/avr/cores/megacommand/SdFat/SdCard/SdCardInfo.cpp new file mode 100644 index 000000000..8b3d15cc8 --- /dev/null +++ b/avr/cores/megacommand/SdFat/SdCard/SdCardInfo.cpp @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "SdCardInfo.h" +//------------------------------------------------------------------------------ +#undef SD_CARD_ERROR +#define SD_CARD_ERROR(e, m) case SD_CARD_ERROR_##e: pr->print(F(#e)); break; +void printSdErrorSymbol(print_t* pr, uint8_t code) { + pr->print(F("SD_CARD_ERROR_")); + switch (code) { + SD_ERROR_CODE_LIST + default: pr->print(F("UNKNOWN")); + } +} +//------------------------------------------------------------------------------ +#undef SD_CARD_ERROR +#define SD_CARD_ERROR(e, m) case SD_CARD_ERROR_##e: pr->print(F(m)); break; +void printSdErrorText(print_t* pr, uint8_t code) { + switch + (code) { + SD_ERROR_CODE_LIST + default: pr->print(F("Unknown error")); + } +} diff --git a/avr/cores/megacommand/SdFat/SdCard/SdCardInfo.h b/avr/cores/megacommand/SdFat/SdCard/SdCardInfo.h new file mode 100644 index 000000000..b6820c423 --- /dev/null +++ b/avr/cores/megacommand/SdFat/SdCard/SdCardInfo.h @@ -0,0 +1,420 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef SdCardInfo_h +#define SdCardInfo_h +#include +#include "../common/SysCall.h" +// Based on the document: +// +// SD Specifications +// Part 1 +// Physical Layer +// Simplified Specification +// Version 8.00 +// Sep 23, 2020 +// +// https://www.sdcard.org/downloads/pls/ +#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__ +// SD registers are big endian. +#error bit fields in structures assume little endian processor. +#endif // __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__ +//------------------------------------------------------------------------------ +// SD card errors +// See the SD Specification for command info. +#define SD_ERROR_CODE_LIST\ + SD_CARD_ERROR(NONE, "No error")\ + SD_CARD_ERROR(CMD0, "Card reset failed")\ + SD_CARD_ERROR(CMD2, "SDIO read CID")\ + SD_CARD_ERROR(CMD3, "SDIO publish RCA")\ + SD_CARD_ERROR(CMD6, "Switch card function")\ + SD_CARD_ERROR(CMD7, "SDIO card select")\ + SD_CARD_ERROR(CMD8, "Send and check interface settings")\ + SD_CARD_ERROR(CMD9, "Read CSD data")\ + SD_CARD_ERROR(CMD10, "Read CID data")\ + SD_CARD_ERROR(CMD12, "Stop multiple block read")\ + SD_CARD_ERROR(CMD13, "Read card status")\ + SD_CARD_ERROR(CMD17, "Read single block")\ + SD_CARD_ERROR(CMD18, "Read multiple blocks")\ + SD_CARD_ERROR(CMD24, "Write single block")\ + SD_CARD_ERROR(CMD25, "Write multiple blocks")\ + SD_CARD_ERROR(CMD32, "Set first erase block")\ + SD_CARD_ERROR(CMD33, "Set last erase block")\ + SD_CARD_ERROR(CMD38, "Erase selected blocks")\ + SD_CARD_ERROR(CMD58, "Read OCR register")\ + SD_CARD_ERROR(CMD59, "Set CRC mode")\ + SD_CARD_ERROR(ACMD6, "Set SDIO bus width")\ + SD_CARD_ERROR(ACMD13, "Read extended status")\ + SD_CARD_ERROR(ACMD23, "Set pre-erased count")\ + SD_CARD_ERROR(ACMD41, "Activate card initialization")\ + SD_CARD_ERROR(ACMD51, "Read SCR data")\ + SD_CARD_ERROR(READ_TOKEN, "Bad read data token")\ + SD_CARD_ERROR(READ_CRC, "Read CRC error")\ + SD_CARD_ERROR(READ_FIFO, "SDIO fifo read timeout")\ + SD_CARD_ERROR(READ_REG, "Read CID or CSD failed.")\ + SD_CARD_ERROR(READ_START, "Bad readStart argument")\ + SD_CARD_ERROR(READ_TIMEOUT, "Read data timeout")\ + SD_CARD_ERROR(STOP_TRAN, "Multiple block stop failed")\ + SD_CARD_ERROR(TRANSFER_COMPLETE, "SDIO transfer complete")\ + SD_CARD_ERROR(WRITE_DATA, "Write data not accepted")\ + SD_CARD_ERROR(WRITE_FIFO, "SDIO fifo write timeout")\ + SD_CARD_ERROR(WRITE_START, "Bad writeStart argument")\ + SD_CARD_ERROR(WRITE_PROGRAMMING, "Flash programming")\ + SD_CARD_ERROR(WRITE_TIMEOUT, "Write timeout")\ + SD_CARD_ERROR(DMA, "DMA transfer failed")\ + SD_CARD_ERROR(ERASE, "Card did not accept erase commands")\ + SD_CARD_ERROR(ERASE_SINGLE_SECTOR, "Card does not support erase")\ + SD_CARD_ERROR(ERASE_TIMEOUT, "Erase command timeout")\ + SD_CARD_ERROR(INIT_NOT_CALLED, "Card has not been initialized")\ + SD_CARD_ERROR(INVALID_CARD_CONFIG, "Invalid card config")\ + SD_CARD_ERROR(FUNCTION_NOT_SUPPORTED, "Unsupported SDIO command") + +enum { +#define SD_CARD_ERROR(e, m) SD_CARD_ERROR_##e, + SD_ERROR_CODE_LIST +#undef SD_CARD_ERROR + SD_CARD_ERROR_UNKNOWN +}; +void printSdErrorSymbol(print_t* pr, uint8_t code); +void printSdErrorText(print_t* pr, uint8_t code); +//------------------------------------------------------------------------------ +// card types +/** Standard capacity V1 SD card */ +const uint8_t SD_CARD_TYPE_SD1 = 1; +/** Standard capacity V2 SD card */ +const uint8_t SD_CARD_TYPE_SD2 = 2; +/** High Capacity SD card */ +const uint8_t SD_CARD_TYPE_SDHC = 3; +//------------------------------------------------------------------------------ +// SD operation timeouts +/** CMD0 retry count */ +const uint8_t SD_CMD0_RETRY = 10; +/** command timeout ms */ +const uint16_t SD_CMD_TIMEOUT = 300; +/** erase timeout ms */ +const uint16_t SD_ERASE_TIMEOUT = 10000; +/** init timeout ms */ +const uint16_t SD_INIT_TIMEOUT = 2000; +/** read timeout ms */ +const uint16_t SD_READ_TIMEOUT = 300; +/** write time out ms */ +const uint16_t SD_WRITE_TIMEOUT = 600; +//------------------------------------------------------------------------------ +// SD card commands +/** GO_IDLE_STATE - init card in spi mode if CS low */ +const uint8_t CMD0 = 0X00; +/** ALL_SEND_CID - Asks any card to send the CID. */ +const uint8_t CMD2 = 0X02; +/** SEND_RELATIVE_ADDR - Ask the card to publish a new RCA. */ +const uint8_t CMD3 = 0X03; +/** SWITCH_FUNC - Switch Function Command */ +const uint8_t CMD6 = 0X06; +/** SELECT/DESELECT_CARD - toggles between the stand-by and transfer states. */ +const uint8_t CMD7 = 0X07; +/** SEND_IF_COND - verify SD Memory Card interface operating condition.*/ +const uint8_t CMD8 = 0X08; +/** SEND_CSD - read the Card Specific Data (CSD register) */ +const uint8_t CMD9 = 0X09; +/** SEND_CID - read the card identification information (CID register) */ +const uint8_t CMD10 = 0X0A; +/** VOLTAGE_SWITCH -Switch to 1.8V bus signaling level. */ +const uint8_t CMD11 = 0X0B; +/** STOP_TRANSMISSION - end multiple sector read sequence */ +const uint8_t CMD12 = 0X0C; +/** SEND_STATUS - read the card status register */ +const uint8_t CMD13 = 0X0D; +/** READ_SINGLE_SECTOR - read a single data sector from the card */ +const uint8_t CMD17 = 0X11; +/** READ_MULTIPLE_SECTOR - read multiple data sectors from the card */ +const uint8_t CMD18 = 0X12; +/** WRITE_SECTOR - write a single data sector to the card */ +const uint8_t CMD24 = 0X18; +/** WRITE_MULTIPLE_SECTOR - write sectors of data until a STOP_TRANSMISSION */ +const uint8_t CMD25 = 0X19; +/** ERASE_WR_BLK_START - sets the address of the first sector to be erased */ +const uint8_t CMD32 = 0X20; +/** ERASE_WR_BLK_END - sets the address of the last sector of the continuous + range to be erased*/ +const uint8_t CMD33 = 0X21; +/** ERASE - erase all previously selected sectors */ +const uint8_t CMD38 = 0X26; +/** APP_CMD - escape for application specific command */ +const uint8_t CMD55 = 0X37; +/** READ_OCR - read the OCR register of a card */ +const uint8_t CMD58 = 0X3A; +/** CRC_ON_OFF - enable or disable CRC checking */ +const uint8_t CMD59 = 0X3B; +/** SET_BUS_WIDTH - Defines the data bus width for data transfer. */ +const uint8_t ACMD6 = 0X06; +/** SD_STATUS - Send the SD Status. */ +const uint8_t ACMD13 = 0X0D; +/** SET_WR_BLK_ERASE_COUNT - Set the number of write sectors to be + pre-erased before writing */ +const uint8_t ACMD23 = 0X17; +/** SD_SEND_OP_COMD - Sends host capacity support information and + activates the card's initialization process */ +const uint8_t ACMD41 = 0X29; +/** Reads the SD Configuration Register (SCR). */ +const uint8_t ACMD51 = 0X33; +//============================================================================== +// CARD_STATUS +/** The command's argument was out of the allowed range for this card. */ +const uint32_t CARD_STATUS_OUT_OF_RANGE = 1UL << 31; +/** A misaligned address which did not match the sector length. */ +const uint32_t CARD_STATUS_ADDRESS_ERROR = 1UL << 30; +/** The transferred sector length is not allowed for this card. */ +const uint32_t CARD_STATUS_SECTOR_LEN_ERROR = 1UL << 29; +/** An error in the sequence of erase commands occurred. */ +const uint32_t CARD_STATUS_ERASE_SEQ_ERROR = 1UL <<28; +/** An invalid selection of write-sectors for erase occurred. */ +const uint32_t CARD_STATUS_ERASE_PARAM = 1UL << 27; +/** Set when the host attempts to write to a protected sector. */ +const uint32_t CARD_STATUS_WP_VIOLATION = 1UL << 26; +/** When set, signals that the card is locked by the host. */ +const uint32_t CARD_STATUS_CARD_IS_LOCKED = 1UL << 25; +/** Set when a sequence or password error has been detected. */ +const uint32_t CARD_STATUS_LOCK_UNLOCK_FAILED = 1UL << 24; +/** The CRC check of the previous command failed. */ +const uint32_t CARD_STATUS_COM_CRC_ERROR = 1UL << 23; +/** Command not legal for the card state. */ +const uint32_t CARD_STATUS_ILLEGAL_COMMAND = 1UL << 22; +/** Card internal ECC was applied but failed to correct the data. */ +const uint32_t CARD_STATUS_CARD_ECC_FAILED = 1UL << 21; +/** Internal card controller error */ +const uint32_t CARD_STATUS_CC_ERROR = 1UL << 20; +/** A general or an unknown error occurred during the operation. */ +const uint32_t CARD_STATUS_ERROR = 1UL << 19; +// bits 19, 18, and 17 reserved. +/** Permanent WP set or attempt to change read only values of CSD. */ +const uint32_t CARD_STATUS_CSD_OVERWRITE = 1UL <<16; +/** partial address space was erased due to write protect. */ +const uint32_t CARD_STATUS_WP_ERASE_SKIP = 1UL << 15; +/** The command has been executed without using the internal ECC. */ +const uint32_t CARD_STATUS_CARD_ECC_DISABLED = 1UL << 14; +/** out of erase sequence command was received. */ +const uint32_t CARD_STATUS_ERASE_RESET = 1UL << 13; +/** The state of the card when receiving the command. + * 0 = idle + * 1 = ready + * 2 = ident + * 3 = stby + * 4 = tran + * 5 = data + * 6 = rcv + * 7 = prg + * 8 = dis + * 9-14 = reserved + * 15 = reserved for I/O mode + */ +const uint32_t CARD_STATUS_CURRENT_STATE = 0XF << 9; +/** Shift for current state. */ +const uint32_t CARD_STATUS_CURRENT_STATE_SHIFT = 9; +/** Corresponds to buffer empty signaling on the bus. */ +const uint32_t CARD_STATUS_READY_FOR_DATA = 1UL << 8; +// bit 7 reserved. +/** Extension Functions may set this bit to get host to deal with events. */ +const uint32_t CARD_STATUS_FX_EVENT = 1UL << 6; +/** The card will expect ACMD, or the command has been interpreted as ACMD */ +const uint32_t CARD_STATUS_APP_CMD = 1UL << 5; +// bit 4 reserved. +/** Error in the sequence of the authentication process. */ +const uint32_t CARD_STATUS_AKE_SEQ_ERROR = 1UL << 3; +// bits 2,1, and 0 reserved for manufacturer test mode. +//============================================================================== +/** status for card in the ready state */ +const uint8_t R1_READY_STATE = 0X00; +/** status for card in the idle state */ +const uint8_t R1_IDLE_STATE = 0X01; +/** status bit for illegal command */ +const uint8_t R1_ILLEGAL_COMMAND = 0X04; +/** start data token for read or write single sector*/ +const uint8_t DATA_START_SECTOR = 0XFE; +/** stop token for write multiple sectors*/ +const uint8_t STOP_TRAN_TOKEN = 0XFD; +/** start data token for write multiple sectors*/ +const uint8_t WRITE_MULTIPLE_TOKEN = 0XFC; +/** mask for data response tokens after a write sector operation */ +const uint8_t DATA_RES_MASK = 0X1F; +/** write data accepted token */ +const uint8_t DATA_RES_ACCEPTED = 0X05; +//============================================================================== +/** + * \class CID + * \brief Card IDentification (CID) register. + */ +typedef struct CID { + // byte 0 + /** Manufacturer ID */ + uint8_t mid; + // byte 1-2 + /** OEM/Application ID. */ + char oid[2]; + // byte 3-7 + /** Product name. */ + char pnm[5]; + // byte 8 + /** Product revision - n.m two 4-bit nibbles. */ + uint8_t prv; + // byte 9-12 + /** Product serial 32-bit number Big Endian format. */ + uint8_t psn8[4]; + // byte 13-14 + /** Manufacturing date big endian - four nibbles RYYM Reserved Year Month. */ + uint8_t mdt[2]; + // byte 15 + /** CRC7 bits 1-7 checksum, bit 0 always 1 */ + uint8_t crc; + // Extract big endian fields. + /** \return major revision number. */ + int prvN() const {return prv >> 4;} + /** \return minor revision number. */ + int prvM() const {return prv & 0XF;} + /** \return Manufacturing Year. */ + int mdtYear() const {return 2000 + ((mdt[0] & 0XF) << 4) + (mdt[1] >> 4);} + /** \return Manufacturing Month. */ + int mdtMonth() const {return mdt[1] & 0XF;} + /** \return Product Serial Number. */ + uint32_t psn() const { + return (uint32_t)psn8[0] << 24 | + (uint32_t)psn8[1] << 16 | + (uint32_t)psn8[2] << 8 | + (uint32_t)psn8[3]; + } +} __attribute__((packed)) cid_t; +//============================================================================== +/** + * \class CSD + * \brief Union of old and new style CSD register. + */ +typedef struct CSD { + /** union of all CSD versions */ + uint8_t csd[16]; + // Extract big endian fields. + /** \return Capacity in sectors */ + uint32_t capacity() const { + uint32_t c_size; + uint8_t ver = csd[0] >> 6; + if (ver == 0) { + c_size = (uint32_t)(csd[6] & 3) << 10; + c_size |= (uint32_t)csd[7] << 2 | csd[8] >> 6; + uint8_t c_size_mult = (csd[9] & 3) << 1 | csd[10] >> 7; + uint8_t read_bl_len = csd[5] & 15; + return (c_size + 1) << (c_size_mult + read_bl_len + 2 - 9); + } else if (ver == 1) { + c_size = (uint32_t)(csd[7] & 63) << 16; + c_size |= (uint32_t)csd[8] << 8; + c_size |= csd[9]; + return (c_size + 1) << 10; + } else { + return 0; + } + } + /** \return true if erase granularity is single block. */ + bool eraseSingleBlock() const {return csd[10] & 0X40;} + /** \return erase size in 512 byte blocks if eraseSingleBlock is false. */ + int eraseSize() const {return ((csd[10] & 0X3F) << 1 | csd[11] >> 7) + 1;} + /** \return true if the contents is copied or true if original. */ + bool copy() const {return csd[14] & 0X40;} + /** \return true if the entire card is permanently write protected. */ + bool permWriteProtect() const {return csd[14] & 0X20;} + /** \return true if the entire card is temporarily write protected. */ + bool tempWriteProtect() const {return csd[14] & 0X10;} +} csd_t; +//============================================================================== +/** + * \class SCR + * \brief SCR register. + */ +typedef struct SCR { + /** Bytes 0-3 SD Association, bytes 4-7 reserved for manufacturer. */ + uint8_t scr[8]; + /** \return SCR_STRUCTURE field - must be zero.*/ + uint8_t srcStructure() {return scr[0] >> 4;} + /** \return SD_SPEC field 0 - v1.0 or V1.01, 1 - 1.10, 2 - V2.00 or greater */ + uint8_t sdSpec() {return scr[0] & 0XF;} + /** \return false if all zero, true if all one. */ + bool dataAfterErase() {return 0X80 & scr[1];} + /** \return CPRM Security Version. */ + uint8_t sdSecurity() {return (scr[1] >> 4) & 0X7;} + /** \return 0101b. */ + uint8_t sdBusWidths() {return scr[1] & 0XF;} + /** \return true if V3.0 or greater. */ + bool sdSpec3() {return scr[2] & 0X80;} + /** \return if true and sdSpecX is zero V4.xx. */ + bool sdSpec4() {return scr[2] & 0X4;} + /** \return nonzero for version 5 or greater if sdSpec == 2, + sdSpec3 == true. Version is return plus four.*/ + uint8_t sdSpecX() {return (scr[2] & 0X3) << 2 | scr[3] >> 6;} + /** \return bit map for support CMD58/59, CMD48/49, CMD23, and CMD20 */ + uint8_t cmdSupport() {return scr[3] &0XF;} + /** \return SD spec version */ + int16_t sdSpecVer() { + if (sdSpec() > 2) { + return -1; + } else if (sdSpec() < 2) { + return sdSpec() ? 110 : 101; + } else if (!sdSpec3()) { + return 200; + } else if (!sdSpec4() && !sdSpecX()) { + return 300; + } + return 400 + 100*sdSpecX(); + } +} scr_t; +//============================================================================== +#ifndef DOXYGEN_SHOULD_SKIP_THIS +// fields are big endian +typedef struct SdStatus { + // + uint8_t busWidthSecureMode; + uint8_t reserved1; + // byte 2 + uint8_t sdCardType[2]; + // byte 4 + uint8_t sizeOfProtectedArea[4]; + // byte 8 + uint8_t speedClass; + // byte 9 + uint8_t performanceMove; + // byte 10 + uint8_t auSize; + // byte 11 + uint8_t eraseSize[2]; + // byte 13 + uint8_t eraseTimeoutOffset; + // byte 14 + uint8_t uhsSpeedAuSize; + // byte 15 + uint8_t videoSpeed; + // byte 16 + uint8_t vscAuSize[2]; + // byte 18 + uint8_t susAddr[3]; + // byte 21 + uint8_t reserved2[3]; + // byte 24 + uint8_t reservedManufacturer[40]; +} SdStatus_t; +#endif // DOXYGEN_SHOULD_SKIP_THIS +#endif // SdCardInfo_h diff --git a/avr/cores/megacommand/SdFat/SdCard/SdCardInterface.h b/avr/cores/megacommand/SdFat/SdCard/SdCardInterface.h new file mode 100644 index 000000000..e773e3ef7 --- /dev/null +++ b/avr/cores/megacommand/SdFat/SdCard/SdCardInterface.h @@ -0,0 +1,129 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef SdCardInterface_h +#define SdCardInterface_h +#include "../common/FsBlockDeviceInterface.h" +#include "SdCardInfo.h" +/** + * \class SdCardInterface + * \brief Abstract interface for an SD card. + */ +class SdCardInterface : public FsBlockDeviceInterface { + public: + /** CMD6 Switch mode: Check Function Set Function. + * \param[in] arg CMD6 argument. + * \param[out] status return status data. + * + * \return true for success or false for failure. + */ + virtual bool cardCMD6(uint32_t arg, uint8_t* status) = 0; + /** end use of card */ + virtual void end() = 0; + /** Erase a range of sectors. + * + * \param[in] firstSector The address of the first sector in the range. + * \param[in] lastSector The address of the last sector in the range. + * + * \return true for success or false for failure. + */ + virtual bool erase(uint32_t firstSector, uint32_t lastSector) = 0; + /** \return error code. */ + virtual uint8_t errorCode() const = 0; + /** \return error data. */ + virtual uint32_t errorData() const = 0; + /** \return true if card is busy. */ + virtual bool isBusy() = 0; + /** \return false by default */ + virtual bool hasDedicatedSpi() {return false;} + /** \return false by default */ + bool virtual isDedicatedSpi() {return false;} + /** Set SPI sharing state + * \param[in] value desired state. + * \return false by default. + */ + virtual bool setDedicatedSpi(bool value) { + (void)value; + return false; + } + /** + * Read a card's CID register. + * + * \param[out] cid pointer to area for returned data. + * + * \return true for success or false for failure. + */ + virtual bool readCID(cid_t* cid) = 0; + /** + * Read a card's CSD register. + * + * \param[out] csd pointer to area for returned data. + * + * \return true for success or false for failure. + */ + virtual bool readCSD(csd_t* csd) = 0; + /** Read OCR register. + * + * \param[out] ocr Value of OCR register. + * \return true for success or false for failure. + */ + virtual bool readOCR(uint32_t* ocr) = 0; + /** Read SCR register. + * + * \param[out] scr Value of SCR register. + * \return true for success or false for failure. + */ + virtual bool readSCR(scr_t *scr) = 0; + /** + * Determine the size of an SD flash memory card. + * + * \return The number of 512 byte data sectors in the card + * or zero if an error occurs. + */ + virtual uint32_t sectorCount() = 0; + /** \return card status. */ + virtual uint32_t status() {return 0XFFFFFFFF;} + /** Return the card type: SD V1, SD V2 or SDHC/SDXC + * \return 0 - SD V1, 1 - SD V2, or 3 - SDHC/SDXC. + */ + virtual uint8_t type() const = 0; + /** Write one data sector in a multiple sector write sequence. + * \param[in] src Pointer to the location of the data to be written. + * \return true for success or false for failure. + */ + + virtual bool writeData(const uint8_t* src) = 0; + /** Start a write multiple sectors sequence. + * + * \param[in] sector Address of first sector in sequence. + * + * \return true for success or false for failure. + */ + virtual bool writeStart(uint32_t sector) = 0; + /** End a write multiple sectors sequence. + * \return true for success or false for failure. + */ + virtual bool writeStop() = 0; +}; +#endif // SdCardInterface_h diff --git a/avr/cores/megacommand/SdFat/SdCard/SdInfo.h b/avr/cores/megacommand/SdFat/SdCard/SdInfo.h deleted file mode 100644 index 867981415..000000000 --- a/avr/cores/megacommand/SdFat/SdCard/SdInfo.h +++ /dev/null @@ -1,480 +0,0 @@ -/** - * Copyright (c) 20011-2017 Bill Greiman - * This file is part of the SdFat library for SD memory cards. - * - * MIT License - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ -#ifndef SdInfo_h -#define SdInfo_h -#include -// Based on the document: -// -// SD Specifications -// Part 1 -// Physical Layer -// Simplified Specification -// Version 5.00 -// Aug 10, 2016 -// -// https://www.sdcard.org/downloads/pls/ -//------------------------------------------------------------------------------ -// SD card errors -// See the SD Specification for command info. -typedef enum { - SD_CARD_ERROR_NONE = 0, - - // Basic commands and switch command. - SD_CARD_ERROR_CMD0 = 0X20, - SD_CARD_ERROR_CMD2, - SD_CARD_ERROR_CMD3, - SD_CARD_ERROR_CMD6, - SD_CARD_ERROR_CMD7, - SD_CARD_ERROR_CMD8, - SD_CARD_ERROR_CMD9, - SD_CARD_ERROR_CMD10, - SD_CARD_ERROR_CMD12, - SD_CARD_ERROR_CMD13, - - // Read, write, erase, and extension commands. - SD_CARD_ERROR_CMD17 = 0X30, - SD_CARD_ERROR_CMD18, - SD_CARD_ERROR_CMD24, - SD_CARD_ERROR_CMD25, - SD_CARD_ERROR_CMD32, - SD_CARD_ERROR_CMD33, - SD_CARD_ERROR_CMD38, - SD_CARD_ERROR_CMD58, - SD_CARD_ERROR_CMD59, - - // Application specific commands. - SD_CARD_ERROR_ACMD6 = 0X40, - SD_CARD_ERROR_ACMD13, - SD_CARD_ERROR_ACMD23, - SD_CARD_ERROR_ACMD41, - - // Read/write errors - SD_CARD_ERROR_READ = 0X50, - SD_CARD_ERROR_READ_CRC, - SD_CARD_ERROR_READ_FIFO, - SD_CARD_ERROR_READ_REG, - SD_CARD_ERROR_READ_START, - SD_CARD_ERROR_READ_TIMEOUT, - SD_CARD_ERROR_STOP_TRAN, - SD_CARD_ERROR_WRITE, - SD_CARD_ERROR_WRITE_FIFO, - SD_CARD_ERROR_WRITE_START, - SD_CARD_ERROR_WRITE_TIMEOUT, - - // Misc errors. - SD_CARD_ERROR_DMA = 0X60, - SD_CARD_ERROR_ERASE, - SD_CARD_ERROR_ERASE_SINGLE_BLOCK, - SD_CARD_ERROR_ERASE_TIMEOUT, - SD_CARD_ERROR_INIT_NOT_CALLED, - SD_CARD_ERROR_FUNCTION_NOT_SUPPORTED -} sd_error_code_t; -//------------------------------------------------------------------------------ -// card types -/** Standard capacity V1 SD card */ -const uint8_t SD_CARD_TYPE_SD1 = 1; -/** Standard capacity V2 SD card */ -const uint8_t SD_CARD_TYPE_SD2 = 2; -/** High Capacity SD card */ -const uint8_t SD_CARD_TYPE_SDHC = 3; -//------------------------------------------------------------------------------ -#define SD_SCK_HZ(maxSpeed) SPISettings(maxSpeed, MSBFIRST, SPI_MODE0) -#define SD_SCK_MHZ(maxMhz) SPISettings(1000000UL*maxMhz, MSBFIRST, SPI_MODE0) -// SPI divisor constants -/** Set SCK to max rate of F_CPU/2. */ -#define SPI_FULL_SPEED SD_SCK_MHZ(50) -/** Set SCK rate to F_CPU/3 for Due */ -#define SPI_DIV3_SPEED SD_SCK_HZ(F_CPU/3) -/** Set SCK rate to F_CPU/4. */ -#define SPI_HALF_SPEED SD_SCK_HZ(F_CPU/4) -/** Set SCK rate to F_CPU/6 for Due */ -#define SPI_DIV6_SPEED SD_SCK_HZ(F_CPU/6) -/** Set SCK rate to F_CPU/8. */ -#define SPI_QUARTER_SPEED SD_SCK_HZ(F_CPU/8) -/** Set SCK rate to F_CPU/16. */ -#define SPI_EIGHTH_SPEED SD_SCK_HZ(F_CPU/16) -/** Set SCK rate to F_CPU/32. */ -#define SPI_SIXTEENTH_SPEED SD_SCK_HZ(F_CPU/32) -//------------------------------------------------------------------------------ -// SD operation timeouts -/** init timeout ms */ -const uint16_t SD_INIT_TIMEOUT = 2000; -/** erase timeout ms */ -const uint16_t SD_ERASE_TIMEOUT = 10000; -/** read timeout ms */ -const uint16_t SD_READ_TIMEOUT = 300; -/** write time out ms */ -const uint16_t SD_WRITE_TIMEOUT = 600; -//------------------------------------------------------------------------------ -// SD card commands -/** GO_IDLE_STATE - init card in spi mode if CS low */ -const uint8_t CMD0 = 0X00; -/** ALL_SEND_CID - Asks any card to send the CID. */ -const uint8_t CMD2 = 0X02; -/** SEND_RELATIVE_ADDR - Ask the card to publish a new RCA. */ -const uint8_t CMD3 = 0X03; -/** SWITCH_FUNC - Switch Function Command */ -const uint8_t CMD6 = 0X06; -/** SELECT/DESELECT_CARD - toggles between the stand-by and transfer states. */ -const uint8_t CMD7 = 0X07; -/** SEND_IF_COND - verify SD Memory Card interface operating condition.*/ -const uint8_t CMD8 = 0X08; -/** SEND_CSD - read the Card Specific Data (CSD register) */ -const uint8_t CMD9 = 0X09; -/** SEND_CID - read the card identification information (CID register) */ -const uint8_t CMD10 = 0X0A; -/** STOP_TRANSMISSION - end multiple block read sequence */ -const uint8_t CMD12 = 0X0C; -/** SEND_STATUS - read the card status register */ -const uint8_t CMD13 = 0X0D; -/** READ_SINGLE_BLOCK - read a single data block from the card */ -const uint8_t CMD17 = 0X11; -/** READ_MULTIPLE_BLOCK - read a multiple data blocks from the card */ -const uint8_t CMD18 = 0X12; -/** WRITE_BLOCK - write a single data block to the card */ -const uint8_t CMD24 = 0X18; -/** WRITE_MULTIPLE_BLOCK - write blocks of data until a STOP_TRANSMISSION */ -const uint8_t CMD25 = 0X19; -/** ERASE_WR_BLK_START - sets the address of the first block to be erased */ -const uint8_t CMD32 = 0X20; -/** ERASE_WR_BLK_END - sets the address of the last block of the continuous - range to be erased*/ -const uint8_t CMD33 = 0X21; -/** ERASE - erase all previously selected blocks */ -const uint8_t CMD38 = 0X26; -/** APP_CMD - escape for application specific command */ -const uint8_t CMD55 = 0X37; -/** READ_OCR - read the OCR register of a card */ -const uint8_t CMD58 = 0X3A; -/** CRC_ON_OFF - enable or disable CRC checking */ -const uint8_t CMD59 = 0X3B; -/** SET_BUS_WIDTH - Defines the data bus width for data transfer. */ -const uint8_t ACMD6 = 0X06; -/** SD_STATUS - Send the SD Status. */ -const uint8_t ACMD13 = 0X0D; -/** SET_WR_BLK_ERASE_COUNT - Set the number of write blocks to be - pre-erased before writing */ -const uint8_t ACMD23 = 0X17; -/** SD_SEND_OP_COMD - Sends host capacity support information and - activates the card's initialization process */ -const uint8_t ACMD41 = 0X29; -//============================================================================== -// CARD_STATUS -/** The command's argument was out of the allowed range for this card. */ -const uint32_t CARD_STATUS_OUT_OF_RANGE = 1UL << 31; -/** A misaligned address which did not match the block length. */ -const uint32_t CARD_STATUS_ADDRESS_ERROR = 1UL << 30; -/** The transferred block length is not allowed for this card. */ -const uint32_t CARD_STATUS_BLOCK_LEN_ERROR = 1UL << 29; -/** An error in the sequence of erase commands occurred. */ -const uint32_t CARD_STATUS_ERASE_SEQ_ERROR = 1UL <<28; -/** An invalid selection of write-blocks for erase occurred. */ -const uint32_t CARD_STATUS_ERASE_PARAM = 1UL << 27; -/** Set when the host attempts to write to a protected block. */ -const uint32_t CARD_STATUS_WP_VIOLATION = 1UL << 26; -/** When set, signals that the card is locked by the host. */ -const uint32_t CARD_STATUS_CARD_IS_LOCKED = 1UL << 25; -/** Set when a sequence or password error has been detected. */ -const uint32_t CARD_STATUS_LOCK_UNLOCK_FAILED = 1UL << 24; -/** The CRC check of the previous command failed. */ -const uint32_t CARD_STATUS_COM_CRC_ERROR = 1UL << 23; -/** Command not legal for the card state. */ -const uint32_t CARD_STATUS_ILLEGAL_COMMAND = 1UL << 22; -/** Card internal ECC was applied but failed to correct the data. */ -const uint32_t CARD_STATUS_CARD_ECC_FAILED = 1UL << 21; -/** Internal card controller error */ -const uint32_t CARD_STATUS_CC_ERROR = 1UL << 20; -/** A general or an unknown error occurred during the operation. */ -const uint32_t CARD_STATUS_ERROR = 1UL << 19; -// bits 19, 18, and 17 reserved. -/** Permanent WP set or attempt to change read only values of CSD. */ -const uint32_t CARD_STATUS_CSD_OVERWRITE = 1UL <<16; -/** partial address space was erased due to write protect. */ -const uint32_t CARD_STATUS_WP_ERASE_SKIP = 1UL << 15; -/** The command has been executed without using the internal ECC. */ -const uint32_t CARD_STATUS_CARD_ECC_DISABLED = 1UL << 14; -/** out of erase sequence command was received. */ -const uint32_t CARD_STATUS_ERASE_RESET = 1UL << 13; -/** The state of the card when receiving the command. - * 0 = idle - * 1 = ready - * 2 = ident - * 3 = stby - * 4 = tran - * 5 = data - * 6 = rcv - * 7 = prg - * 8 = dis - * 9-14 = reserved - * 15 = reserved for I/O mode - */ -const uint32_t CARD_STATUS_CURRENT_STATE = 0XF << 9; -/** Shift for current state. */ -const uint32_t CARD_STATUS_CURRENT_STATE_SHIFT = 9; -/** Corresponds to buffer empty signaling on the bus. */ -const uint32_t CARD_STATUS_READY_FOR_DATA = 1UL << 8; -// bit 7 reserved. -/** Extension Functions may set this bit to get host to deal with events. */ -const uint32_t CARD_STATUS_FX_EVENT = 1UL << 6; -/** The card will expect ACMD, or the command has been interpreted as ACMD */ -const uint32_t CARD_STATUS_APP_CMD = 1UL << 5; -// bit 4 reserved. -/** Error in the sequence of the authentication process. */ -const uint32_t CARD_STATUS_AKE_SEQ_ERROR = 1UL << 3; -// bits 2,1, and 0 reserved for manufacturer test mode. -//============================================================================== -/** status for card in the ready state */ -const uint8_t R1_READY_STATE = 0X00; -/** status for card in the idle state */ -const uint8_t R1_IDLE_STATE = 0X01; -/** status bit for illegal command */ -const uint8_t R1_ILLEGAL_COMMAND = 0X04; -/** start data token for read or write single block*/ -const uint8_t DATA_START_BLOCK = 0XFE; -/** stop token for write multiple blocks*/ -const uint8_t STOP_TRAN_TOKEN = 0XFD; -/** start data token for write multiple blocks*/ -const uint8_t WRITE_MULTIPLE_TOKEN = 0XFC; -/** mask for data response tokens after a write block operation */ -const uint8_t DATA_RES_MASK = 0X1F; -/** write data accepted token */ -const uint8_t DATA_RES_ACCEPTED = 0X05; -//============================================================================== -/** - * \class CID - * \brief Card IDentification (CID) register. - */ -typedef struct CID { - // byte 0 - /** Manufacturer ID */ - unsigned char mid; - // byte 1-2 - /** OEM/Application ID */ - char oid[2]; - // byte 3-7 - /** Product name */ - char pnm[5]; - // byte 8 - /** Product revision least significant digit */ - unsigned char prv_m : 4; - /** Product revision most significant digit */ - unsigned char prv_n : 4; - // byte 9-12 - /** Product serial number */ - uint32_t psn; - // byte 13 - /** Manufacturing date year low digit */ - unsigned char mdt_year_high : 4; - /** not used */ - unsigned char reserved : 4; - // byte 14 - /** Manufacturing date month */ - unsigned char mdt_month : 4; - /** Manufacturing date year low digit */ - unsigned char mdt_year_low : 4; - // byte 15 - /** not used always 1 */ - unsigned char always1 : 1; - /** CRC7 checksum */ - unsigned char crc : 7; -} __attribute__((packed)) cid_t; - -//============================================================================== -/** - * \class CSDV1 - * \brief CSD register for version 1.00 cards . - */ -typedef struct CSDV1 { - // byte 0 - unsigned char reserved1 : 6; - unsigned char csd_ver : 2; - // byte 1 - unsigned char taac; - // byte 2 - unsigned char nsac; - // byte 3 - unsigned char tran_speed; - // byte 4 - unsigned char ccc_high; - // byte 5 - unsigned char read_bl_len : 4; - unsigned char ccc_low : 4; - // byte 6 - unsigned char c_size_high : 2; - unsigned char reserved2 : 2; - unsigned char dsr_imp : 1; - unsigned char read_blk_misalign : 1; - unsigned char write_blk_misalign : 1; - unsigned char read_bl_partial : 1; - // byte 7 - unsigned char c_size_mid; - // byte 8 - unsigned char vdd_r_curr_max : 3; - unsigned char vdd_r_curr_min : 3; - unsigned char c_size_low : 2; - // byte 9 - unsigned char c_size_mult_high : 2; - unsigned char vdd_w_cur_max : 3; - unsigned char vdd_w_curr_min : 3; - // byte 10 - unsigned char sector_size_high : 6; - unsigned char erase_blk_en : 1; - unsigned char c_size_mult_low : 1; - // byte 11 - unsigned char wp_grp_size : 7; - unsigned char sector_size_low : 1; - // byte 12 - unsigned char write_bl_len_high : 2; - unsigned char r2w_factor : 3; - unsigned char reserved3 : 2; - unsigned char wp_grp_enable : 1; - // byte 13 - unsigned char reserved4 : 5; - unsigned char write_partial : 1; - unsigned char write_bl_len_low : 2; - // byte 14 - unsigned char reserved5: 2; - unsigned char file_format : 2; - unsigned char tmp_write_protect : 1; - unsigned char perm_write_protect : 1; - unsigned char copy : 1; - /** Indicates the file format on the card */ - unsigned char file_format_grp : 1; - // byte 15 - unsigned char always1 : 1; - unsigned char crc : 7; -} __attribute__((packed)) csd1_t; -//============================================================================== -/** - * \class CSDV2 - * \brief CSD register for version 2.00 cards. - */ -typedef struct CSDV2 { - // byte 0 - unsigned char reserved1 : 6; - unsigned char csd_ver : 2; - // byte 1 - /** fixed to 0X0E */ - unsigned char taac; - // byte 2 - /** fixed to 0 */ - unsigned char nsac; - // byte 3 - unsigned char tran_speed; - // byte 4 - unsigned char ccc_high; - // byte 5 - /** This field is fixed to 9h, which indicates READ_BL_LEN=512 Byte */ - unsigned char read_bl_len : 4; - unsigned char ccc_low : 4; - // byte 6 - /** not used */ - unsigned char reserved2 : 4; - unsigned char dsr_imp : 1; - /** fixed to 0 */ - unsigned char read_blk_misalign : 1; - /** fixed to 0 */ - unsigned char write_blk_misalign : 1; - /** fixed to 0 - no partial read */ - unsigned char read_bl_partial : 1; - // byte 7 - /** high part of card size */ - unsigned char c_size_high : 6; - /** not used */ - unsigned char reserved3 : 2; - // byte 8 - /** middle part of card size */ - unsigned char c_size_mid; - // byte 9 - /** low part of card size */ - unsigned char c_size_low; - // byte 10 - /** sector size is fixed at 64 KB */ - unsigned char sector_size_high : 6; - /** fixed to 1 - erase single is supported */ - unsigned char erase_blk_en : 1; - /** not used */ - unsigned char reserved4 : 1; - // byte 11 - unsigned char wp_grp_size : 7; - /** sector size is fixed at 64 KB */ - unsigned char sector_size_low : 1; - // byte 12 - /** write_bl_len fixed for 512 byte blocks */ - unsigned char write_bl_len_high : 2; - /** fixed value of 2 */ - unsigned char r2w_factor : 3; - /** not used */ - unsigned char reserved5 : 2; - /** fixed value of 0 - no write protect groups */ - unsigned char wp_grp_enable : 1; - // byte 13 - unsigned char reserved6 : 5; - /** always zero - no partial block read*/ - unsigned char write_partial : 1; - /** write_bl_len fixed for 512 byte blocks */ - unsigned char write_bl_len_low : 2; - // byte 14 - unsigned char reserved7: 2; - /** Do not use always 0 */ - unsigned char file_format : 2; - unsigned char tmp_write_protect : 1; - unsigned char perm_write_protect : 1; - unsigned char copy : 1; - /** Do not use always 0 */ - unsigned char file_format_grp : 1; - // byte 15 - /** not used always 1 */ - unsigned char always1 : 1; - /** checksum */ - unsigned char crc : 7; -} __attribute__((packed)) csd2_t; -//============================================================================== -/** - * \class csd_t - * \brief Union of old and new style CSD register. - */ -union csd_t { - csd1_t v1; - csd2_t v2; -}; -//----------------------------------------------------------------------------- -inline uint32_t sdCardCapacity(csd_t* csd) { - if (csd->v1.csd_ver == 0) { - uint8_t read_bl_len = csd->v1.read_bl_len; - uint16_t c_size = (csd->v1.c_size_high << 10) - | (csd->v1.c_size_mid << 2) | csd->v1.c_size_low; - uint8_t c_size_mult = (csd->v1.c_size_mult_high << 1) - | csd->v1.c_size_mult_low; - return (uint32_t)(c_size + 1) << (c_size_mult + read_bl_len - 7); - } else if (csd->v2.csd_ver == 1) { - uint32_t c_size = 0X10000L * csd->v2.c_size_high + 0X100L - * (uint32_t)csd->v2.c_size_mid + csd->v2.c_size_low; - return (c_size + 1) << 10; - } else { - return 0; - } -} -#endif // SdInfo_h diff --git a/avr/cores/megacommand/SdFat/SdCard/SdSpiCard.cpp b/avr/cores/megacommand/SdFat/SdCard/SdSpiCard.cpp index 1b54f27ad..06412cf2a 100644 --- a/avr/cores/megacommand/SdFat/SdCard/SdSpiCard.cpp +++ b/avr/cores/megacommand/SdFat/SdCard/SdSpiCard.cpp @@ -1,5 +1,5 @@ /** - * Copyright (c) 20011-2017 Bill Greiman + * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -23,11 +23,21 @@ * DEALINGS IN THE SOFTWARE. */ #include "SdSpiCard.h" -// debug trace macro -#define SD_TRACE(m, b) -// #define SD_TRACE(m, b) Serial.print(m);Serial.println(b); -#define SD_CS_DBG(m) -// #define SD_CS_DBG(m) Serial.println(F(m)); +//============================================================================== +class Timeout { + public: + Timeout() {} + explicit Timeout(uint16_t ms) {set(ms);} + uint16_t millis16() {return millis();} + void set(uint16_t ms) { + m_endTime = ms + millis16(); + } + bool timedOut() { + return (int16_t)(m_endTime - millis16()) < 0; + } + private: + uint16_t m_endTime; +}; //============================================================================== #if USE_SD_CRC // CRC functions @@ -50,7 +60,7 @@ static uint8_t CRC7(const uint8_t* data, uint8_t n) { #if USE_SD_CRC == 1 // Shift based CRC-CCITT // uses the x^16,x^12,x^5,x^1 polynomial. -static uint16_t CRC_CCITT(const uint8_t *data, size_t n) { +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 = (uint8_t)(crc >> 8) | (crc << 8); @@ -117,18 +127,29 @@ static uint16_t CRC_CCITT(const uint8_t* data, size_t n) { #endif // CRC_CCITT #endif // USE_SD_CRC //============================================================================== -// SdSpiCard member functions +// SharedSpiCard member functions //------------------------------------------------------------------------------ -bool SdSpiCard::begin(SdSpiDriver* spi, uint8_t csPin, SPISettings settings) { +bool SharedSpiCard::begin(SdSpiConfig spiConfig) { + Timeout timeout; m_spiActive = false; + m_beginCalled = false; m_errorCode = SD_CARD_ERROR_NONE; m_type = 0; - m_spiDriver = spi; - uint16_t t0 = curTimeMS(); + m_csPin = spiConfig.csPin; +#if SPI_DRIVER_SELECT >= 2 + m_spiDriverPtr = spiConfig.spiPort; + if (!m_spiDriverPtr) { + error(SD_CARD_ERROR_INVALID_CARD_CONFIG); + goto fail; + } +#endif // SPI_DRIVER_SELECT + sdCsInit(m_csPin); + spiUnselect(); + spiSetSckSpeed(1000UL*SD_MAX_INIT_RATE_KHZ); + spiBegin(spiConfig); + m_beginCalled = true; uint32_t arg; - - m_spiDriver->begin(csPin); - m_spiDriver->setSpiSettings(SD_SCK_HZ(250000)); + m_state = IDLE_STATE; spiStart(); // must supply min of 74 clock cycles with CS high. @@ -138,11 +159,18 @@ bool SdSpiCard::begin(SdSpiDriver* spi, uint8_t csPin, SPISettings settings) { } spiSelect(); // command to go idle in SPI mode - while (cardCommand(CMD0, 0) != R1_IDLE_STATE) { - if (isTimedOut(t0, SD_INIT_TIMEOUT)) { + for (uint8_t i = 1;; i++) { + if (cardCommand(CMD0, 0) == R1_IDLE_STATE) { + break; + } + if (i == SD_CMD0_RETRY) { error(SD_CARD_ERROR_CMD0); goto fail; } + // Force any active transfer to end for an already initialized card. + for (uint8_t j = 0; j < 0XFF; j++) { + spiSend(0XFF); + } } #if USE_SD_CRC if (cardCommand(CMD59, 1) != R1_IDLE_STATE) { @@ -151,33 +179,29 @@ bool SdSpiCard::begin(SdSpiDriver* spi, uint8_t csPin, SPISettings settings) { } #endif // USE_SD_CRC // check SD version - while (1) { - if (cardCommand(CMD8, 0x1AA) == (R1_ILLEGAL_COMMAND | R1_IDLE_STATE)) { - type(SD_CARD_TYPE_SD1); - break; - } + if (!(cardCommand(CMD8, 0x1AA) & R1_ILLEGAL_COMMAND)) { + type(SD_CARD_TYPE_SD2); for (uint8_t i = 0; i < 4; i++) { m_status = spiReceive(); } - if (m_status == 0XAA) { - type(SD_CARD_TYPE_SD2); - break; - } - if (isTimedOut(t0, SD_INIT_TIMEOUT)) { + if (m_status != 0XAA) { error(SD_CARD_ERROR_CMD8); goto fail; } + } else { + type(SD_CARD_TYPE_SD1); } // initialize card and send host supports SDHC if SD2 arg = type() == SD_CARD_TYPE_SD2 ? 0X40000000 : 0; - + timeout.set(SD_INIT_TIMEOUT); while (cardAcmd(ACMD41, arg) != R1_READY_STATE) { // check for timeout - if (isTimedOut(t0, SD_INIT_TIMEOUT)) { + if (timeout.timedOut()) { error(SD_CARD_ERROR_ACMD41); goto fail; } } + // if SD2 read OCR register to check for SDHC card if (type() == SD_CARD_TYPE_SD2) { if (cardCommand(CMD58, 0)) { @@ -193,23 +217,42 @@ bool SdSpiCard::begin(SdSpiDriver* spi, uint8_t csPin, SPISettings settings) { } } spiStop(); - m_spiDriver->setSpiSettings(settings); + spiSetSckSpeed(spiConfig.maxSck); return true; -fail: + fail: + spiStop(); + return false; +} +//------------------------------------------------------------------------------ +bool SharedSpiCard::cardCMD6(uint32_t arg, uint8_t* status) { + if (cardCommand(CMD6, arg)) { + error(SD_CARD_ERROR_CMD6); + goto fail; + } + if (!readData(status, 64)) { + goto fail; + } + spiStop(); + return true; + + fail: spiStop(); return false; } //------------------------------------------------------------------------------ // send command and return error code. Return zero for OK -uint8_t SdSpiCard::cardCommand(uint8_t cmd, uint32_t arg) { +uint8_t SharedSpiCard::cardCommand(uint8_t cmd, uint32_t arg) { + if (!syncDevice()) { + return 0XFF; + } // select card if (!m_spiActive) { spiStart(); } - // wait if busy - waitNotBusy(SD_WRITE_TIMEOUT); - + if (cmd != CMD0 && cmd != CMD12 && !waitReady(SD_CMD_TIMEOUT)) { + return 0XFF; + } #if USE_SD_CRC // form message uint8_t buf[6]; @@ -229,7 +272,7 @@ uint8_t SdSpiCard::cardCommand(uint8_t cmd, uint32_t arg) { spiSend(cmd | 0x40); // send argument - uint8_t *pa = reinterpret_cast(&arg); + uint8_t* pa = reinterpret_cast(&arg); for (int8_t i = 3; i >= 0; i--) { spiSend(pa[i]); } @@ -238,144 +281,101 @@ uint8_t SdSpiCard::cardCommand(uint8_t cmd, uint32_t arg) { spiSend(cmd == CMD0 ? 0X95 : 0X87); #endif // USE_SD_CRC - // skip stuff byte for stop read - if (cmd == CMD12) { - spiReceive(); - } + // discard first fill byte to avoid MISO pull-up problem. + spiReceive(); - // wait for response - for (uint8_t i = 0; ((m_status = spiReceive()) & 0X80) && i != 0XFF; i++) { - } + // there are 1-8 fill bytes before response. fill bytes should be 0XFF. + uint16_t n = 0; + do { + m_status = spiReceive(); + } while (m_status & 0X80 && ++n < 10); return m_status; } //------------------------------------------------------------------------------ -uint32_t SdSpiCard::cardSize() { - csd_t csd; - return readCSD(&csd) ? sdCardCapacity(&csd) : 0; +void SharedSpiCard::end() { + if (m_beginCalled) { + spiStop(); + spiEnd(); + m_beginCalled = false; + } } //------------------------------------------------------------------------------ -bool SdSpiCard::erase(uint32_t firstBlock, uint32_t lastBlock) { +bool SharedSpiCard::erase(uint32_t firstSector, uint32_t lastSector) { csd_t csd; if (!readCSD(&csd)) { goto fail; } - // check for single block erase - if (!csd.v1.erase_blk_en) { + // check for single sector erase + if (!csd.eraseSingleBlock()) { // erase size mask - uint8_t m = (csd.v1.sector_size_high << 1) | csd.v1.sector_size_low; - if ((firstBlock & m) != 0 || ((lastBlock + 1) & m) != 0) { + uint8_t m = csd.eraseSize() - 1; + if ((firstSector & m) != 0 || ((lastSector + 1) & m) != 0) { // error card can't erase specified area - error(SD_CARD_ERROR_ERASE_SINGLE_BLOCK); + error(SD_CARD_ERROR_ERASE_SINGLE_SECTOR); goto fail; } } if (m_type != SD_CARD_TYPE_SDHC) { - firstBlock <<= 9; - lastBlock <<= 9; + firstSector <<= 9; + lastSector <<= 9; } - if (cardCommand(CMD32, firstBlock) - || cardCommand(CMD33, lastBlock) + if (cardCommand(CMD32, firstSector) + || cardCommand(CMD33, lastSector) || cardCommand(CMD38, 0)) { error(SD_CARD_ERROR_ERASE); goto fail; } - if (!waitNotBusy(SD_ERASE_TIMEOUT)) { + if (!waitReady(SD_ERASE_TIMEOUT)) { error(SD_CARD_ERROR_ERASE_TIMEOUT); goto fail; } spiStop(); return true; -fail: + fail: spiStop(); return false; } //------------------------------------------------------------------------------ -bool SdSpiCard::eraseSingleBlockEnable() { +bool SharedSpiCard::eraseSingleSectorEnable() { csd_t csd; - return readCSD(&csd) ? csd.v1.erase_blk_en : false; + return readCSD(&csd) ? csd.eraseSingleBlock() : false; } //------------------------------------------------------------------------------ -bool SdSpiCard::isBusy() { - bool rtn = true; +bool SharedSpiCard::isBusy() { + if (m_state == READ_STATE) { + return false; + } bool spiActive = m_spiActive; if (!spiActive) { spiStart(); } - for (uint8_t i = 0; i < 8; i++) { - if (0XFF == spiReceive()) { - rtn = false; - break; - } - } + bool rtn = 0XFF != spiReceive(); if (!spiActive) { spiStop(); } return rtn; } //------------------------------------------------------------------------------ -bool SdSpiCard::isTimedOut(uint16_t startMS, uint16_t timeoutMS) { -#if WDT_YIELD_TIME_MICROS - static uint32_t last; - if ((micros() - last) > WDT_YIELD_TIME_MICROS) { - SysCall::yield(); - last = micros(); - } -#endif // WDT_YIELD_TIME_MICROS - return (curTimeMS() - startMS) > timeoutMS; -} -//------------------------------------------------------------------------------ -bool SdSpiCard::readBlock(uint32_t blockNumber, uint8_t* dst) { - SD_TRACE("RB", blockNumber); - // use address if not SDHC card - if (type() != SD_CARD_TYPE_SDHC) { - blockNumber <<= 9; - } - if (cardCommand(CMD17, blockNumber)) { - error(SD_CARD_ERROR_CMD17); - goto fail; - } - if (!readData(dst, 512)) { - goto fail; - } - spiStop(); - return true; - -fail: - spiStop(); - return false; -} -//------------------------------------------------------------------------------ -bool SdSpiCard::readBlocks(uint32_t block, uint8_t* dst, size_t count) { - if (!readStart(block)) { - return false; - } - for (uint16_t b = 0; b < count; b++, dst += 512) { - if (!readData(dst, 512)) { - return false; - } - } - return readStop(); -} -//------------------------------------------------------------------------------ -bool SdSpiCard::readData(uint8_t *dst) { +bool SharedSpiCard::readData(uint8_t* dst) { return readData(dst, 512); } //------------------------------------------------------------------------------ -bool SdSpiCard::readData(uint8_t* dst, size_t count) { +bool SharedSpiCard::readData(uint8_t* dst, size_t count) { #if USE_SD_CRC uint16_t crc; #endif // USE_SD_CRC - // wait for start block token - uint16_t t0 = curTimeMS(); + + // wait for start sector token + Timeout timeout(SD_READ_TIMEOUT); while ((m_status = spiReceive()) == 0XFF) { - if (isTimedOut(t0, SD_READ_TIMEOUT)) { + if (timeout.timedOut()) { error(SD_CARD_ERROR_READ_TIMEOUT); goto fail; } } - if (m_status != DATA_START_BLOCK) { - error(SD_CARD_ERROR_READ); + if (m_status != DATA_START_SECTOR) { + error(SD_CARD_ERROR_READ_TOKEN); goto fail; } // transfer data @@ -391,38 +391,41 @@ bool SdSpiCard::readData(uint8_t* dst, size_t count) { error(SD_CARD_ERROR_READ_CRC); goto fail; } -#else +#else // USE_SD_CRC // discard crc spiReceive(); spiReceive(); #endif // USE_SD_CRC return true; -fail: + fail: spiStop(); return false; } //------------------------------------------------------------------------------ -bool SdSpiCard::readOCR(uint32_t* ocr) { - uint8_t *p = reinterpret_cast(ocr); +bool SharedSpiCard::readOCR(uint32_t* ocr) { + uint8_t* p = reinterpret_cast(ocr); if (cardCommand(CMD58, 0)) { error(SD_CARD_ERROR_CMD58); goto fail; } for (uint8_t i = 0; i < 4; i++) { +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ p[3 - i] = spiReceive(); +#else // __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + p[i] = spiReceive(); +#endif // __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ } - spiStop(); return true; -fail: + fail: spiStop(); return false; } //------------------------------------------------------------------------------ /** read CID or CSR register */ -bool SdSpiCard::readRegister(uint8_t cmd, void* buf) { +bool SharedSpiCard::readRegister(uint8_t cmd, void* buf) { uint8_t* dst = reinterpret_cast(buf); if (cardCommand(cmd, 0)) { error(SD_CARD_ERROR_READ_REG); @@ -434,116 +437,212 @@ bool SdSpiCard::readRegister(uint8_t cmd, void* buf) { spiStop(); return true; -fail: + fail: + spiStop(); + return false; +} +//------------------------------------------------------------------------------ +bool SharedSpiCard::readSCR(scr_t* scr) { + uint8_t* dst = reinterpret_cast(scr); + if (cardAcmd(ACMD51, 0)) { + error(SD_CARD_ERROR_ACMD51); + goto fail; + } + if (!readData(dst, sizeof(scr_t))) { + goto fail; + } + spiStop(); + return true; + + fail: + spiStop(); + return false; +} +//------------------------------------------------------------------------------ +bool SharedSpiCard::readSector(uint32_t sector, uint8_t* dst) { + // use address if not SDHC card + if (type() != SD_CARD_TYPE_SDHC) { + sector <<= 9; + } + if (cardCommand(CMD17, sector)) { + error(SD_CARD_ERROR_CMD17); + goto fail; + } + if (!readData(dst, 512)) { + goto fail; + } spiStop(); + return true; + + fail: + spiStop(); + return false; +} +//------------------------------------------------------------------------------ +bool SharedSpiCard::readSectors(uint32_t sector, uint8_t* dst, size_t ns) { + if (!readStart(sector)) { + goto fail; + } + for (size_t i = 0; i < ns; i++, dst += 512) { + if (!readData(dst, 512)) { + goto fail; + } + } + return readStop(); + fail: return false; } //------------------------------------------------------------------------------ -bool SdSpiCard::readStart(uint32_t blockNumber) { - SD_TRACE("RS", blockNumber); +bool SharedSpiCard::readStart(uint32_t sector) { if (type() != SD_CARD_TYPE_SDHC) { - blockNumber <<= 9; + sector <<= 9; } - if (cardCommand(CMD18, blockNumber)) { + if (cardCommand(CMD18, sector)) { error(SD_CARD_ERROR_CMD18); goto fail; } -// spiStop(); + m_state = READ_STATE; return true; -fail: + fail: spiStop(); return false; } -//----------------------------------------------------------------------------- -bool SdSpiCard::readStatus(uint8_t* status) { +//------------------------------------------------------------------------------ +bool SharedSpiCard::readStatus(SdStatus* status) { + uint8_t* dst = reinterpret_cast(status); // retrun is R2 so read extra status byte. if (cardAcmd(ACMD13, 0) || spiReceive()) { error(SD_CARD_ERROR_ACMD13); goto fail; } - if (!readData(status, 64)) { + if (!readData(dst, 64)) { goto fail; } spiStop(); return true; -fail: + fail: + spiStop(); + return false; +} +//------------------------------------------------------------------------------ +bool SharedSpiCard::readStop() { + m_state = IDLE_STATE; + if (cardCommand(CMD12, 0)) { + error(SD_CARD_ERROR_CMD12); + goto fail; + } + spiStop(); + return true; + + fail: spiStop(); return false; } -//----------------------------------------------------------------------------- -void SdSpiCard::spiStart() { +//------------------------------------------------------------------------------ +uint32_t SharedSpiCard::sectorCount() { + csd_t csd; + return readCSD(&csd) ? csd.capacity() : 0; +} +//------------------------------------------------------------------------------ +void SharedSpiCard::spiStart() { if (!m_spiActive) { spiActivate(); - spiSelect(); m_spiActive = true; + spiSelect(); + // Dummy byte to drive MISO busy status. + spiSend(0XFF); } } -//----------------------------------------------------------------------------- -void SdSpiCard::spiStop() { +//------------------------------------------------------------------------------ +void SharedSpiCard::spiStop() { if (m_spiActive) { spiUnselect(); + // Insure MISO goes to low Z. spiSend(0XFF); spiDeactivate(); m_spiActive = false; } } //------------------------------------------------------------------------------ -bool SdSpiCard::readStop() { - if (cardCommand(CMD12, 0)) { - error(SD_CARD_ERROR_CMD12); +bool SharedSpiCard::syncDevice() { + if (m_state == WRITE_STATE) { + return writeStop(); + } + if (m_state == READ_STATE) { + return readStop(); + } + return true; +} +//------------------------------------------------------------------------------ +bool SharedSpiCard::waitReady(uint16_t ms) { + Timeout timeout(ms); + while (spiReceive() != 0XFF) { + if (timeout.timedOut()) { + return false; + } + } + return true; +} +//------------------------------------------------------------------------------ +bool SharedSpiCard::writeData(const uint8_t* src) { + // wait for previous write to finish + if (!waitReady(SD_WRITE_TIMEOUT)) { + error(SD_CARD_ERROR_WRITE_TIMEOUT); + goto fail; + } + if (!writeData(WRITE_MULTIPLE_TOKEN, src)) { goto fail; } - spiStop(); return true; -fail: + fail: spiStop(); return false; } //------------------------------------------------------------------------------ -// wait for card to go not busy -bool SdSpiCard::waitNotBusy(uint16_t timeoutMS) { - uint16_t t0 = curTimeMS(); -#if WDT_YIELD_TIME_MICROS - // Call isTimedOut first to insure yield is called. - while (!isTimedOut(t0, timeoutMS)) { - if (spiReceive() == 0XFF) { - return true; - } - } - return false; -#else // WDT_YIELD_TIME_MICROS - // Check not busy first since yield is not called in isTimedOut. - while (spiReceive() != 0XFF) { - if (isTimedOut(t0, timeoutMS)) { - return false; - } +// send one sector of data for write sector or write multiple sectors +bool SharedSpiCard::writeData(uint8_t token, const uint8_t* src) { +#if USE_SD_CRC + uint16_t crc = CRC_CCITT(src, 512); +#else // USE_SD_CRC + uint16_t crc = 0XFFFF; +#endif // USE_SD_CRC + spiSend(token); + spiSend(src, 512); + spiSend(crc >> 8); + spiSend(crc & 0XFF); + + m_status = spiReceive(); + if ((m_status & DATA_RES_MASK) != DATA_RES_ACCEPTED) { + error(SD_CARD_ERROR_WRITE_DATA); + goto fail; } return true; -#endif // WDT_YIELD_TIME_MICROS + + fail: + spiStop(); + return false; } //------------------------------------------------------------------------------ -bool SdSpiCard::writeBlock(uint32_t blockNumber, const uint8_t* src) { - SD_TRACE("WB", blockNumber); +bool SharedSpiCard::writeSector(uint32_t sector, const uint8_t* src) { // use address if not SDHC card if (type() != SD_CARD_TYPE_SDHC) { - blockNumber <<= 9; + sector <<= 9; } - if (cardCommand(CMD24, blockNumber)) { + if (cardCommand(CMD24, sector)) { error(SD_CARD_ERROR_CMD24); goto fail; } - if (!writeData(DATA_START_BLOCK, src)) { + if (!writeData(DATA_START_SECTOR, src)) { goto fail; } - #if CHECK_FLASH_PROGRAMMING // wait for flash programming to complete - if (!waitNotBusy(SD_WRITE_TIMEOUT)) { - error(SD_CARD_ERROR_WRITE_TIMEOUT); + if (!waitReady(SD_WRITE_TIMEOUT)) { + error(SD_CARD_ERROR_WRITE_PROGRAMMING); goto fail; } // response is r2 so get and check two bytes for nonzero @@ -551,21 +650,22 @@ bool SdSpiCard::writeBlock(uint32_t blockNumber, const uint8_t* src) { error(SD_CARD_ERROR_CMD13); goto fail; } -#endif // CHECK_PROGRAMMING +#endif // CHECK_FLASH_PROGRAMMING spiStop(); return true; -fail: + fail: spiStop(); return false; } //------------------------------------------------------------------------------ -bool SdSpiCard::writeBlocks(uint32_t block, const uint8_t* src, size_t count) { - if (!writeStart(block)) { +bool SharedSpiCard::writeSectors(uint32_t sector, + const uint8_t* src, size_t ns) { + if (!writeStart(sector)) { goto fail; } - for (size_t b = 0; b < count; b++, src += 512) { + for (size_t i = 0; i < ns; i++, src += 512) { if (!writeData(src)) { goto fail; } @@ -577,94 +677,101 @@ bool SdSpiCard::writeBlocks(uint32_t block, const uint8_t* src, size_t count) { return false; } //------------------------------------------------------------------------------ -bool SdSpiCard::writeData(const uint8_t* src) { - // wait for previous write to finish - if (!waitNotBusy(SD_WRITE_TIMEOUT)) { - error(SD_CARD_ERROR_WRITE_TIMEOUT); - goto fail; +bool SharedSpiCard::writeStart(uint32_t sector) { + // use address if not SDHC card + if (type() != SD_CARD_TYPE_SDHC) { + sector <<= 9; } - if (!writeData(WRITE_MULTIPLE_TOKEN, src)) { + if (cardCommand(CMD25, sector)) { + error(SD_CARD_ERROR_CMD25); goto fail; } + m_state = WRITE_STATE; return true; -fail: + fail: spiStop(); return false; } //------------------------------------------------------------------------------ -// send one block of data for write block or write multiple blocks -bool SdSpiCard::writeData(uint8_t token, const uint8_t* src) { -#if USE_SD_CRC - uint16_t crc = CRC_CCITT(src, 512); -#else // USE_SD_CRC - uint16_t crc = 0XFFFF; -#endif // USE_SD_CRC - spiSend(token); - spiSend(src, 512); - spiSend(crc >> 8); - spiSend(crc & 0XFF); - - m_status = spiReceive(); - if ((m_status & DATA_RES_MASK) != DATA_RES_ACCEPTED) { - error(SD_CARD_ERROR_WRITE); +bool SharedSpiCard::writeStop() { + if (!waitReady(SD_WRITE_TIMEOUT)) { goto fail; } + spiSend(STOP_TRAN_TOKEN); + spiStop(); + m_state = IDLE_STATE; return true; -fail: + fail: + error(SD_CARD_ERROR_STOP_TRAN); spiStop(); return false; } -//------------------------------------------------------------------------------ -bool SdSpiCard::writeStart(uint32_t blockNumber) { - // use address if not SDHC card - if (type() != SD_CARD_TYPE_SDHC) { - blockNumber <<= 9; - } - if (cardCommand(CMD25, blockNumber)) { - error(SD_CARD_ERROR_CMD25); - goto fail; +//============================================================================== +bool DedicatedSpiCard::begin(SdSpiConfig spiConfig) { + if (!SharedSpiCard::begin(spiConfig)) { + return false; } + m_dedicatedSpi = spiOptionDedicated(spiConfig.options); return true; - -fail: - spiStop(); - return false; } //------------------------------------------------------------------------------ -bool SdSpiCard::writeStart(uint32_t blockNumber, uint32_t eraseCount) { - SD_TRACE("WS", blockNumber); - // send pre-erase count - if (cardAcmd(ACMD23, eraseCount)) { - error(SD_CARD_ERROR_ACMD23); - goto fail; - } - // use address if not SDHC card - if (type() != SD_CARD_TYPE_SDHC) { - blockNumber <<= 9; +bool DedicatedSpiCard::readSector(uint32_t sector, uint8_t* dst) { + return readSectors(sector, dst, 1); +} +//------------------------------------------------------------------------------ +bool DedicatedSpiCard::readSectors( + uint32_t sector, uint8_t* dst, size_t ns) { + if (sdState() != READ_STATE || sector != m_curSector) { + if (!readStart(sector)) { + goto fail; + } + m_curSector = sector; } - if (cardCommand(CMD25, blockNumber)) { - error(SD_CARD_ERROR_CMD25); - goto fail; + for (size_t i = 0; i < ns; i++, dst += 512) { + if (!readData(dst)) { + goto fail; + } } - return true; + m_curSector += ns; + return m_dedicatedSpi ? true : readStop(); -fail: - spiStop(); + fail: return false; } //------------------------------------------------------------------------------ -bool SdSpiCard::writeStop() { - if (!waitNotBusy(SD_WRITE_TIMEOUT)) { - goto fail; +bool DedicatedSpiCard::setDedicatedSpi(bool value) { + if (!syncDevice()) { + return false; } - spiSend(STOP_TRAN_TOKEN); - spiStop(); + m_dedicatedSpi = value; return true; +} +//------------------------------------------------------------------------------ +bool DedicatedSpiCard::writeSector(uint32_t sector, const uint8_t* src) { + if (m_dedicatedSpi) { + return writeSectors(sector, src, 1); + } + return SharedSpiCard::writeSector(sector, src); +} +//------------------------------------------------------------------------------ +bool DedicatedSpiCard::writeSectors( + uint32_t sector, const uint8_t* src, size_t ns) { + if (sdState() != WRITE_STATE || m_curSector != sector) { + if (!writeStart(sector)) { + goto fail; + } + m_curSector = sector; + } + for (size_t i = 0; i < ns; i++, src += 512) { + if (!writeData(src)) { + goto fail; + } + } + m_curSector += ns; + return m_dedicatedSpi ? true : writeStop(); fail: - error(SD_CARD_ERROR_STOP_TRAN); - spiStop(); return false; } diff --git a/avr/cores/megacommand/SdFat/SdCard/SdSpiCard.h b/avr/cores/megacommand/SdFat/SdCard/SdSpiCard.h index 4c2fa08d7..784067ee6 100644 --- a/avr/cores/megacommand/SdFat/SdCard/SdSpiCard.h +++ b/avr/cores/megacommand/SdFat/SdCard/SdSpiCard.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 20011-2017 Bill Greiman + * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -22,106 +22,110 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ -#ifndef SdSpiCard_h -#define SdSpiCard_h /** * \file * \brief SdSpiCard class for V2 SD/SDHC cards */ +#ifndef SdSpiCard_h +#define SdSpiCard_h #include -#include "SysCall.h" -#include "SdInfo.h" -#include "../FatLib/BaseBlockDriver.h" +#include "../common/SysCall.h" +#include "SdCardInfo.h" +#include "SdCardInterface.h" #include "../SpiDriver/SdSpiDriver.h" +/** Verify correct SPI active if non-zero. */ +#define CHECK_SPI_ACTIVE 0 +#if CHECK_SPI_ACTIVE +/** Check SPI active. */ +#define SPI_ASSERT_ACTIVE {if (!m_spiActive) {\ + Serial.print(F("SPI_ASSERTACTIVE"));\ + Serial.println(__LINE__);}} +#else // CHECK_SPI_ACTIVE +/** Do not check SPI active. */ +#define SPI_ASSERT_ACTIVE +#endif // CHECK_SPI_ACTIVE //============================================================================== /** - * \class SdSpiCard - * \brief Raw access to SD and SDHC flash memory cards via SPI protocol. + * \class SharedSpiCard + * \brief Raw access to SD and SDHC flash memory cards via shared SPI port. */ -#if ENABLE_EXTENDED_TRANSFER_CLASS || ENABLE_SDIO_CLASS -class SdSpiCard : public BaseBlockDriver { -#else // ENABLE_EXTENDED_TRANSFER_CLASS || ENABLE_SDIO_CLASS -class SdSpiCard { -#endif // ENABLE_EXTENDED_TRANSFER_CLASS || ENABLE_SDIO_CLASS +#if HAS_SDIO_CLASS +class SharedSpiCard : public SdCardInterface { +#elif USE_BLOCK_DEVICE_INTERFACE +class SharedSpiCard : public FsBlockDeviceInterface { +#else // HAS_SDIO_CLASS +class SharedSpiCard { +#endif // HAS_SDIO_CLASS public: - /** Construct an instance of SdSpiCard. */ - SdSpiCard() : m_errorCode(SD_CARD_ERROR_INIT_NOT_CALLED), m_type(0) {} + /** SD is in idle state */ + static const uint8_t IDLE_STATE = 0; + /** SD is in multi-sector read state. */ + static const uint8_t READ_STATE = 1; + /** SD is in multi-sector write state. */ + static const uint8_t WRITE_STATE = 2; + /** Construct an instance of SharedSpiCard. */ + SharedSpiCard() {} /** Initialize the SD card. - * \param[in] spi SPI driver for card. - * \param[in] csPin card chip select pin. - * \param[in] spiSettings SPI speed, mode, and bit order. - * \return true for success else false. + * \param[in] spiConfig SPI card configuration. + * \return true for success or false for failure. */ - bool begin(SdSpiDriver* spi, uint8_t csPin, SPISettings spiSettings); - /** - * Determine the size of an SD flash memory card. + bool begin(SdSpiConfig spiConfig); + /** CMD6 Switch mode: Check Function Set Function. + * \param[in] arg CMD6 argument. + * \param[out] status return status data. * - * \return The number of 512 byte data blocks in the card - * or zero if an error occurs. + * \return true for success or false for failure. */ - uint32_t cardSize(); - /** Erase a range of blocks. + bool cardCMD6(uint32_t arg, uint8_t* status); + /** End use of card */ + void end(); + /** Erase a range of sectors. * - * \param[in] firstBlock The address of the first block in the range. - * \param[in] lastBlock The address of the last block in the range. + * \param[in] firstSector The address of the first sector in the range. + * \param[in] lastSector The address of the last sector in the range. * * \note This function requests the SD card to do a flash erase for a - * range of blocks. The data on the card after an erase operation is + * range of sectors. The data on the card after an erase operation is * either 0 or 1, depends on the card vendor. The card must support - * single block erase. + * single sector erase. * - * \return The value true is returned for success and - * the value false is returned for failure. + * \return true for success or false for failure. */ - bool erase(uint32_t firstBlock, uint32_t lastBlock); - /** Determine if card supports single block erase. + bool erase(uint32_t firstSector, uint32_t lastSector); + /** Determine if card supports single sector erase. * - * \return true is returned if single block erase is supported. - * false is returned if single block erase is not supported. + * \return true is returned if single sector erase is supported. + * false is returned if single sector erase is not supported. */ - bool eraseSingleBlockEnable(); + bool eraseSingleSectorEnable(); /** * Set SD error code. * \param[in] code value for error code. */ void error(uint8_t code) { +// (void)code; m_errorCode = code; } /** - * \return code for the last error. See SdInfo.h for a list of error codes. + * \return code for the last error. See SdCardInfo.h for a list of error codes. */ - int errorCode() const { + uint8_t errorCode() const { return m_errorCode; } /** \return error data for last error. */ - int errorData() const { + uint32_t errorData() const { return m_status; } + /** \return false for shared class. */ + bool hasDedicatedSpi() {return false;} /** * Check for busy. MISO low indicates the card is busy. * * \return true if busy else false. */ bool isBusy(); - /** - * Read a 512 byte block from an SD card. - * - * \param[in] lba Logical block to be read. - * \param[out] dst Pointer to the location that will receive the data. - * \return The value true is returned for success and - * the value false is returned for failure. - */ - bool readBlock(uint32_t lba, uint8_t* dst); - /** - * Read multiple 512 byte blocks from an SD card. - * - * \param[in] lba Logical block to be read. - * \param[in] nb Number of blocks to be read. - * \param[out] dst Pointer to the location that will receive the data. - * \return The value true is returned for success and - * the value false is returned for failure. - */ - bool readBlocks(uint32_t lba, uint8_t* dst, size_t nb); + /** \return false, can't be in dedicated state. */ + bool isDedicatedSpi() {return false;} /** * Read a card's CID register. The CID contains card identification * information such as Manufacturer ID, Product name, Product serial @@ -145,110 +149,135 @@ class SdSpiCard { bool readCSD(csd_t* csd) { return readRegister(CMD9, csd); } - /** Read one data block in a multiple block read sequence + /** Read one data sector in a multiple sector read sequence * * \param[out] dst Pointer to the location for the data to be read. * - * \return The value true is returned for success and - * the value false is returned for failure. + * \return true for success or false for failure. */ - bool readData(uint8_t *dst); + bool readData(uint8_t* dst); /** Read OCR register. * * \param[out] ocr Value of OCR register. - * \return true for success else false. + * \return true for success or false for failure. */ bool readOCR(uint32_t* ocr); - /** Start a read multiple blocks sequence. + /** Read SCR register. + * + * \param[out] scr Value of SCR register. + * \return true for success or false for failure. + */ + bool readSCR(scr_t* scr); + /** + * Read a 512 byte sector from an SD card. + * + * \param[in] sector Logical sector to be read. + * \param[out] dst Pointer to the location that will receive the data. + * \return true for success or false for failure. + */ + bool readSector(uint32_t sector, uint8_t* dst); + /** + * Read multiple 512 byte sectors from an SD card. * - * \param[in] blockNumber Address of first block in sequence. + * \param[in] sector Logical sector to be read. + * \param[in] ns Number of sectors to be read. + * \param[out] dst Pointer to the location that will receive the data. + * \return true for success or false for failure. + */ + bool readSectors(uint32_t sector, uint8_t* dst, size_t ns); + + /** Start a read multiple sector sequence. + * + * \param[in] sector Address of first sector 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. + * multiple sector reads. SPI chipSelect must be low for the entire sequence. * - * \return The value true is returned for success and - * the value false is returned for failure. + * \return true for success or false for failure. */ - bool readStart(uint32_t blockNumber); + bool readStart(uint32_t sector); /** Return the 64 byte card status * \param[out] status location for 64 status bytes. - * \return The value true is returned for success and - * the value false is returned for failure. + * \return true for success or false for failure. */ - bool readStatus(uint8_t* status); - /** End a read multiple blocks sequence. + bool readStatus(SdStatus* status); + /** End a read multiple sectors sequence. * - * \return The value true is returned for success and - * the value false is returned for failure. + * \return true for success or false for failure. */ bool readStop(); + /** \return SD multi-sector read/write state */ + uint8_t sdState() {return m_state;} + /** + * Determine the size of an SD flash memory card. + * + * \return The number of 512 byte data sectors in the card + * or zero if an error occurs. + */ + uint32_t sectorCount(); +#ifndef DOXYGEN_SHOULD_SKIP_THIS + // Use sectorCount(). cardSize() will be removed in the future. + uint32_t __attribute__((error("use sectorCount()"))) cardSize(); +#endif // DOXYGEN_SHOULD_SKIP_THIS + /** Set SPI sharing state + * \param[in] value desired state. + * \return false for shared card + */ + bool setDedicatedSpi(bool value) { + (void)value; + return false; + } + /** end a mult-sector transfer. + * + * \return true for success or false for failure. + */ + bool stopTransfer(); /** \return success if sync successful. Not for user apps. */ - bool syncBlocks() {return true;} - /** Return the card type: SD V1, SD V2 or SDHC - * \return 0 - SD V1, 1 - SD V2, or 3 - SDHC. + bool syncDevice(); + /** Return the card type: SD V1, SD V2 or SDHC/SDXC + * \return 0 - SD V1, 1 - SD V2, or 3 - SDHC/SDXC. */ - int type() const { + uint8_t type() const { return m_type; } /** - * Writes a 512 byte block to an SD card. + * Write a 512 byte sector to an SD card. * - * \param[in] lba Logical block to be written. + * \param[in] sector Logical sector to be written. * \param[in] src Pointer to the location of the data to be written. - * \return The value true is returned for success and - * the value false is returned for failure. + * \return true for success or false for failure. */ - bool writeBlock(uint32_t lba, const uint8_t* src); + bool writeSector(uint32_t sector, const uint8_t* src); /** - * Write multiple 512 byte blocks to an SD card. + * Write multiple 512 byte sectors to an SD card. * - * \param[in] lba Logical block to be written. - * \param[in] nb Number of blocks to be written. + * \param[in] sector Logical sector to be written. + * \param[in] ns Number of sectors to be written. * \param[in] src Pointer to the location of the data to be written. - * \return The value true is returned for success and - * the value false is returned for failure. + * \return true for success or false for failure. */ - bool writeBlocks(uint32_t lba, const uint8_t* src, size_t nb); - /** Write one data block in a multiple block write sequence. + bool writeSectors(uint32_t sector, const uint8_t* src, size_t ns); + /** Write one data sector in a multiple sector write sequence. * \param[in] src Pointer to the location of the data to be written. - * \return The value true is returned for success and - * the value false is returned for failure. + * \return true for success or false for failure. */ bool writeData(const uint8_t* src); - /** Start a write multiple blocks sequence. + /** Start a write multiple sectors sequence. * - * \param[in] blockNumber Address of first block in sequence. + * \param[in] sector Address of first sector in sequence. * * \note This function is used with writeData() and writeStop() - * for optimized multiple block writes. + * for optimized multiple sector writes. * - * \return The value true is returned for success and - * the value false is returned for failure. + * \return true for success or false for failure. */ - bool writeStart(uint32_t blockNumber); + bool writeStart(uint32_t sector); - /** Start a write multiple blocks sequence with pre-erase. - * - * \param[in] blockNumber Address of first block in sequence. - * \param[in] eraseCount The number of blocks to be pre-erased. + /** End a write multiple sectors sequence. * - * \note This function is used with writeData() and writeStop() - * for optimized multiple block writes. - * - * \return The value true is returned for success and - * the value false is returned for failure. - */ - bool writeStart(uint32_t blockNumber, uint32_t eraseCount); - /** End a write multiple blocks sequence. - * - * \return The value true is returned for success and - * the value false is returned for failure. + * \return true for success or false for failure. */ bool writeStop(); - /** Set CS low and activate the card. */ - void spiStart(); - /** Set CS high and deactivate the card. */ - void spiStop(); private: // private functions @@ -257,117 +286,166 @@ class SdSpiCard { return cardCommand(cmd, arg); } uint8_t cardCommand(uint8_t cmd, uint32_t arg); - bool isTimedOut(uint16_t startMS, uint16_t timeoutMS); bool readData(uint8_t* dst, size_t count); bool readRegister(uint8_t cmd, void* buf); - + void spiSelect() { + sdCsWrite(m_csPin, false); + } + void spiStart(); + void spiStop(); + void spiUnselect() { + sdCsWrite(m_csPin, true); + } void type(uint8_t value) { m_type = value; } - - bool waitNotBusy(uint16_t timeoutMS); + bool waitReady(uint16_t ms); bool writeData(uint8_t token, const uint8_t* src); - - //--------------------------------------------------------------------------- - // functions defined in SdSpiDriver.h +#if SPI_DRIVER_SELECT < 2 void spiActivate() { - m_spiDriver->activate(); + m_spiDriver.activate(); + } + void spiBegin(SdSpiConfig spiConfig) { + m_spiDriver.begin(spiConfig); } void spiDeactivate() { - m_spiDriver->deactivate(); + m_spiDriver.deactivate(); + } + void spiEnd() { + m_spiDriver.end(); } uint8_t spiReceive() { - return m_spiDriver->receive(); + SPI_ASSERT_ACTIVE; + return m_spiDriver.receive(); } uint8_t spiReceive(uint8_t* buf, size_t n) { - return m_spiDriver->receive(buf, n); + SPI_ASSERT_ACTIVE; + return m_spiDriver.receive(buf, n); } void spiSend(uint8_t data) { - m_spiDriver->send(data); + SPI_ASSERT_ACTIVE; + m_spiDriver.send(data); } void spiSend(const uint8_t* buf, size_t n) { - m_spiDriver->send(buf, n); + SPI_ASSERT_ACTIVE; + m_spiDriver.send(buf, n); } - void spiSelect() { - m_spiDriver->select(); + void spiSetSckSpeed(uint32_t maxSck) { + m_spiDriver.setSckSpeed(maxSck); } - void spiUnselect() { - m_spiDriver->unselect(); + SdSpiDriver m_spiDriver; +#else // SPI_DRIVER_SELECT < 2 + void spiActivate() { + m_spiDriverPtr->activate(); + } + void spiBegin(SdSpiConfig spiConfig) { + m_spiDriverPtr->begin(spiConfig); + } + void spiDeactivate() { + m_spiDriverPtr->deactivate(); + } + void spiEnd() { + m_spiDriverPtr->end(); } - uint8_t m_errorCode; - SdSpiDriver *m_spiDriver; + uint8_t spiReceive() { + SPI_ASSERT_ACTIVE; + return m_spiDriverPtr->receive(); + } + uint8_t spiReceive(uint8_t* buf, size_t n) { + SPI_ASSERT_ACTIVE; + return m_spiDriverPtr->receive(buf, n); + } + void spiSend(uint8_t data) { + SPI_ASSERT_ACTIVE; + m_spiDriverPtr->send(data); + } + void spiSend(const uint8_t* buf, size_t n) { + SPI_ASSERT_ACTIVE; + m_spiDriverPtr->send(buf, n); + } + void spiSetSckSpeed(uint32_t maxSck) { + m_spiDriverPtr->setSckSpeed(maxSck); + } + SdSpiDriver* m_spiDriverPtr; + +#endif // SPI_DRIVER_SELECT < 2 + bool m_beginCalled = false; + SdCsPin_t m_csPin; + uint8_t m_errorCode = SD_CARD_ERROR_INIT_NOT_CALLED; bool m_spiActive; + uint8_t m_state; uint8_t m_status; - uint8_t m_type; + uint8_t m_type = 0; }; + //============================================================================== /** - * \class SdSpiCardEX - * \brief Extended SD I/O block driver. + * \class DedicatedSpiCard + * \brief Raw access to SD and SDHC flash memory cards via dedicate SPI port. */ -class SdSpiCardEX : public SdSpiCard { +class DedicatedSpiCard : public SharedSpiCard { public: - /** Initialize the SD card - * - * \param[in] spi SPI driver. - * \param[in] csPin Card chip select pin number. - * \param[in] spiSettings SPI speed, mode, and bit order. - * \return The value true is returned for success and - * the value false is returned for failure. + /** Construct an instance of DedicatedSpiCard. */ + DedicatedSpiCard() {} + /** Initialize the SD card. + * \param[in] spiConfig SPI card configuration. + * \return true for success or false for failure. */ - bool begin(SdSpiDriver* spi, uint8_t csPin, SPISettings spiSettings) { - m_curState = IDLE_STATE; - return SdSpiCard::begin(spi, csPin, spiSettings); - } + bool begin(SdSpiConfig spiConfig); + /** \return true, can be in dedicaded state. */ + bool hasDedicatedSpi() {return true;} + /** \return true if in dedicated SPI state. */ + bool isDedicatedSpi() {return m_dedicatedSpi;} /** - * Read a 512 byte block from an SD card. + * Read a 512 byte sector from an SD card. * - * \param[in] block Logical block to be read. + * \param[in] sector Logical sector to be read. * \param[out] dst Pointer to the location that will receive the data. - * \return The value true is returned for success and - * the value false is returned for failure. - */ - bool readBlock(uint32_t block, uint8_t* dst); - /** End multi-block transfer and go to idle state. - * \return The value true is returned for success and - * the value false is returned for failure. + * \return true for success or false for failure. */ - bool syncBlocks(); + bool readSector(uint32_t sector, uint8_t* dst); /** - * Writes a 512 byte block to an SD card. + * Read multiple 512 byte sectors from an SD card. * - * \param[in] block Logical block to be written. - * \param[in] src Pointer to the location of the data to be written. - * \return The value true is returned for success and - * the value false is returned for failure. + * \param[in] sector Logical sector to be read. + * \param[in] ns Number of sectors to be read. + * \param[out] dst Pointer to the location that will receive the data. + * \return true for success or false for failure. */ - bool writeBlock(uint32_t block, const uint8_t* src); + bool readSectors(uint32_t sector, uint8_t* dst, size_t ns); + /** Set SPI sharing state + * \param[in] value desired state. + * \return true for success else false; + */ + bool setDedicatedSpi(bool value); /** - * Read multiple 512 byte blocks from an SD card. + * Write a 512 byte sector to an SD card. * - * \param[in] block Logical block to be read. - * \param[in] nb Number of blocks to be read. - * \param[out] dst Pointer to the location that will receive the data. - * \return The value true is returned for success and - * the value false is returned for failure. + * \param[in] sector Logical sector to be written. + * \param[in] src Pointer to the location of the data to be written. + * \return true for success or false for failure. */ - bool readBlocks(uint32_t block, uint8_t* dst, size_t nb); + bool writeSector(uint32_t sector, const uint8_t* src); /** - * Write multiple 512 byte blocks to an SD card. + * Write multiple 512 byte sectors to an SD card. * - * \param[in] block Logical block to be written. - * \param[in] nb Number of blocks to be written. + * \param[in] sector Logical sector to be written. + * \param[in] ns Number of sectors to be written. * \param[in] src Pointer to the location of the data to be written. - * \return The value true is returned for success and - * the value false is returned for failure. + * \return true for success or false for failure. */ - bool writeBlocks(uint32_t block, const uint8_t* src, size_t nb); + bool writeSectors(uint32_t sector, const uint8_t* src, size_t ns); private: - static const uint32_t IDLE_STATE = 0; - static const uint32_t READ_STATE = 1; - static const uint32_t WRITE_STATE = 2; - uint32_t m_curBlock; - uint8_t m_curState; + uint32_t m_curSector; + bool m_dedicatedSpi = false; }; +//============================================================================== +#if ENABLE_DEDICATED_SPI +/** typedef for dedicated SPI. */ +typedef DedicatedSpiCard SdSpiCard; +#else +/** typedef for shared SPI. */ +typedef SharedSpiCard SdSpiCard; +#endif #endif // SdSpiCard_h diff --git a/avr/cores/megacommand/SdFat/SdCard/SdSpiCardEX.cpp b/avr/cores/megacommand/SdFat/SdCard/SdSpiCardEX.cpp deleted file mode 100644 index c276ca514..000000000 --- a/avr/cores/megacommand/SdFat/SdCard/SdSpiCardEX.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Copyright (c) 20011-2017 Bill Greiman - * This file is part of the SdFat library for SD memory cards. - * - * MIT License - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ -#include "SdSpiCard.h" -bool SdSpiCardEX::readBlock(uint32_t block, uint8_t* dst) { - if (m_curState != READ_STATE || block != m_curBlock) { - if (!syncBlocks()) { - return false; - } - if (!SdSpiCard::readStart(block)) { - return false; - } - m_curBlock = block; - m_curState = READ_STATE; - } - if (!SdSpiCard::readData(dst)) { - return false; - } - m_curBlock++; - return true; -} -//----------------------------------------------------------------------------- -bool SdSpiCardEX::readBlocks(uint32_t block, uint8_t* dst, size_t nb) { - for (size_t i = 0; i < nb; i++) { - if (!readBlock(block + i, dst + i*512UL)) { - return false; - } - } - return true; -} -//----------------------------------------------------------------------------- -bool SdSpiCardEX::syncBlocks() { - if (m_curState == READ_STATE) { - m_curState = IDLE_STATE; - if (!SdSpiCard::readStop()) { - return false; - } - } else if (m_curState == WRITE_STATE) { - m_curState = IDLE_STATE; - if (!SdSpiCard::writeStop()) { - return false; - } - } - return true; -} -//----------------------------------------------------------------------------- -bool SdSpiCardEX::writeBlock(uint32_t block, const uint8_t* src) { - if (m_curState != WRITE_STATE || m_curBlock != block) { - if (!syncBlocks()) { - return false; - } - if (!SdSpiCard::writeStart(block)) { - return false; - } - m_curBlock = block; - m_curState = WRITE_STATE; - } - if (!SdSpiCard::writeData(src)) { - return false; - } - m_curBlock++; - return true; -} -//----------------------------------------------------------------------------- -bool SdSpiCardEX::writeBlocks(uint32_t block, - const uint8_t* src, size_t nb) { - for (size_t i = 0; i < nb; i++) { - if (!writeBlock(block + i, src + i*512UL)) { - return false; - } - } - return true; -} diff --git a/avr/cores/megacommand/SdFat/SdCard/SdioCard.h b/avr/cores/megacommand/SdFat/SdCard/SdioCard.h index 188b0f766..ffedb46ce 100644 --- a/avr/cores/megacommand/SdFat/SdCard/SdioCard.h +++ b/avr/cores/megacommand/SdFat/SdCard/SdioCard.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 20011-2017 Bill Greiman + * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -24,47 +24,78 @@ */ #ifndef SdioCard_h #define SdioCard_h -#include "SysCall.h" -#include "BlockDriver.h" +#include "../common/SysCall.h" +#include "SdCardInterface.h" + +#define FIFO_SDIO 0 +#define DMA_SDIO 1 +/** + * \class SdioConfig + * \brief SDIO card configuration. + */ +class SdioConfig { + public: + SdioConfig() {} + /** + * SdioConfig constructor. + * \param[in] opt SDIO options. + */ + explicit SdioConfig(uint8_t opt) : m_options(opt) {} + /** \return SDIO card options. */ + uint8_t options() {return m_options;} + /** \return true if DMA_SDIO. */ + bool useDma() {return m_options & DMA_SDIO;} + private: + uint8_t m_options = FIFO_SDIO; +}; +//------------------------------------------------------------------------------ /** * \class SdioCard * \brief Raw SDIO access to SD and SDHC flash memory cards. */ -class SdioCard : public BaseBlockDriver { +class SdioCard : public SdCardInterface { public: /** Initialize the SD card. - * \return true for success else false. + * \param[in] sdioConfig SDIO card configuration. + * \return true for success or false for failure. */ - bool begin(); - /** - * Determine the size of an SD flash memory card. + bool begin(SdioConfig sdioConfig); + /** CMD6 Switch mode: Check Function Set Function. + * \param[in] arg CMD6 argument. + * \param[out] status return status data. * - * \return The number of 512 byte data blocks in the card - * or zero if an error occurs. + * \return true for success or false for failure. + */ + bool cardCMD6(uint32_t arg, uint8_t* status); + /** Disable an SDIO card. + * not implemented. */ - uint32_t cardSize(); - /** Erase a range of blocks. + void end() {} + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + uint32_t __attribute__((error("use sectorCount()"))) cardSize(); +#endif // DOXYGEN_SHOULD_SKIP_THIS + /** Erase a range of sectors. * - * \param[in] firstBlock The address of the first block in the range. - * \param[in] lastBlock The address of the last block in the range. + * \param[in] firstSector The address of the first sector in the range. + * \param[in] lastSector The address of the last sector in the range. * * \note This function requests the SD card to do a flash erase for a - * range of blocks. The data on the card after an erase operation is + * range of sectors. The data on the card after an erase operation is * either 0 or 1, depends on the card vendor. The card must support - * single block erase. + * single sector erase. * - * \return The value true is returned for success and - * the value false is returned for failure. + * \return true for success or false for failure. */ - bool erase(uint32_t firstBlock, uint32_t lastBlock); + bool erase(uint32_t firstSector, uint32_t lastSector); /** - * \return code for the last error. See SdInfo.h for a list of error codes. + * \return code for the last error. See SdCardInfo.h for a list of error codes. */ - uint8_t errorCode(); + uint8_t errorCode() const; /** \return error data for last error. */ - uint32_t errorData(); + uint32_t errorData() const; /** \return error line for last error. Tmp function for debug. */ - uint32_t errorLine(); + uint32_t errorLine() const; /** * Check for busy with CMD13. * @@ -74,24 +105,22 @@ class SdioCard : public BaseBlockDriver { /** \return the SD clock frequency in kHz. */ uint32_t kHzSdClk(); /** - * Read a 512 byte block from an SD card. + * Read a 512 byte sector from an SD card. * - * \param[in] lba Logical block to be read. + * \param[in] sector Logical sector to be read. * \param[out] dst Pointer to the location that will receive the data. - * \return The value true is returned for success and - * the value false is returned for failure. + * \return true for success or false for failure. */ - bool readBlock(uint32_t lba, uint8_t* dst); + bool readSector(uint32_t sector, uint8_t* dst); /** - * Read multiple 512 byte blocks from an SD card. + * Read multiple 512 byte sectors from an SD card. * - * \param[in] lba Logical block to be read. - * \param[in] nb Number of blocks to be read. + * \param[in] sector Logical sector to be read. + * \param[in] ns Number of sectors to be read. * \param[out] dst Pointer to the location that will receive the data. - * \return The value true is returned for success and - * the value false is returned for failure. + * \return true for success or false for failure. */ - bool readBlocks(uint32_t lba, uint8_t* dst, size_t nb); + bool readSectors(uint32_t sector, uint8_t* dst, size_t ns); /** * Read a card's CID register. The CID contains card identification * information such as Manufacturer ID, Product name, Product serial @@ -101,7 +130,7 @@ class SdioCard : public BaseBlockDriver { * * \return true for success or false for failure. */ - bool readCID(void* cid); + bool readCID(cid_t* cid); /** * Read a card's CSD register. The CSD contains Card-Specific Data that * provides information regarding access to the card's contents. @@ -110,192 +139,129 @@ class SdioCard : public BaseBlockDriver { * * \return true for success or false for failure. */ - bool readCSD(void* csd); - /** Read one data block in a multiple block read sequence + bool readCSD(csd_t* csd); + /** Read one data sector in a multiple sector read sequence * * \param[out] dst Pointer to the location for the data to be read. * - * \return The value true is returned for success and - * the value false is returned for failure. + * \return true for success or false for failure. */ - bool readData(uint8_t *dst); + bool readData(uint8_t* dst); /** Read OCR register. * * \param[out] ocr Value of OCR register. - * \return true for success else false. + * \return true for success or false for failure. */ bool readOCR(uint32_t* ocr); - /** Start a read multiple blocks sequence. + /** Read SCR register. + * + * \param[out] scr Value of SCR register. + * \return true for success or false for failure. + */ + bool readSCR(scr_t *scr); + /** Start a read multiple sectors sequence. * - * \param[in] lba Address of first block in sequence. + * \param[in] sector Address of first sector 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. + * multiple sector reads. SPI chipSelect must be low for the entire sequence. * - * \return The value true is returned for success and - * the value false is returned for failure. + * \return true for success or false for failure. */ - bool readStart(uint32_t lba); - /** Start a read multiple blocks sequence. + bool readStart(uint32_t sector); + /** Start a read multiple sectors sequence. * - * \param[in] lba Address of first block in sequence. - * \param[in] count Maximum block count. + * \param[in] sector Address of first sector in sequence. + * \param[in] count Maximum sector count. * \note This function is used with readData() and readStop() for optimized - * multiple block reads. SPI chipSelect must be low for the entire sequence. + * multiple sector reads. SPI chipSelect must be low for the entire sequence. * - * \return The value true is returned for success and - * the value false is returned for failure. + * \return true for success or false for failure. */ - bool readStart(uint32_t lba, uint32_t count); - /** End a read multiple blocks sequence. + bool readStart(uint32_t sector, uint32_t count); + /** End a read multiple sectors sequence. * - * \return The value true is returned for success and - * the value false is returned for failure. + * \return true for success or false for failure. */ bool readStop(); + /** \return SDIO card status. */ + uint32_t status(); + /** + * Determine the size of an SD flash memory card. + * + * \return The number of 512 byte data sectors in the card + * or zero if an error occurs. + */ + uint32_t sectorCount(); + /** + * Send CMD12 to stop read or write. + * + * \param[in] blocking If true, wait for command complete. + * + * \return true for success or false for failure. + */ + bool stopTransmission(bool blocking); /** \return success if sync successful. Not for user apps. */ - bool syncBlocks(); + bool syncDevice(); /** Return the card type: SD V1, SD V2 or SDHC * \return 0 - SD V1, 1 - SD V2, or 3 - SDHC. */ - uint8_t type(); + uint8_t type() const; /** - * Writes a 512 byte block to an SD card. + * Writes a 512 byte sector to an SD card. * - * \param[in] lba Logical block to be written. + * \param[in] sector Logical sector to be written. * \param[in] src Pointer to the location of the data to be written. - * \return The value true is returned for success and - * the value false is returned for failure. + * \return true for success or false for failure. */ - bool writeBlock(uint32_t lba, const uint8_t* src); + bool writeSector(uint32_t sector, const uint8_t* src); /** - * Write multiple 512 byte blocks to an SD card. + * Write multiple 512 byte sectors to an SD card. * - * \param[in] lba Logical block to be written. - * \param[in] nb Number of blocks to be written. + * \param[in] sector Logical sector to be written. + * \param[in] ns Number of sectors to be written. * \param[in] src Pointer to the location of the data to be written. - * \return The value true is returned for success and - * the value false is returned for failure. + * \return true for success or false for failure. */ - bool writeBlocks(uint32_t lba, const uint8_t* src, size_t nb); - /** Write one data block in a multiple block write sequence. + bool writeSectors(uint32_t sector, const uint8_t* src, size_t ns); + /** Write one data sector in a multiple sector write sequence. * \param[in] src Pointer to the location of the data to be written. - * \return The value true is returned for success and - * the value false is returned for failure. + * \return true for success or false for failure. */ bool writeData(const uint8_t* src); - /** Start a write multiple blocks sequence. + /** Start a write multiple sectors sequence. * - * \param[in] lba Address of first block in sequence. + * \param[in] sector Address of first sector in sequence. * * \note This function is used with writeData() and writeStop() - * for optimized multiple block writes. + * for optimized multiple sector writes. * - * \return The value true is returned for success and - * the value false is returned for failure. + * \return true for success or false for failure. */ - bool writeStart(uint32_t lba); - /** Start a write multiple blocks sequence. + bool writeStart(uint32_t sector); + /** Start a write multiple sectors sequence. * - * \param[in] lba Address of first block in sequence. - * \param[in] count Maximum block count. + * \param[in] sector Address of first sector in sequence. + * \param[in] count Maximum sector count. * \note This function is used with writeData() and writeStop() - * for optimized multiple block writes. + * for optimized multiple sector writes. * - * \return The value true is returned for success and - * the value false is returned for failure. + * \return true for success or false for failure. */ - bool writeStart(uint32_t lba, uint32_t count); + bool writeStart(uint32_t sector, uint32_t count); - /** End a write multiple blocks sequence. + /** End a write multiple sectors sequence. * - * \return The value true is returned for success and - * the value false is returned for failure. + * \return true for success or false for failure. */ bool writeStop(); -}; -//============================================================================== -/** - * \class SdioCardEX - * \brief Extended SD I/O block driver. - */ -class SdioCardEX : public SdioCard { - public: - /** Initialize the SD card - * - * \return The value true is returned for success and - * the value false is returned for failure. - */ - bool begin() { - m_curState = IDLE_STATE; - return SdioCard::begin(); - } - /** Erase a range of blocks. - * - * \param[in] firstBlock The address of the first block in the range. - * \param[in] lastBlock The address of the last block in the range. - * - * \note This function requests the SD card to do a flash erase for a - * range of blocks. The data on the card after an erase operation is - * either 0 or 1, depends on the card vendor. The card must support - * single block erase. - * - * \return The value true is returned for success and - * the value false is returned for failure. - */ - bool erase(uint32_t firstBlock, uint32_t lastBlock) { - return syncBlocks() && SdioCard::erase(firstBlock, lastBlock); - } - /** - * Read a 512 byte block from an SD card. - * - * \param[in] block Logical block to be read. - * \param[out] dst Pointer to the location that will receive the data. - * \return The value true is returned for success and - * the value false is returned for failure. - */ - bool readBlock(uint32_t block, uint8_t* dst); - /** End multi-block transfer and go to idle state. - * \return The value true is returned for success and - * the value false is returned for failure. - */ - bool syncBlocks(); - /** - * Writes a 512 byte block to an SD card. - * - * \param[in] block Logical block to be written. - * \param[in] src Pointer to the location of the data to be written. - * \return The value true is returned for success and - * the value false is returned for failure. - */ - bool writeBlock(uint32_t block, const uint8_t* src); - /** - * Read multiple 512 byte blocks from an SD card. - * - * \param[in] block Logical block to be read. - * \param[in] nb Number of blocks to be read. - * \param[out] dst Pointer to the location that will receive the data. - * \return The value true is returned for success and - * the value false is returned for failure. - */ - bool readBlocks(uint32_t block, uint8_t* dst, size_t nb); - /** - * Write multiple 512 byte blocks to an SD card. - * - * \param[in] block Logical block to be written. - * \param[in] nb Number of blocks to be written. - * \param[in] src Pointer to the location of the data to be written. - * \return The value true is returned for success and - * the value false is returned for failure. - */ - bool writeBlocks(uint32_t block, const uint8_t* src, size_t nb); private: - static const uint32_t IDLE_STATE = 0; - static const uint32_t READ_STATE = 1; - static const uint32_t WRITE_STATE = 2; - uint32_t m_curLba; - uint32_t m_limitLba; - uint8_t m_curState; + static const uint8_t IDLE_STATE = 0; + static const uint8_t READ_STATE = 1; + static const uint8_t WRITE_STATE = 2; + uint32_t m_curSector; + SdioConfig m_sdioConfig; + uint8_t m_curState = IDLE_STATE; }; #endif // SdioCard_h diff --git a/avr/cores/megacommand/SdFat/SdCard/SdioCardEX.cpp b/avr/cores/megacommand/SdFat/SdCard/SdioCardEX.cpp deleted file mode 100644 index edbed2b81..000000000 --- a/avr/cores/megacommand/SdFat/SdCard/SdioCardEX.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/** - * Copyright (c) 20011-2017 Bill Greiman - * This file is part of the SdFat library for SD memory cards. - * - * MIT License - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ -#include "SdioCard.h" - -// limit of K66 due to errata KINETIS_K_0N65N. -const uint32_t MAX_SDHC_COUNT = 0XFFFF; - -// Max RU is 1024 blocks. -const uint32_t RU_MASK = 0X03FF; - -bool SdioCardEX::readBlock(uint32_t lba, uint8_t* dst) { - if (m_curState != READ_STATE || lba != m_curLba) { - if (!syncBlocks()) { - return false; - } - m_limitLba = (lba + MAX_SDHC_COUNT) & ~RU_MASK; - if (!SdioCard::readStart(lba, m_limitLba - lba)) { - return false; - } - m_curLba = lba; - m_curState = READ_STATE; - } - if (!SdioCard::readData(dst)) { - return false; - } - m_curLba++; - if (m_curLba >= m_limitLba) { - m_curState = IDLE_STATE; - } - return true; -} -//----------------------------------------------------------------------------- -bool SdioCardEX::readBlocks(uint32_t lba, uint8_t* dst, size_t nb) { - for (size_t i = 0; i < nb; i++) { - if (!readBlock(lba + i, dst + i*512UL)) { - return false; - } - } - return true; -} -//----------------------------------------------------------------------------- -bool SdioCardEX::syncBlocks() { - if (m_curState == READ_STATE) { - m_curState = IDLE_STATE; - if (!SdioCard::readStop()) { - return false; - } - } else if (m_curState == WRITE_STATE) { - m_curState = IDLE_STATE; - if (!SdioCard::writeStop()) { - return false; - } - } - return true; -} -//----------------------------------------------------------------------------- -bool SdioCardEX::writeBlock(uint32_t lba, const uint8_t* src) { - if (m_curState != WRITE_STATE || m_curLba != lba) { - if (!syncBlocks()) { - return false; - } - m_limitLba = (lba + MAX_SDHC_COUNT) & ~RU_MASK; - if (!SdioCard::writeStart(lba , m_limitLba - lba)) { - return false; - } - m_curLba = lba; - m_curState = WRITE_STATE; - } - if (!SdioCard::writeData(src)) { - return false; - } - m_curLba++; - if (m_curLba >= m_limitLba) { - m_curState = IDLE_STATE; - } - return true; -} -//----------------------------------------------------------------------------- -bool SdioCardEX::writeBlocks(uint32_t lba, const uint8_t* src, size_t nb) { - for (size_t i = 0; i < nb; i++) { - if (!writeBlock(lba + i, src + i*512UL)) { - return false; - } - } - return true; -} diff --git a/avr/cores/megacommand/SdFat/SdCard/SdioTeensy.cpp b/avr/cores/megacommand/SdFat/SdCard/SdioTeensy.cpp index 95ce779c8..1333dbfe1 100644 --- a/avr/cores/megacommand/SdFat/SdCard/SdioTeensy.cpp +++ b/avr/cores/megacommand/SdFat/SdCard/SdioTeensy.cpp @@ -1,5 +1,5 @@ /** - * Copyright (c) 20011-2017 Bill Greiman + * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -22,13 +22,18 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ -#if defined(__MK64FX512__) || defined(__MK66FX1M0__) +#if defined(__MK64FX512__) || defined(__MK66FX1M0__) || defined(__IMXRT1062__) +#include "SdioTeensy.h" +#include "SdCardInfo.h" #include "SdioCard.h" //============================================================================== +// limit of K66 due to errata KINETIS_K_0N65N. +const uint32_t MAX_BLKCNT = 0XFFFF; +//============================================================================== #define SDHC_PROCTL_DTW_4BIT 0x01 const uint32_t FIFO_WML = 16; -const uint32_t CMD8_RETRIES = 10; -const uint32_t BUSY_TIMEOUT_MICROS = 500000; +const uint32_t CMD8_RETRIES = 3; +const uint32_t BUSY_TIMEOUT_MICROS = 1000000; //============================================================================== const uint32_t SDHC_IRQSTATEN_MASK = SDHC_IRQSTATEN_DMAESEN | SDHC_IRQSTATEN_AC12ESEN | @@ -56,7 +61,7 @@ const uint32_t SDHC_IRQSIGEN_MASK = SDHC_IRQSIGEN_DTOEIEN | SDHC_IRQSIGEN_CIEIEN | SDHC_IRQSIGEN_CEBEIEN | SDHC_IRQSIGEN_CCEIEN | SDHC_IRQSIGEN_CTOEIEN | SDHC_IRQSIGEN_TCIEN; -//============================================================================= +//============================================================================== const uint32_t CMD_RESP_NONE = SDHC_XFERTYP_RSPTYP(0); const uint32_t CMD_RESP_R1 = SDHC_XFERTYP_CICEN | SDHC_XFERTYP_CCCEN | @@ -73,6 +78,7 @@ const uint32_t CMD_RESP_R6 = CMD_RESP_R1; const uint32_t CMD_RESP_R7 = CMD_RESP_R1; +#if defined(__MK64FX512__) || defined(__MK66FX1M0__) const uint32_t DATA_READ = SDHC_XFERTYP_DTDSEL | SDHC_XFERTYP_DPSEL; const uint32_t DATA_READ_DMA = DATA_READ | SDHC_XFERTYP_DMAEN; @@ -81,21 +87,52 @@ const uint32_t DATA_READ_MULTI_DMA = DATA_READ_DMA | SDHC_XFERTYP_MSBSEL | SDHC_XFERTYP_AC12EN | SDHC_XFERTYP_BCEN; const uint32_t DATA_READ_MULTI_PGM = DATA_READ | SDHC_XFERTYP_MSBSEL | - SDHC_XFERTYP_BCEN | SDHC_XFERTYP_AC12EN; + SDHC_XFERTYP_BCEN; const uint32_t DATA_WRITE_DMA = SDHC_XFERTYP_DPSEL | SDHC_XFERTYP_DMAEN; const uint32_t DATA_WRITE_MULTI_DMA = DATA_WRITE_DMA | SDHC_XFERTYP_MSBSEL | SDHC_XFERTYP_AC12EN | SDHC_XFERTYP_BCEN; -const uint32_t DATA_WRITE_MULTI_PGM = SDHC_XFERTYP_DPSEL | - SDHC_XFERTYP_MSBSEL | - SDHC_XFERTYP_BCEN | SDHC_XFERTYP_AC12EN; +const uint32_t DATA_WRITE_MULTI_PGM = SDHC_XFERTYP_DPSEL | SDHC_XFERTYP_MSBSEL | + SDHC_XFERTYP_BCEN; + +#elif defined(__IMXRT1062__) +// Use low bits for SDHC_MIX_CTRL since bits 15-0 of SDHC_XFERTYP are reserved. +const uint32_t SDHC_MIX_CTRL_MASK = SDHC_MIX_CTRL_DMAEN | SDHC_MIX_CTRL_BCEN | + SDHC_MIX_CTRL_AC12EN | + SDHC_MIX_CTRL_DDR_EN | + SDHC_MIX_CTRL_DTDSEL | + SDHC_MIX_CTRL_MSBSEL | + SDHC_MIX_CTRL_NIBBLE_POS | + SDHC_MIX_CTRL_AC23EN; + +const uint32_t DATA_READ = SDHC_MIX_CTRL_DTDSEL | SDHC_XFERTYP_DPSEL; + +const uint32_t DATA_READ_DMA = DATA_READ | SDHC_MIX_CTRL_DMAEN; + +const uint32_t DATA_READ_MULTI_DMA = DATA_READ_DMA | SDHC_MIX_CTRL_MSBSEL | + SDHC_MIX_CTRL_AC12EN | SDHC_MIX_CTRL_BCEN; + +const uint32_t DATA_READ_MULTI_PGM = DATA_READ | SDHC_MIX_CTRL_MSBSEL; + + +const uint32_t DATA_WRITE_DMA = SDHC_XFERTYP_DPSEL | SDHC_MIX_CTRL_DMAEN; + +const uint32_t DATA_WRITE_MULTI_DMA = DATA_WRITE_DMA | SDHC_MIX_CTRL_MSBSEL | + SDHC_MIX_CTRL_AC12EN | SDHC_MIX_CTRL_BCEN; + +const uint32_t DATA_WRITE_MULTI_PGM = SDHC_XFERTYP_DPSEL | SDHC_MIX_CTRL_MSBSEL; + +#endif // defined(__MK64FX512__) || defined(__MK66FX1M0__) const uint32_t ACMD6_XFERTYP = SDHC_XFERTYP_CMDINX(ACMD6) | CMD_RESP_R1; const uint32_t ACMD41_XFERTYP = SDHC_XFERTYP_CMDINX(ACMD41) | CMD_RESP_R3; +const uint32_t ACMD51_XFERTYP = SDHC_XFERTYP_CMDINX(ACMD51) | CMD_RESP_R1 | + DATA_READ_DMA; + const uint32_t CMD0_XFERTYP = SDHC_XFERTYP_CMDINX(CMD0) | CMD_RESP_NONE; const uint32_t CMD2_XFERTYP = SDHC_XFERTYP_CMDINX(CMD2) | CMD_RESP_R2; @@ -113,6 +150,8 @@ const uint32_t CMD9_XFERTYP = SDHC_XFERTYP_CMDINX(CMD9) | CMD_RESP_R2; const uint32_t CMD10_XFERTYP = SDHC_XFERTYP_CMDINX(CMD10) | CMD_RESP_R2; +const uint32_t CMD11_XFERTYP = SDHC_XFERTYP_CMDINX(CMD11) | CMD_RESP_R1; + const uint32_t CMD12_XFERTYP = SDHC_XFERTYP_CMDINX(CMD12) | CMD_RESP_R1b | SDHC_XFERTYP_CMDTYP(3); @@ -144,7 +183,7 @@ const uint32_t CMD38_XFERTYP = SDHC_XFERTYP_CMDINX(CMD38) | CMD_RESP_R1b; const uint32_t CMD55_XFERTYP = SDHC_XFERTYP_CMDINX(CMD55) | CMD_RESP_R1; -//============================================================================= +//============================================================================== static bool cardCommand(uint32_t xfertyp, uint32_t arg); static void enableGPIO(bool enable); static void enableDmaIrs(); @@ -157,11 +196,12 @@ static void setSdclk(uint32_t kHzMax); static bool yieldTimeout(bool (*fcn)()); static bool waitDmaStatus(); static bool waitTimeout(bool (*fcn)()); -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ static bool (*m_busyFcn)() = 0; static bool m_initDone = false; static bool m_version2; static bool m_highCapacity; +static bool m_transferActive = false; static uint8_t m_errorCode = SD_CARD_ERROR_INIT_NOT_CALLED; static uint32_t m_errorLine = 0; static uint32_t m_rca; @@ -171,54 +211,190 @@ static uint32_t m_sdClkKhz = 0; static uint32_t m_ocr; static cid_t m_cid; static csd_t m_csd; -//============================================================================= +static scr_t m_scr; +//============================================================================== +#define DBG_TRACE Serial.print("TRACE."); Serial.println(__LINE__); delay(200); #define USE_DEBUG_MODE 0 #if USE_DEBUG_MODE #define DBG_IRQSTAT() if (SDHC_IRQSTAT) {Serial.print(__LINE__);\ Serial.print(" IRQSTAT "); Serial.println(SDHC_IRQSTAT, HEX);} - static void printRegs(uint32_t line) { - Serial.print(line); - Serial.print(" PRSSTAT "); - Serial.print(SDHC_PRSSTAT, HEX); - Serial.print(" PROCTL "); - Serial.print(SDHC_PROCTL, HEX); - Serial.print(" IRQSTAT "); - Serial.print(SDHC_IRQSTAT, HEX); - Serial.print(" m_irqstat "); + uint32_t blkattr = SDHC_BLKATTR; + uint32_t xfertyp = SDHC_XFERTYP; + uint32_t prsstat = SDHC_PRSSTAT; + uint32_t proctl = SDHC_PROCTL; + uint32_t irqstat = SDHC_IRQSTAT; + Serial.print("\nLINE: "); + Serial.println(line); + Serial.print("BLKATTR "); + Serial.println(blkattr, HEX); + Serial.print("XFERTYP "); + Serial.print(xfertyp, HEX); + Serial.print(" CMD"); + Serial.print(xfertyp >> 24); + Serial.print(" TYP"); + Serial.print((xfertyp >> 2) & 3); + if (xfertyp & SDHC_XFERTYP_DPSEL) {Serial.print(" DPSEL");} + Serial.println(); + Serial.print("PRSSTAT "); + Serial.print(prsstat, HEX); + if (prsstat & SDHC_PRSSTAT_BREN) {Serial.print(" BREN");} + if (prsstat & SDHC_PRSSTAT_BWEN) {Serial.print(" BWEN");} + if (prsstat & SDHC_PRSSTAT_RTA) {Serial.print(" RTA");} + if (prsstat & SDHC_PRSSTAT_WTA) {Serial.print(" WTA");} + if (prsstat & SDHC_PRSSTAT_SDOFF) {Serial.print(" SDOFF");} + if (prsstat & SDHC_PRSSTAT_PEROFF) {Serial.print(" PEROFF");} + if (prsstat & SDHC_PRSSTAT_HCKOFF) {Serial.print(" HCKOFF");} + if (prsstat & SDHC_PRSSTAT_IPGOFF) {Serial.print(" IPGOFF");} + if (prsstat & SDHC_PRSSTAT_SDSTB) {Serial.print(" SDSTB");} + if (prsstat & SDHC_PRSSTAT_DLA) {Serial.print(" DLA");} + if (prsstat & SDHC_PRSSTAT_CDIHB) {Serial.print(" CDIHB");} + if (prsstat & SDHC_PRSSTAT_CIHB) {Serial.print(" CIHB");} + Serial.println(); + Serial.print("PROCTL "); + Serial.print(proctl, HEX); + if (proctl & SDHC_PROCTL_SABGREQ) Serial.print(" SABGREQ"); + Serial.print(" EMODE"); + Serial.print((proctl >>4) & 3); + Serial.print(" DWT"); + Serial.print((proctl >>1) & 3); + Serial.println(); + Serial.print("IRQSTAT "); + Serial.print(irqstat, HEX); + if (irqstat & SDHC_IRQSTAT_BGE) {Serial.print(" BGE");} + if (irqstat & SDHC_IRQSTAT_TC) {Serial.print(" TC");} + if (irqstat & SDHC_IRQSTAT_CC) {Serial.print(" CC");} + Serial.print("\nm_irqstat "); Serial.println(m_irqstat, HEX); } #else // USE_DEBUG_MODE #define DBG_IRQSTAT() #endif // USE_DEBUG_MODE -//============================================================================= +//============================================================================== // Error function and macro. #define sdError(code) setSdErrorCode(code, __LINE__) inline bool setSdErrorCode(uint8_t code, uint32_t line) { m_errorCode = code; m_errorLine = line; - return false; // setSdErrorCode +#if USE_DEBUG_MODE + printRegs(line); +#endif // USE_DEBUG_MODE + return false; } -//============================================================================= +//============================================================================== // ISR -void sdhc_isr() { +static void sdIrs() { SDHC_IRQSIGEN = 0; m_irqstat = SDHC_IRQSTAT; SDHC_IRQSTAT = m_irqstat; +#if defined(__IMXRT1062__) + SDHC_MIX_CTRL &= ~(SDHC_MIX_CTRL_AC23EN | SDHC_MIX_CTRL_DMAEN); +#endif m_dmaBusy = false; } -//============================================================================= +//============================================================================== +// GPIO and clock functions. +#if defined(__MK64FX512__) || defined(__MK66FX1M0__) +//------------------------------------------------------------------------------ +static void enableGPIO(bool enable) { + const uint32_t PORT_CLK = PORT_PCR_MUX(4) | PORT_PCR_DSE; + const uint32_t PORT_CMD_DATA = PORT_CLK | PORT_PCR_PE | PORT_PCR_PS; + const uint32_t PORT_PUP = PORT_PCR_MUX(1) | PORT_PCR_PE | PORT_PCR_PS; + + PORTE_PCR0 = enable ? PORT_CMD_DATA : PORT_PUP; // SDHC_D1 + PORTE_PCR1 = enable ? PORT_CMD_DATA : PORT_PUP; // SDHC_D0 + PORTE_PCR2 = enable ? PORT_CLK : PORT_PUP; // SDHC_CLK + PORTE_PCR3 = enable ? PORT_CMD_DATA : PORT_PUP; // SDHC_CMD + PORTE_PCR4 = enable ? PORT_CMD_DATA : PORT_PUP; // SDHC_D3 + PORTE_PCR5 = enable ? PORT_CMD_DATA : PORT_PUP; // SDHC_D2 +} +//------------------------------------------------------------------------------ +static void initClock() { +#ifdef HAS_KINETIS_MPU + // Allow SDHC Bus Master access. + MPU_RGDAAC0 |= 0x0C000000; +#endif // HAS_KINETIS_MPU + // Enable SDHC clock. + SIM_SCGC3 |= SIM_SCGC3_SDHC; +} +static uint32_t baseClock() { return F_CPU;} + +#elif defined(__IMXRT1062__) +//------------------------------------------------------------------------------ +static void gpioMux(uint8_t mode) { + IOMUXC_SW_MUX_CTL_PAD_GPIO_SD_B0_04 = mode; // DAT2 + IOMUXC_SW_MUX_CTL_PAD_GPIO_SD_B0_05 = mode; // DAT3 + IOMUXC_SW_MUX_CTL_PAD_GPIO_SD_B0_00 = mode; // CMD + IOMUXC_SW_MUX_CTL_PAD_GPIO_SD_B0_01 = mode; // CLK + IOMUXC_SW_MUX_CTL_PAD_GPIO_SD_B0_02 = mode; // DAT0 + IOMUXC_SW_MUX_CTL_PAD_GPIO_SD_B0_03 = mode; // DAT1 +} +//------------------------------------------------------------------------------ +// add speed strength args? +static void enableGPIO(bool enable) { + const uint32_t CLOCK_MASK = IOMUXC_SW_PAD_CTL_PAD_PKE | +#if defined(ARDUINO_TEENSY41) + IOMUXC_SW_PAD_CTL_PAD_DSE(7) | +#else // defined(ARDUINO_TEENSY41) + IOMUXC_SW_PAD_CTL_PAD_DSE(4) | ///// WHG +#endif // defined(ARDUINO_TEENSY41) + IOMUXC_SW_PAD_CTL_PAD_SPEED(2); + + const uint32_t DATA_MASK = CLOCK_MASK | IOMUXC_SW_PAD_CTL_PAD_PUE | + IOMUXC_SW_PAD_CTL_PAD_PUS(1); + if (enable) { + gpioMux(0); + IOMUXC_SW_PAD_CTL_PAD_GPIO_SD_B0_04 = DATA_MASK; // DAT2 + IOMUXC_SW_PAD_CTL_PAD_GPIO_SD_B0_05 = DATA_MASK; // DAT3 + IOMUXC_SW_PAD_CTL_PAD_GPIO_SD_B0_00 = DATA_MASK; // CMD + IOMUXC_SW_PAD_CTL_PAD_GPIO_SD_B0_01 = CLOCK_MASK; // CLK + IOMUXC_SW_PAD_CTL_PAD_GPIO_SD_B0_02 = DATA_MASK; // DAT0 + IOMUXC_SW_PAD_CTL_PAD_GPIO_SD_B0_03 = DATA_MASK; // DAT1 + } else { + gpioMux(5); + } +} +//------------------------------------------------------------------------------ +static void initClock() { + /* set PDF_528 PLL2PFD0 */ + CCM_ANALOG_PFD_528 |= (1 << 7); + CCM_ANALOG_PFD_528 &= ~(0x3F << 0); + CCM_ANALOG_PFD_528 |= ((24) & 0x3F << 0); // 12 - 35 + CCM_ANALOG_PFD_528 &= ~(1 << 7); + + /* Enable USDHC clock. */ + CCM_CCGR6 |= CCM_CCGR6_USDHC1(CCM_CCGR_ON); + CCM_CSCDR1 &= ~(CCM_CSCDR1_USDHC1_CLK_PODF_MASK); + CCM_CSCMR1 |= CCM_CSCMR1_USDHC1_CLK_SEL; // PLL2PFD0 +// CCM_CSCDR1 |= CCM_CSCDR1_USDHC1_CLK_PODF((7)); / &0x7 WHG + CCM_CSCDR1 |= CCM_CSCDR1_USDHC1_CLK_PODF((1)); +} +//------------------------------------------------------------------------------ +static uint32_t baseClock() { + uint32_t divider = ((CCM_CSCDR1 >> 11) & 0x7) + 1; + return (528000000U * 3)/((CCM_ANALOG_PFD_528 & 0x3F)/6)/divider; +} +#endif // defined(__MK64FX512__) || defined(__MK66FX1M0__) +//============================================================================== // Static functions. static bool cardAcmd(uint32_t rca, uint32_t xfertyp, uint32_t arg) { return cardCommand(CMD55_XFERTYP, rca) && cardCommand (xfertyp, arg); } -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ static bool cardCommand(uint32_t xfertyp, uint32_t arg) { DBG_IRQSTAT(); if (waitTimeout(isBusyCommandInhibit)) { return false; // Caller will set errorCode. } SDHC_CMDARG = arg; +#if defined(__IMXRT1062__) + // Set MIX_CTRL if data transfer. + if (xfertyp & SDHC_XFERTYP_DPSEL) { + SDHC_MIX_CTRL &= ~SDHC_MIX_CTRL_MASK; + SDHC_MIX_CTRL |= xfertyp & SDHC_MIX_CTRL_MASK; + } + xfertyp &= ~SDHC_MIX_CTRL_MASK; +#endif // defined(__IMXRT1062__) SDHC_XFERTYP = xfertyp; if (waitTimeout(isBusyCommandComplete)) { return false; // Caller will set errorCode. @@ -229,65 +405,55 @@ static bool cardCommand(uint32_t xfertyp, uint32_t arg) { return (m_irqstat & SDHC_IRQSTAT_CC) && !(m_irqstat & SDHC_IRQSTAT_CMD_ERROR); } -//----------------------------------------------------------------------------- -static bool cardCMD6(uint32_t arg, uint8_t* status) { - // CMD6 returns 64 bytes. +//------------------------------------------------------------------------------ +static bool cardACMD51(scr_t* scr) { + // ACMD51 returns 8 bytes. if (waitTimeout(isBusyCMD13)) { return sdError(SD_CARD_ERROR_CMD13); } enableDmaIrs(); - SDHC_DSADDR = (uint32_t)status; - SDHC_CMDARG = arg; - SDHC_BLKATTR = SDHC_BLKATTR_BLKCNT(1) | SDHC_BLKATTR_BLKSIZE(64); + SDHC_DSADDR = (uint32_t)scr; + SDHC_BLKATTR = SDHC_BLKATTR_BLKCNT(1) | SDHC_BLKATTR_BLKSIZE(8); SDHC_IRQSIGEN = SDHC_IRQSIGEN_MASK; - SDHC_XFERTYP = CMD6_XFERTYP; - + if (!cardAcmd(m_rca, ACMD51_XFERTYP, 0)) { + return sdError(SD_CARD_ERROR_ACMD51); + } if (!waitDmaStatus()) { - return sdError(SD_CARD_ERROR_CMD6); + return sdError(SD_CARD_ERROR_DMA); } return true; } -//----------------------------------------------------------------------------- -static void enableGPIO(bool enable) { - const uint32_t PORT_CLK = PORT_PCR_MUX(4) | PORT_PCR_DSE; - const uint32_t PORT_CMD_DATA = PORT_CLK | PORT_PCR_PS | PORT_PCR_PE; - - PORTE_PCR0 = enable ? PORT_CMD_DATA : 0; // SDHC_D1 - PORTE_PCR1 = enable ? PORT_CMD_DATA : 0; // SDHC_D0 - PORTE_PCR2 = enable ? PORT_CLK : 0; // SDHC_CLK - PORTE_PCR3 = enable ? PORT_CMD_DATA : 0; // SDHC_CMD - PORTE_PCR4 = enable ? PORT_CMD_DATA : 0; // SDHC_D3 - PORTE_PCR5 = enable ? PORT_CMD_DATA : 0; // SDHC_D2 -} -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ static void enableDmaIrs() { m_dmaBusy = true; m_irqstat = 0; } -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ static void initSDHC() { -#ifdef HAS_KINETIS_MPU - // Allow SDHC Bus Master access. - MPU_RGDAAC0 |= 0x0C000000; -#endif - // Enable SDHC clock. - SIM_SCGC3 |= SIM_SCGC3_SDHC; + initClock(); // Disable GPIO clock. enableGPIO(false); +#if defined (__IMXRT1062__) + SDHC_MIX_CTRL |= 0x80000000; +#endif // (__IMXRT1062__) + // Reset SDHC. Use default Water Mark Level of 16. - SDHC_SYSCTL = SDHC_SYSCTL_RSTA; + SDHC_SYSCTL |= SDHC_SYSCTL_RSTA | SDHC_SYSCTL_SDCLKFS(0x80); + while (SDHC_SYSCTL & SDHC_SYSCTL_RSTA) { } + // Set initial SCK rate. - setSdclk(400); + setSdclk(SD_MAX_INIT_RATE_KHZ); enableGPIO(true); // Enable desired IRQSTAT bits. SDHC_IRQSTATEN = SDHC_IRQSTATEN_MASK; + attachInterruptVector(IRQ_SDHC, sdIrs); NVIC_SET_PRIORITY(IRQ_SDHC, 6*16); NVIC_ENABLE_IRQ(IRQ_SDHC); @@ -296,41 +462,45 @@ static void initSDHC() { while (SDHC_SYSCTL & SDHC_SYSCTL_INITA) { } } -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ +static uint32_t statusCMD13() { + return cardCommand(CMD13_XFERTYP, m_rca) ? SDHC_CMDRSP0 : 0; +} +//------------------------------------------------------------------------------ static bool isBusyCMD13() { - if (!cardCommand(CMD13_XFERTYP, m_rca)) { - // Caller will timeout. - return true; - } - return !(SDHC_CMDRSP0 & CARD_STATUS_READY_FOR_DATA); + return !(statusCMD13() & CARD_STATUS_READY_FOR_DATA); } -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ static bool isBusyCommandComplete() { - return !(SDHC_IRQSTAT &(SDHC_IRQSTAT_CC | SDHC_IRQSTAT_CMD_ERROR)); + return !(SDHC_IRQSTAT & (SDHC_IRQSTAT_CC | SDHC_IRQSTAT_CMD_ERROR)); } -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ static bool isBusyCommandInhibit() { return SDHC_PRSSTAT & SDHC_PRSSTAT_CIHB; } -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ +static bool isBusyDat() { + return SDHC_PRSSTAT & (1 << 24) ? false : true; +} +//------------------------------------------------------------------------------ static bool isBusyDMA() { return m_dmaBusy; } -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ static bool isBusyFifoRead() { return !(SDHC_PRSSTAT & SDHC_PRSSTAT_BREN); } -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ static bool isBusyFifoWrite() { return !(SDHC_PRSSTAT & SDHC_PRSSTAT_BWEN); } -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ static bool isBusyTransferComplete() { return !(SDHC_IRQSTAT & (SDHC_IRQSTAT_TC | SDHC_IRQSTAT_ERROR)); } -//----------------------------------------------------------------------------- -static bool rdWrBlocks(uint32_t xfertyp, - uint32_t lba, uint8_t* buf, size_t n) { +//------------------------------------------------------------------------------ +static bool rdWrSectors(uint32_t xfertyp, + uint32_t sector, uint8_t* buf, size_t n) { if ((3 & (uint32_t)buf) || n == 0) { return sdError(SD_CARD_ERROR_DMA); } @@ -339,14 +509,14 @@ static bool rdWrBlocks(uint32_t xfertyp, } enableDmaIrs(); SDHC_DSADDR = (uint32_t)buf; - SDHC_CMDARG = m_highCapacity ? lba : 512*lba; SDHC_BLKATTR = SDHC_BLKATTR_BLKCNT(n) | SDHC_BLKATTR_BLKSIZE(512); SDHC_IRQSIGEN = SDHC_IRQSIGEN_MASK; - SDHC_XFERTYP = xfertyp; - + if (!cardCommand(xfertyp, m_highCapacity ? sector : 512*sector)) { + return false; + } return waitDmaStatus(); } -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // Read 16 byte CID or CSD register. static bool readReg16(uint32_t xfertyp, void* data) { uint8_t* d = reinterpret_cast(data); @@ -360,26 +530,28 @@ static bool readReg16(uint32_t xfertyp, void* data) { d[15] = 0; return true; } -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ static void setSdclk(uint32_t kHzMax) { const uint32_t DVS_LIMIT = 0X10; const uint32_t SDCLKFS_LIMIT = 0X100; uint32_t dvs = 1; uint32_t sdclkfs = 1; uint32_t maxSdclk = 1000*kHzMax; + uint32_t base = baseClock(); - while ((F_CPU/(sdclkfs*DVS_LIMIT) > maxSdclk) && (sdclkfs < SDCLKFS_LIMIT)) { + while ((base/(sdclkfs*DVS_LIMIT) > maxSdclk) && (sdclkfs < SDCLKFS_LIMIT)) { sdclkfs <<= 1; } - while ((F_CPU/(sdclkfs*dvs) > maxSdclk) && (dvs < DVS_LIMIT)) { + while ((base/(sdclkfs*dvs) > maxSdclk) && (dvs < DVS_LIMIT)) { dvs++; } - m_sdClkKhz = F_CPU/(1000*sdclkfs*dvs); + m_sdClkKhz = base/(1000*sdclkfs*dvs); sdclkfs >>= 1; dvs--; - +#if defined(__MK64FX512__) || defined(__MK66FX1M0__) // Disable SDHC clock. SDHC_SYSCTL &= ~SDHC_SYSCTL_SDCLKEN; +#endif // defined(__MK64FX512__) || defined(__MK66FX1M0__) // Change dividers. uint32_t sysctl = SDHC_SYSCTL & ~(SDHC_SYSCTL_DTOCV_MASK @@ -391,33 +563,36 @@ static void setSdclk(uint32_t kHzMax) { // Wait until the SDHC clock is stable. while (!(SDHC_PRSSTAT & SDHC_PRSSTAT_SDSTB)) { } + +#if defined(__MK64FX512__) || defined(__MK66FX1M0__) // Enable the SDHC clock. SDHC_SYSCTL |= SDHC_SYSCTL_SDCLKEN; +#endif // defined(__MK64FX512__) || defined(__MK66FX1M0__) } -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ static bool transferStop() { - DBG_IRQSTAT(); - + // This fix allows CDIHB to be cleared in Tennsy 3.x without a reset. + SDHC_PROCTL &= ~SDHC_PROCTL_SABGREQ; if (!cardCommand(CMD12_XFERTYP, 0)) { return sdError(SD_CARD_ERROR_CMD12); } - if (yieldTimeout(isBusyCMD13)) { + if (yieldTimeout(isBusyDat)) { return sdError(SD_CARD_ERROR_CMD13); } - // Save registers before reset DAT lines. - uint32_t irqsststen = SDHC_IRQSTATEN; - uint32_t proctl = SDHC_PROCTL & ~SDHC_PROCTL_SABGREQ; - - // Do reset to clear CDIHB. Should be a better way! - SDHC_SYSCTL |= SDHC_SYSCTL_RSTD; - - // Restore registers. - SDHC_IRQSTATEN = irqsststen; - SDHC_PROCTL = proctl; - + if (SDHC_PRSSTAT & SDHC_PRSSTAT_CDIHB) { + // This should not happen after above fix. + // Save registers before reset DAT lines. + uint32_t irqsststen = SDHC_IRQSTATEN; + uint32_t proctl = SDHC_PROCTL & ~SDHC_PROCTL_SABGREQ; + // Do reset to clear CDIHB. Should be a better way! + SDHC_SYSCTL |= SDHC_SYSCTL_RSTD; + // Restore registers. + SDHC_IRQSTATEN = irqsststen; + SDHC_PROCTL = proctl; + } return true; } -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // Return true if timeout occurs. static bool yieldTimeout(bool (*fcn)()) { m_busyFcn = fcn; @@ -432,14 +607,14 @@ static bool yieldTimeout(bool (*fcn)()) { m_busyFcn = 0; return false; // Caller will set errorCode. } -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ static bool waitDmaStatus() { if (yieldTimeout(isBusyDMA)) { return false; // Caller will set errorCode. } return (m_irqstat & SDHC_IRQSTAT_TC) && !(m_irqstat & SDHC_IRQSTAT_ERROR); } -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // Return true if timeout occurs. static bool waitTimeout(bool (*fcn)()) { uint32_t m = micros(); @@ -450,10 +625,28 @@ static bool waitTimeout(bool (*fcn)()) { } return false; // Caller will set errorCode. } -//============================================================================= -bool SdioCard::begin() { +//------------------------------------------------------------------------------ +static bool waitTransferComplete() { + if (!m_transferActive) { + return true; + } + bool timeOut = waitTimeout(isBusyTransferComplete); + m_transferActive = false; + m_irqstat = SDHC_IRQSTAT; + SDHC_IRQSTAT = m_irqstat; + if (timeOut || (m_irqstat & SDHC_IRQSTAT_ERROR)) { + return sdError(SD_CARD_ERROR_TRANSFER_COMPLETE); + } + return true; +} +//============================================================================== +// Start of SdioCard member functions. +//============================================================================== +bool SdioCard::begin(SdioConfig sdioConfig) { uint32_t kHzSdClk; uint32_t arg; + m_sdioConfig = sdioConfig; + m_curState = IDLE_STATE; m_initDone = false; m_errorCode = SD_CARD_ERROR_NONE; m_highCapacity = false; @@ -461,7 +654,6 @@ bool SdioCard::begin() { // initialize controller. initSDHC(); - if (!cardCommand(CMD0_XFERTYP, 0)) { return sdError(SD_CARD_ERROR_CMD0); } @@ -474,16 +666,18 @@ bool SdioCard::begin() { m_version2 = true; break; } + SDHC_SYSCTL |= SDHC_SYSCTL_RSTA; + while (SDHC_SYSCTL & SDHC_SYSCTL_RSTA) {} } + // Must support 3.2-3.4 Volts arg = m_version2 ? 0X40300000 : 0x00300000; - uint32_t m = micros(); + int m = micros(); do { if (!cardAcmd(0, ACMD41_XFERTYP, arg) || ((micros() - m) > BUSY_TIMEOUT_MICROS)) { return sdError(SD_CARD_ERROR_ACMD41); } } while ((SDHC_CMDRSP0 & 0x80000000) == 0); - m_ocr = SDHC_CMDRSP0; if (SDHC_CMDRSP0 & 0x40000000) { // Is high capacity. @@ -516,48 +710,70 @@ bool SdioCard::begin() { SDHC_WML = SDHC_WML_RDWML(FIFO_WML) | SDHC_WML_WRWML(FIFO_WML); + if (!cardACMD51(&m_scr)) { + return false; + } // Determine if High Speed mode is supported and set frequency. + // Check status[16] for error 0XF or status[16] for new mode 0X1. uint8_t status[64]; - if (cardCMD6(0X00FFFFFF, status) && (2 & status[13]) && + if (m_scr.sdSpec() > 0 && + cardCMD6(0X00FFFFFF, status) && (2 & status[13]) && cardCMD6(0X80FFFFF1, status) && (status[16] & 0XF) == 1) { kHzSdClk = 50000; } else { kHzSdClk = 25000; } - // disable GPIO + // Disable GPIO. enableGPIO(false); // Set the SDHC SCK frequency. setSdclk(kHzSdClk); - // enable GPIO + // Enable GPIO. enableGPIO(true); m_initDone = true; return true; } -//----------------------------------------------------------------------------- -uint32_t SdioCard::cardSize() { - return sdCardCapacity(&m_csd); +//------------------------------------------------------------------------------ +bool SdioCard::cardCMD6(uint32_t arg, uint8_t* status) { + // CMD6 returns 64 bytes. + if (waitTimeout(isBusyCMD13)) { + return sdError(SD_CARD_ERROR_CMD13); + } + enableDmaIrs(); + SDHC_DSADDR = (uint32_t)status; + SDHC_BLKATTR = SDHC_BLKATTR_BLKCNT(1) | SDHC_BLKATTR_BLKSIZE(64); + SDHC_IRQSIGEN = SDHC_IRQSIGEN_MASK; + if (!cardCommand(CMD6_XFERTYP, arg)) { + return sdError(SD_CARD_ERROR_CMD6); + } + if (!waitDmaStatus()) { + return sdError(SD_CARD_ERROR_DMA); + } + return true; } -//----------------------------------------------------------------------------- -bool SdioCard::erase(uint32_t firstBlock, uint32_t lastBlock) { - // check for single block erase - if (!m_csd.v1.erase_blk_en) { +//------------------------------------------------------------------------------ +bool SdioCard::erase(uint32_t firstSector, uint32_t lastSector) { + if (m_curState != IDLE_STATE && !syncDevice()) { + return false; + } + // check for single sector erase + if (!m_csd.eraseSingleBlock()) { // erase size mask - uint8_t m = (m_csd.v1.sector_size_high << 1) | m_csd.v1.sector_size_low; - if ((firstBlock & m) != 0 || ((lastBlock + 1) & m) != 0) { + uint8_t m = m_csd.eraseSize() - 1; + if ((firstSector & m) != 0 || ((lastSector + 1) & m) != 0) { // error card can't erase specified area - return sdError(SD_CARD_ERROR_ERASE_SINGLE_BLOCK); + return sdError(SD_CARD_ERROR_ERASE_SINGLE_SECTOR); } } if (!m_highCapacity) { - firstBlock <<= 9; - lastBlock <<= 9; + firstSector <<= 9; + lastSector <<= 9; } - if (!cardCommand(CMD32_XFERTYP, firstBlock)) { + if (!cardCommand(CMD32_XFERTYP, firstSector)) { return sdError(SD_CARD_ERROR_CMD32); } - if (!cardCommand(CMD33_XFERTYP, lastBlock)) { + if (!cardCommand(CMD33_XFERTYP, lastSector)) { return sdError(SD_CARD_ERROR_CMD33); } if (!cardCommand(CMD38_XFERTYP, 0)) { @@ -568,81 +784,67 @@ bool SdioCard::erase(uint32_t firstBlock, uint32_t lastBlock) { } return true; } -//----------------------------------------------------------------------------- -uint8_t SdioCard::errorCode() { +//------------------------------------------------------------------------------ +uint8_t SdioCard::errorCode() const { return m_errorCode; } -//----------------------------------------------------------------------------- -uint32_t SdioCard::errorData() { +//------------------------------------------------------------------------------ +uint32_t SdioCard::errorData() const { return m_irqstat; } -//----------------------------------------------------------------------------- -uint32_t SdioCard::errorLine() { +//------------------------------------------------------------------------------ +uint32_t SdioCard::errorLine() const { return m_errorLine; } -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ bool SdioCard::isBusy() { - return m_busyFcn ? m_busyFcn() : m_initDone && isBusyCMD13(); -} -//----------------------------------------------------------------------------- -uint32_t SdioCard::kHzSdClk() { - return m_sdClkKhz; -} -//----------------------------------------------------------------------------- -bool SdioCard::readBlock(uint32_t lba, uint8_t* buf) { - uint8_t aligned[512]; - - uint8_t* ptr = (uint32_t)buf & 3 ? aligned : buf; - - if (!rdWrBlocks(CMD17_DMA_XFERTYP, lba, ptr, 1)) { - return sdError(SD_CARD_ERROR_CMD18); - } - if (ptr != buf) { - memcpy(buf, aligned, 512); - } - return true; -} -//----------------------------------------------------------------------------- -bool SdioCard::readBlocks(uint32_t lba, uint8_t* buf, size_t n) { - if ((uint32_t)buf & 3) { - for (size_t i = 0; i < n; i++, lba++, buf += 512) { - if (!readBlock(lba, buf)) { - return false; // readBlock will set errorCode. + if (m_sdioConfig.useDma()) { + return m_busyFcn ? m_busyFcn() : m_initDone && isBusyCMD13(); + } else { + if (m_transferActive) { + if (isBusyTransferComplete()) { + return true; } +#if defined(__MK64FX512__) || defined(__MK66FX1M0__) + if ((SDHC_BLKATTR & 0XFFFF0000) != 0) { + return false; + } + m_transferActive = false; + stopTransmission(false); + return true; +#else // defined(__MK64FX512__) || defined(__MK66FX1M0__) + return false; +#endif // defined(__MK64FX512__) || defined(__MK66FX1M0__) } - return true; - } - if (!rdWrBlocks(CMD18_DMA_XFERTYP, lba, buf, n)) { - return sdError(SD_CARD_ERROR_CMD18); + // Use DAT0 low as busy. + return SDHC_PRSSTAT & (1 << 24) ? false : true; } - return true; } -//----------------------------------------------------------------------------- -bool SdioCard::readCID(void* cid) { +//------------------------------------------------------------------------------ +uint32_t SdioCard::kHzSdClk() { + return m_sdClkKhz; +} +//------------------------------------------------------------------------------ +bool SdioCard::readCID(cid_t* cid) { memcpy(cid, &m_cid, 16); return true; } -//----------------------------------------------------------------------------- -bool SdioCard::readCSD(void* csd) { +//------------------------------------------------------------------------------ +bool SdioCard::readCSD(csd_t* csd) { memcpy(csd, &m_csd, 16); return true; } -//----------------------------------------------------------------------------- -bool SdioCard::readData(uint8_t *dst) { +//------------------------------------------------------------------------------ +bool SdioCard::readData(uint8_t* dst) { DBG_IRQSTAT(); - uint32_t *p32 = reinterpret_cast(dst); + uint32_t* p32 = reinterpret_cast(dst); if (!(SDHC_PRSSTAT & SDHC_PRSSTAT_RTA)) { SDHC_PROCTL &= ~SDHC_PROCTL_SABGREQ; - if ((SDHC_BLKATTR & 0XFFFF0000) == 0X10000) { - // Don't stop at block gap if last block. Allows auto CMD12. - SDHC_PROCTL |= SDHC_PROCTL_CREQ; - } else { - noInterrupts(); - SDHC_PROCTL |= SDHC_PROCTL_CREQ; - SDHC_PROCTL |= SDHC_PROCTL_SABGREQ; - interrupts(); - } + noInterrupts(); + SDHC_PROCTL |= SDHC_PROCTL_CREQ; + SDHC_PROCTL |= SDHC_PROCTL_SABGREQ; + interrupts(); } if (waitTimeout(isBusyFifoRead)) { return sdError(SD_CARD_ERROR_READ_FIFO); @@ -662,94 +864,155 @@ bool SdioCard::readData(uint8_t *dst) { SDHC_IRQSTAT = m_irqstat; return (m_irqstat & SDHC_IRQSTAT_TC) && !(m_irqstat & SDHC_IRQSTAT_ERROR); } -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ bool SdioCard::readOCR(uint32_t* ocr) { *ocr = m_ocr; return true; } -//----------------------------------------------------------------------------- -bool SdioCard::readStart(uint32_t lba) { - // K66/K65 Errata - SDHC: Does not support Infinite Block Transfer Mode. - return sdError(SD_CARD_ERROR_FUNCTION_NOT_SUPPORTED); +//------------------------------------------------------------------------------ +bool SdioCard::readSCR(scr_t* scr) { + memcpy(scr, &m_scr, 8); + return true; } -//----------------------------------------------------------------------------- -// SDHC will do Auto CMD12 after count blocks. -bool SdioCard::readStart(uint32_t lba, uint32_t count) { - DBG_IRQSTAT(); - if (count > 0XFFFF) { - return sdError(SD_CARD_ERROR_READ_START); +//------------------------------------------------------------------------------ +bool SdioCard::readSector(uint32_t sector, uint8_t* dst) { + if (m_sdioConfig.useDma()) { + uint8_t aligned[512]; + + uint8_t* ptr = (uint32_t)dst & 3 ? aligned : dst; + + if (!rdWrSectors(CMD17_DMA_XFERTYP, sector, ptr, 1)) { + return sdError(SD_CARD_ERROR_CMD17); + } + if (ptr != dst) { + memcpy(dst, aligned, 512); + } + } else { + if (!waitTransferComplete()) { + return false; + } + if (m_curState != READ_STATE || sector != m_curSector) { + if (!syncDevice()) { + return false; + } + if (!readStart(sector)) { + return false; + } + m_curSector = sector; + m_curState = READ_STATE; + } + if (!readData(dst)) { + return false; + } +#if defined(__MK64FX512__) || defined(__MK66FX1M0__) + if ((SDHC_BLKATTR & 0XFFFF0000) == 0) { + if (!syncDevice()) { + return false; + } + } +#endif // defined(__MK64FX512__) || defined(__MK66FX1M0__) + m_curSector++; + } + return true; +} +//------------------------------------------------------------------------------ +bool SdioCard::readSectors(uint32_t sector, uint8_t* dst, size_t n) { + if (m_sdioConfig.useDma()) { + if ((uint32_t)dst & 3) { + for (size_t i = 0; i < n; i++, sector++, dst += 512) { + if (!readSector(sector, dst)) { + return false; // readSector will set errorCode. + } + } + return true; + } + if (!rdWrSectors(CMD18_DMA_XFERTYP, sector, dst, n)) { + return sdError(SD_CARD_ERROR_CMD18); + } + } else { + for (size_t i = 0; i < n; i++) { + if (!readSector(sector + i, dst + i*512UL)) { + return false; + } + } } + return true; +} +//------------------------------------------------------------------------------ +// SDHC will do Auto CMD12 after count sectors. +bool SdioCard::readStart(uint32_t sector) { + DBG_IRQSTAT(); if (yieldTimeout(isBusyCMD13)) { return sdError(SD_CARD_ERROR_CMD13); } - if (count > 1) { - SDHC_PROCTL |= SDHC_PROCTL_SABGREQ; - } - SDHC_BLKATTR = SDHC_BLKATTR_BLKCNT(count) | SDHC_BLKATTR_BLKSIZE(512); - if (!cardCommand(CMD18_PGM_XFERTYP, m_highCapacity ? lba : 512*lba)) { + SDHC_PROCTL |= SDHC_PROCTL_SABGREQ; +#if defined(__IMXRT1062__) + // Infinite transfer. + SDHC_BLKATTR = SDHC_BLKATTR_BLKSIZE(512); +#else // defined(__IMXRT1062__) + // Errata - can't do infinite transfer. + SDHC_BLKATTR = SDHC_BLKATTR_BLKCNT(MAX_BLKCNT) | SDHC_BLKATTR_BLKSIZE(512); +#endif // defined(__IMXRT1062__) + + if (!cardCommand(CMD18_PGM_XFERTYP, m_highCapacity ? sector : 512*sector)) { return sdError(SD_CARD_ERROR_CMD18); } return true; } -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ bool SdioCard::readStop() { return transferStop(); } -//----------------------------------------------------------------------------- -bool SdioCard::syncBlocks() { - return true; +//------------------------------------------------------------------------------ +uint32_t SdioCard::sectorCount() { + return m_csd.capacity(); } -//----------------------------------------------------------------------------- -uint8_t SdioCard::type() { - return m_version2 ? m_highCapacity ? - SD_CARD_TYPE_SDHC : SD_CARD_TYPE_SD2 : SD_CARD_TYPE_SD1; +//------------------------------------------------------------------------------ +uint32_t SdioCard::status() { + return statusCMD13(); } -//----------------------------------------------------------------------------- -bool SdioCard::writeBlock(uint32_t lba, const uint8_t* buf) { - uint8_t *ptr; - uint8_t aligned[512]; - if (3 & (uint32_t)buf) { - ptr = aligned; - memcpy(aligned, buf, 512); - } else { - ptr = const_cast(buf); +//------------------------------------------------------------------------------ +bool SdioCard::stopTransmission(bool blocking) { + m_curState = IDLE_STATE; + // This fix allows CDIHB to be cleared in Tennsy 3.x without a reset. + SDHC_PROCTL &= ~SDHC_PROCTL_SABGREQ; + if (!cardCommand(CMD12_XFERTYP, 0)) { + return sdError(SD_CARD_ERROR_CMD12); } - if (!rdWrBlocks(CMD24_DMA_XFERTYP, lba, ptr, 1)) { - return sdError(SD_CARD_ERROR_CMD24); + if (blocking) { + if (yieldTimeout(isBusyDat)) { + return sdError(SD_CARD_ERROR_CMD13); + } } return true; } -//----------------------------------------------------------------------------- -bool SdioCard::writeBlocks(uint32_t lba, const uint8_t* buf, size_t n) { - uint8_t* ptr = const_cast(buf); - if (3 & (uint32_t)ptr) { - for (size_t i = 0; i < n; i++, lba++, ptr += 512) { - if (!writeBlock(lba, ptr)) { - return false; // writeBlock will set errorCode. - } - } - return true; +//------------------------------------------------------------------------------ +bool SdioCard::syncDevice() { + if (!waitTransferComplete()) { + return false; } - if (!rdWrBlocks(CMD25_DMA_XFERTYP, lba, ptr, n)) { - return sdError(SD_CARD_ERROR_CMD25); + if (m_curState != IDLE_STATE) { + return stopTransmission(true); } return true; } -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ +uint8_t SdioCard::type() const { + return m_version2 ? m_highCapacity ? + SD_CARD_TYPE_SDHC : SD_CARD_TYPE_SD2 : SD_CARD_TYPE_SD1; +} +//------------------------------------------------------------------------------ bool SdioCard::writeData(const uint8_t* src) { DBG_IRQSTAT(); + if (!waitTransferComplete()) { + return false; + } const uint32_t* p32 = reinterpret_cast(src); - if (!(SDHC_PRSSTAT & SDHC_PRSSTAT_WTA)) { SDHC_PROCTL &= ~SDHC_PROCTL_SABGREQ; - // Don't stop at block gap if last block. Allows auto CMD12. - if ((SDHC_BLKATTR & 0XFFFF0000) == 0X10000) { - SDHC_PROCTL |= SDHC_PROCTL_CREQ; - } else { - SDHC_PROCTL |= SDHC_PROCTL_CREQ; - SDHC_PROCTL |= SDHC_PROCTL_SABGREQ; - } + SDHC_PROCTL |= SDHC_PROCTL_CREQ; } + SDHC_PROCTL |= SDHC_PROCTL_SABGREQ; if (waitTimeout(isBusyFifoWrite)) { return sdError(SD_CARD_ERROR_WRITE_FIFO); } @@ -761,40 +1024,97 @@ bool SdioCard::writeData(const uint8_t* src) { } p32 += FIFO_WML; } - if (waitTimeout(isBusyTransferComplete)) { - return sdError(SD_CARD_ERROR_WRITE_TIMEOUT); - } - m_irqstat = SDHC_IRQSTAT; - SDHC_IRQSTAT = m_irqstat; - return (m_irqstat & SDHC_IRQSTAT_TC) && !(m_irqstat & SDHC_IRQSTAT_ERROR); + m_transferActive = true; + return true; } -//----------------------------------------------------------------------------- -bool SdioCard::writeStart(uint32_t lba) { - // K66/K65 Errata - SDHC: Does not support Infinite Block Transfer Mode. - return sdError(SD_CARD_ERROR_FUNCTION_NOT_SUPPORTED); +//------------------------------------------------------------------------------ +bool SdioCard::writeSector(uint32_t sector, const uint8_t* src) { + if (m_sdioConfig.useDma()) { + uint8_t* ptr; + uint8_t aligned[512]; + if (3 & (uint32_t)src) { + ptr = aligned; + memcpy(aligned, src, 512); + } else { + ptr = const_cast(src); + } + if (!rdWrSectors(CMD24_DMA_XFERTYP, sector, ptr, 1)) { + return sdError(SD_CARD_ERROR_CMD24); + } + } else { + if (!waitTransferComplete()) { + return false; + } +#if defined(__MK64FX512__) || defined(__MK66FX1M0__) + // End transfer with CMD12 if required. + if ((SDHC_BLKATTR & 0XFFFF0000) == 0) { + if (!syncDevice()) { + return false; + } + } +#endif // defined(__MK64FX512__) || defined(__MK66FX1M0__) + if (m_curState != WRITE_STATE || m_curSector != sector) { + if (!syncDevice()) { + return false; + } + if (!writeStart(sector )) { + return false; + } + m_curSector = sector; + m_curState = WRITE_STATE; + } + if (!writeData(src)) { + return false; + } + m_curSector++; + } + return true; } -//----------------------------------------------------------------------------- -// SDHC will do Auto CMD12 after count blocks. -bool SdioCard::writeStart(uint32_t lba, uint32_t count) { - if (count > 0XFFFF) { - return sdError(SD_CARD_ERROR_WRITE_START); +//------------------------------------------------------------------------------ +bool SdioCard::writeSectors(uint32_t sector, const uint8_t* src, size_t n) { + if (m_sdioConfig.useDma()) { + uint8_t* ptr = const_cast(src); + if (3 & (uint32_t)ptr) { + for (size_t i = 0; i < n; i++, sector++, ptr += 512) { + if (!writeSector(sector, ptr)) { + return false; // writeSector will set errorCode. + } + } + return true; + } + if (!rdWrSectors(CMD25_DMA_XFERTYP, sector, ptr, n)) { + return sdError(SD_CARD_ERROR_CMD25); + } + } else { + for (size_t i = 0; i < n; i++) { + if (!writeSector(sector + i, src + i*512UL)) { + return false; + } + } } - DBG_IRQSTAT(); + return true; +} +//------------------------------------------------------------------------------ +bool SdioCard::writeStart(uint32_t sector) { if (yieldTimeout(isBusyCMD13)) { return sdError(SD_CARD_ERROR_CMD13); } - if (count > 1) { - SDHC_PROCTL |= SDHC_PROCTL_SABGREQ; - } - SDHC_BLKATTR = SDHC_BLKATTR_BLKCNT(count) | SDHC_BLKATTR_BLKSIZE(512); - - if (!cardCommand(CMD25_PGM_XFERTYP, m_highCapacity ? lba : 512*lba)) { + SDHC_PROCTL &= ~SDHC_PROCTL_SABGREQ; + +#if defined(__IMXRT1062__) + // Infinite transfer. + SDHC_BLKATTR = SDHC_BLKATTR_BLKSIZE(512); +#else // defined(__IMXRT1062__) + // Errata - can't do infinite transfer. + SDHC_BLKATTR = SDHC_BLKATTR_BLKCNT(MAX_BLKCNT) | SDHC_BLKATTR_BLKSIZE(512); +#endif // defined(__IMXRT1062__) + if (!cardCommand(CMD25_PGM_XFERTYP, m_highCapacity ? sector : 512*sector)) { return sdError(SD_CARD_ERROR_CMD25); } return true; } -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ bool SdioCard::writeStop() { return transferStop(); } -#endif // defined(__MK64FX512__) || defined(__MK66FX1M0__) +#endif // defined(__MK64FX512__) defined(__MK66FX1M0__) defined(__IMXRT1062__) diff --git a/avr/cores/megacommand/SdFat/SdCard/SdioTeensy.h b/avr/cores/megacommand/SdFat/SdCard/SdioTeensy.h new file mode 100644 index 000000000..69573dc11 --- /dev/null +++ b/avr/cores/megacommand/SdFat/SdCard/SdioTeensy.h @@ -0,0 +1,277 @@ +#ifndef SdioTeensy_h +#define SdioTeensy_h + +// From Paul's SD.h driver. + +#if defined(__IMXRT1062__) +#define MAKE_REG_MASK(m,s) (((uint32_t)(((uint32_t)(m) << s)))) +#define MAKE_REG_GET(x,m,s) (((uint32_t)(((uint32_t)(x)>>s) & m))) +#define MAKE_REG_SET(x,m,s) (((uint32_t)(((uint32_t)(x) & m) << s))) + +#define SDHC_BLKATTR_BLKSIZE_MASK MAKE_REG_MASK(0x1FFF,0) //uint32_t)(((n) & 0x1FFF)<<0) // Transfer Block Size Mask +#define SDHC_BLKATTR_BLKSIZE(n) MAKE_REG_SET(n,0x1FFF,0) //uint32_t)(((n) & 0x1FFF)<<0) // Transfer Block Size +#define SDHC_BLKATTR_BLKCNT_MASK MAKE_REG_MASK(0x1FFF,16) //((uint32_t)0x1FFF<<16) +#define SDHC_BLKATTR_BLKCNT(n) MAKE_REG_SET(n,0x1FFF,16) //(uint32_t)(((n) & 0x1FFF)<<16) // Blocks Count For Current Transfer + +#define SDHC_XFERTYP_CMDINX(n) MAKE_REG_SET(n,0x3F,24) //(uint32_t)(((n) & 0x3F)<<24)// Command Index +#define SDHC_XFERTYP_CMDTYP(n) MAKE_REG_SET(n,0x3,22) //(uint32_t)(((n) & 0x3)<<22) // Command Type +#define SDHC_XFERTYP_DPSEL MAKE_REG_MASK(0x1,21) //((uint32_t)0x00200000) // Data Present Select +#define SDHC_XFERTYP_CICEN MAKE_REG_MASK(0x1,20) //((uint32_t)0x00100000) // Command Index Check Enable +#define SDHC_XFERTYP_CCCEN MAKE_REG_MASK(0x1,19) //((uint32_t)0x00080000) // Command CRC Check Enable +#define SDHC_XFERTYP_RSPTYP(n) MAKE_REG_SET(n,0x3,16) //(uint32_t)(((n) & 0x3)<<16) // Response Type Select +#define SDHC_XFERTYP_MSBSEL MAKE_REG_MASK(0x1,5) //((uint32_t)0x00000020) // Multi/Single Block Select +#define SDHC_XFERTYP_DTDSEL MAKE_REG_MASK(0x1,4) //((uint32_t)0x00000010) // Data Transfer Direction Select +#define SDHC_XFERTYP_AC12EN MAKE_REG_MASK(0x1,2) //((uint32_t)0x00000004) // Auto CMD12 Enable +#define SDHC_XFERTYP_BCEN MAKE_REG_MASK(0x1,1) //((uint32_t)0x00000002) // Block Count Enable +#define SDHC_XFERTYP_DMAEN MAKE_REG_MASK(0x3,0) //((uint32_t)0x00000001) // DMA Enable + +#define SDHC_PRSSTAT_DLSL_MASK MAKE_REG_MASK(0xFF,24) //((uint32_t)0xFF000000) // DAT Line Signal Level +#define SDHC_PRSSTAT_CLSL MAKE_REG_MASK(0x1,23) //((uint32_t)0x00800000) // CMD Line Signal Level +#define SDHC_PRSSTAT_WPSPL MAKE_REG_MASK(0x1,19) // +#define SDHC_PRSSTAT_CDPL MAKE_REG_MASK(0x1,18) // +#define SDHC_PRSSTAT_CINS MAKE_REG_MASK(0x1,16) //((uint32_t)0x00010000) // Card Inserted +#define SDHC_PRSSTAT_TSCD MAKE_REG_MASK(0x1,15) +#define SDHC_PRSSTAT_RTR MAKE_REG_MASK(0x1,12) +#define SDHC_PRSSTAT_BREN MAKE_REG_MASK(0x1,11) //((uint32_t)0x00000800) // Buffer Read Enable +#define SDHC_PRSSTAT_BWEN MAKE_REG_MASK(0x1,10) //((uint32_t)0x00000400) // Buffer Write Enable +#define SDHC_PRSSTAT_RTA MAKE_REG_MASK(0x1,9) //((uint32_t)0x00000200) // Read Transfer Active +#define SDHC_PRSSTAT_WTA MAKE_REG_MASK(0x1,8) //((uint32_t)0x00000100) // Write Transfer Active +#define SDHC_PRSSTAT_SDOFF MAKE_REG_MASK(0x1,7) //((uint32_t)0x00000080) // SD Clock Gated Off Internally +#define SDHC_PRSSTAT_PEROFF MAKE_REG_MASK(0x1,6) //((uint32_t)0x00000040) // SDHC clock Gated Off Internally +#define SDHC_PRSSTAT_HCKOFF MAKE_REG_MASK(0x1,5) //((uint32_t)0x00000020) // System Clock Gated Off Internally +#define SDHC_PRSSTAT_IPGOFF MAKE_REG_MASK(0x1,4) //((uint32_t)0x00000010) // Bus Clock Gated Off Internally +#define SDHC_PRSSTAT_SDSTB MAKE_REG_MASK(0x1,3) //((uint32_t)0x00000008) // SD Clock Stable +#define SDHC_PRSSTAT_DLA MAKE_REG_MASK(0x1,2) //((uint32_t)0x00000004) // Data Line Active +#define SDHC_PRSSTAT_CDIHB MAKE_REG_MASK(0x1,1) //((uint32_t)0x00000002) // Command Inhibit (DAT) +#define SDHC_PRSSTAT_CIHB MAKE_REG_MASK(0x1,0) //((uint32_t)0x00000001) // Command Inhibit (CMD) + +#define SDHC_PROTCT_NONEXACT_BLKRD MAKE_REG_MASK(0x1,30) // +#define SDHC_PROTCT_BURST_LENEN(n) MAKE_REG_SET(n,0x7,12) // +#define SDHC_PROCTL_WECRM MAKE_REG_MASK(0x1,26) //((uint32_t)0x04000000) // Wakeup Event Enable On SD Card Removal +#define SDHC_PROCTL_WECINS MAKE_REG_MASK(0x1,25) //((uint32_t)0x02000000) // Wakeup Event Enable On SD Card Insertion +#define SDHC_PROCTL_WECINT MAKE_REG_MASK(0x1,24) //((uint32_t)0x01000000) // Wakeup Event Enable On Card Interrupt +#define SDHC_PROCTL_RD_DONE_NOBLK MAKE_REG_MASK(0x1,20) // +#define SDHC_PROCTL_IABG MAKE_REG_MASK(0x1,19) //((uint32_t)0x00080000) // Interrupt At Block Gap +#define SDHC_PROCTL_RWCTL MAKE_REG_MASK(0x1,18) //((uint32_t)0x00040000) // Read Wait Control +#define SDHC_PROCTL_CREQ MAKE_REG_MASK(0x1,17) //((uint32_t)0x00020000) // Continue Request +#define SDHC_PROCTL_SABGREQ MAKE_REG_MASK(0x1,16) //((uint32_t)0x00010000) // Stop At Block Gap Request +#define SDHC_PROCTL_DMAS(n) MAKE_REG_SET(n,0x3,8) //(uint32_t)(((n) & 0x3)<<8) // DMA Select +#define SDHC_PROCTL_CDSS MAKE_REG_MASK(0x1,7) //((uint32_t)0x00000080) // Card Detect Signal Selection +#define SDHC_PROCTL_CDTL MAKE_REG_MASK(0x1,6) //((uint32_t)0x00000040) // Card Detect Test Level +#define SDHC_PROCTL_EMODE(n) MAKE_REG_SET(n,0x3,4) //(uint32_t)(((n) & 0x3)<<4) // Endian Mode +#define SDHC_PROCTL_EMODE_MASK MAKE_REG_MASK(0x3,4) //(uint32_t)((0x3)<<4) // Endian Mode +#define SDHC_PROCTL_D3CD MAKE_REG_MASK(0x1,3) //((uint32_t)0x00000008) // DAT3 As Card Detection Pin +#define SDHC_PROCTL_DTW(n) MAKE_REG_SET(n,0x3,1) //(uint32_t)(((n) & 0x3)<<1) // Data Transfer Width, 0=1bit, 1=4bit, 2=8bit +#define SDHC_PROCTL_DTW_MASK MAKE_REG_MASK(0x3,1) //((uint32_t)0x00000006) +#define SDHC_PROCTL_LCTL MAKE_REG_MASK(0x1,0) //((uint32_t)0x00000001) // LED Control + +#define SDHC_SYSCTL_RSTT MAKE_REG_MASK(0x1,28) // +#define SDHC_SYSCTL_INITA MAKE_REG_MASK(0x1,27) //((uint32_t)0x08000000) // Initialization Active +#define SDHC_SYSCTL_RSTD MAKE_REG_MASK(0x1,26) //((uint32_t)0x04000000) // Software Reset For DAT Line +#define SDHC_SYSCTL_RSTC MAKE_REG_MASK(0x1,25) //((uint32_t)0x02000000) // Software Reset For CMD Line +#define SDHC_SYSCTL_RSTA MAKE_REG_MASK(0x1,24) //((uint32_t)0x01000000) // Software Reset For ALL +#define SDHC_SYSCTL_DTOCV(n) MAKE_REG_SET(n,0xF,16) //(uint32_t)(((n) & 0xF)<<16) // Data Timeout Counter Value +#define SDHC_SYSCTL_DTOCV_MASK MAKE_REG_MASK(0xF,16) //((uint32_t)0x000F0000) +#define SDHC_SYSCTL_SDCLKFS(n) MAKE_REG_SET(n,0xFF,8) //(uint32_t)(((n) & 0xFF)<<8) // SDCLK Frequency Select +#define SDHC_SYSCTL_SDCLKFS_MASK MAKE_REG_MASK(0xFF,8) //((uint32_t)0x0000FF00) +#define SDHC_SYSCTL_DVS(n) MAKE_REG_SET(n,0xF,4) //(uint32_t)(((n) & 0xF)<<4) // Divisor +#define SDHC_SYSCTL_DVS_MASK MAKE_REG_MASK(0xF,4) //((uint32_t)0x000000F0) + +#define SDHC_SYSCTL_SDCLKEN ((uint32_t)0x00000008) // SD Clock Enable +#define SDHC_SYSCTL_PEREN ((uint32_t)0x00000004) // Peripheral Clock Enable +#define SDHC_SYSCTL_HCKEN ((uint32_t)0x00000002) // System Clock Enable +#define SDHC_SYSCTL_IPGEN ((uint32_t)0x00000001) // IPG Clock Enable + +#define SDHC_IRQSTAT_DMAE MAKE_REG_MASK(0x1,28) //((uint32_t)0x10000000) // DMA Error +#define SDHC_IRQSTAT_TNE MAKE_REG_MASK(0x1,26) // +#define SDHC_IRQSTAT_AC12E MAKE_REG_MASK(0x1,24) //((uint32_t)0x01000000) // Auto CMD12 Error +#define SDHC_IRQSTAT_DEBE MAKE_REG_MASK(0x1,22) //((uint32_t)0x00400000) // Data End Bit Error +#define SDHC_IRQSTAT_DCE MAKE_REG_MASK(0x1,21) //((uint32_t)0x00200000) // Data CRC Error +#define SDHC_IRQSTAT_DTOE MAKE_REG_MASK(0x1,20) //((uint32_t)0x00100000) // Data Timeout Error +#define SDHC_IRQSTAT_CIE MAKE_REG_MASK(0x1,19) //((uint32_t)0x00080000) // Command Index Error +#define SDHC_IRQSTAT_CEBE MAKE_REG_MASK(0x1,18) //((uint32_t)0x00040000) // Command End Bit Error +#define SDHC_IRQSTAT_CCE MAKE_REG_MASK(0x1,17) //((uint32_t)0x00020000) // Command CRC Error +#define SDHC_IRQSTAT_CTOE MAKE_REG_MASK(0x1,16) //((uint32_t)0x00010000) // Command Timeout Error +#define SDHC_IRQSTAT_TP MAKE_REG_MASK(0x1,14) // +#define SDHC_IRQSTAT_RTE MAKE_REG_MASK(0x1,12) // +#define SDHC_IRQSTAT_CINT MAKE_REG_MASK(0x1,8) //((uint32_t)0x00000100) // Card Interrupt +#define SDHC_IRQSTAT_CRM MAKE_REG_MASK(0x1,7) //((uint32_t)0x00000080) // Card Removal +#define SDHC_IRQSTAT_CINS MAKE_REG_MASK(0x1,6) //((uint32_t)0x00000040) // Card Insertion +#define SDHC_IRQSTAT_BRR MAKE_REG_MASK(0x1,5) //((uint32_t)0x00000020) // Buffer Read Ready +#define SDHC_IRQSTAT_BWR MAKE_REG_MASK(0x1,4) //((uint32_t)0x00000010) // Buffer Write Ready +#define SDHC_IRQSTAT_DINT MAKE_REG_MASK(0x1,3) //((uint32_t)0x00000008) // DMA Interrupt +#define SDHC_IRQSTAT_BGE MAKE_REG_MASK(0x1,2) //((uint32_t)0x00000004) // Block Gap Event +#define SDHC_IRQSTAT_TC MAKE_REG_MASK(0x1,1) //((uint32_t)0x00000002) // Transfer Complete +#define SDHC_IRQSTAT_CC MAKE_REG_MASK(0x1,0) //((uint32_t)0x00000001) // Command Complete + +#define SDHC_IRQSTATEN_DMAESEN MAKE_REG_MASK(0x1,28) //((uint32_t)0x10000000) // DMA Error Status Enable +#define SDHC_IRQSTATEN_TNESEN MAKE_REG_MASK(0x1,26) // +#define SDHC_IRQSTATEN_AC12ESEN MAKE_REG_MASK(0x1,24) //((uint32_t)0x01000000) // Auto CMD12 Error Status Enable +#define SDHC_IRQSTATEN_DEBESEN MAKE_REG_MASK(0x1,22) //((uint32_t)0x00400000) // Data End Bit Error Status Enable +#define SDHC_IRQSTATEN_DCESEN MAKE_REG_MASK(0x1,21) //((uint32_t)0x00200000) // Data CRC Error Status Enable +#define SDHC_IRQSTATEN_DTOESEN MAKE_REG_MASK(0x1,20) //((uint32_t)0x00100000) // Data Timeout Error Status Enable +#define SDHC_IRQSTATEN_CIESEN MAKE_REG_MASK(0x1,19) //((uint32_t)0x00080000) // Command Index Error Status Enable +#define SDHC_IRQSTATEN_CEBESEN MAKE_REG_MASK(0x1,18) //((uint32_t)0x00040000) // Command End Bit Error Status Enable +#define SDHC_IRQSTATEN_CCESEN MAKE_REG_MASK(0x1,17) //((uint32_t)0x00020000) // Command CRC Error Status Enable +#define SDHC_IRQSTATEN_CTOESEN MAKE_REG_MASK(0x1,16) //((uint32_t)0x00010000) // Command Timeout Error Status Enable +#define SDHC_IRQSTATEN_TPSEN MAKE_REG_MASK(0x1,14) // +#define SDHC_IRQSTATEN_RTESEN MAKE_REG_MASK(0x1,12) // +#define SDHC_IRQSTATEN_CINTSEN MAKE_REG_MASK(0x1,8) //((uint32_t)0x00000100) // Card Interrupt Status Enable +#define SDHC_IRQSTATEN_CRMSEN MAKE_REG_MASK(0x1,7) //((uint32_t)0x00000080) // Card Removal Status Enable +#define SDHC_IRQSTATEN_CINSEN MAKE_REG_MASK(0x1,6) //((uint32_t)0x00000040) // Card Insertion Status Enable +#define SDHC_IRQSTATEN_BRRSEN MAKE_REG_MASK(0x1,5) //((uint32_t)0x00000020) // Buffer Read Ready Status Enable +#define SDHC_IRQSTATEN_BWRSEN MAKE_REG_MASK(0x1,4) //((uint32_t)0x00000010) // Buffer Write Ready Status Enable +#define SDHC_IRQSTATEN_DINTSEN MAKE_REG_MASK(0x1,3) //((uint32_t)0x00000008) // DMA Interrupt Status Enable +#define SDHC_IRQSTATEN_BGESEN MAKE_REG_MASK(0x1,2) //((uint32_t)0x00000004) // Block Gap Event Status Enable +#define SDHC_IRQSTATEN_TCSEN MAKE_REG_MASK(0x1,1) //((uint32_t)0x00000002) // Transfer Complete Status Enable +#define SDHC_IRQSTATEN_CCSEN MAKE_REG_MASK(0x1,0) //((uint32_t)0x00000001) // Command Complete Status Enable + +#define SDHC_IRQSIGEN_DMAEIEN MAKE_REG_MASK(0x1,28) //((uint32_t)0x10000000) // DMA Error Interrupt Enable +#define SDHC_IRQSIGEN_TNEIEN MAKE_REG_MASK(0x1,26) // +#define SDHC_IRQSIGEN_AC12EIEN MAKE_REG_MASK(0x1,24) //((uint32_t)0x01000000) // Auto CMD12 Error Interrupt Enable +#define SDHC_IRQSIGEN_DEBEIEN MAKE_REG_MASK(0x1,22) //((uint32_t)0x00400000) // Data End Bit Error Interrupt Enable +#define SDHC_IRQSIGEN_DCEIEN MAKE_REG_MASK(0x1,21) //((uint32_t)0x00200000) // Data CRC Error Interrupt Enable +#define SDHC_IRQSIGEN_DTOEIEN MAKE_REG_MASK(0x1,20) //((uint32_t)0x00100000) // Data Timeout Error Interrupt Enable +#define SDHC_IRQSIGEN_CIEIEN MAKE_REG_MASK(0x1,19) //((uint32_t)0x00080000) // Command Index Error Interrupt Enable +#define SDHC_IRQSIGEN_CEBEIEN MAKE_REG_MASK(0x1,18) //((uint32_t)0x00040000) // Command End Bit Error Interrupt Enable +#define SDHC_IRQSIGEN_CCEIEN MAKE_REG_MASK(0x1,17) //((uint32_t)0x00020000) // Command CRC Error Interrupt Enable +#define SDHC_IRQSIGEN_CTOEIEN MAKE_REG_MASK(0x1,16) //((uint32_t)0x00010000) // Command Timeout Error Interrupt Enable +#define SDHC_IRQSIGEN_TPIEN MAKE_REG_MASK(0x1,14) // +#define SDHC_IRQSIGEN_RTEIEN MAKE_REG_MASK(0x1,12) // +#define SDHC_IRQSIGEN_CINTIEN MAKE_REG_MASK(0x1,8) //((uint32_t)0x00000100) // Card Interrupt Interrupt Enable +#define SDHC_IRQSIGEN_CRMIEN MAKE_REG_MASK(0x1,7) //((uint32_t)0x00000080) // Card Removal Interrupt Enable +#define SDHC_IRQSIGEN_CINSIEN MAKE_REG_MASK(0x1,6) //((uint32_t)0x00000040) // Card Insertion Interrupt Enable +#define SDHC_IRQSIGEN_BRRIEN MAKE_REG_MASK(0x1,5) //((uint32_t)0x00000020) // Buffer Read Ready Interrupt Enable +#define SDHC_IRQSIGEN_BWRIEN MAKE_REG_MASK(0x1,4) //((uint32_t)0x00000010) // Buffer Write Ready Interrupt Enable +#define SDHC_IRQSIGEN_DINTIEN MAKE_REG_MASK(0x1,3) //((uint32_t)0x00000008) // DMA Interrupt Interrupt Enable +#define SDHC_IRQSIGEN_BGEIEN MAKE_REG_MASK(0x1,2) //((uint32_t)0x00000004) // Block Gap Event Interrupt Enable +#define SDHC_IRQSIGEN_TCIEN MAKE_REG_MASK(0x1,1) //((uint32_t)0x00000002) // Transfer Complete Interrupt Enable +#define SDHC_IRQSIGEN_CCIEN MAKE_REG_MASK(0x1,0) //((uint32_t)0x00000001) // Command Complete Interrupt Enable + +#define SDHC_AC12ERR_SMPLCLK_SEL MAKE_REG_MASK(0x1,23) // +#define SDHC_AC12ERR_EXEC_TUNING MAKE_REG_MASK(0x1,22) // +#define SDHC_AC12ERR_CNIBAC12E MAKE_REG_MASK(0x1,7) //((uint32_t)0x00000080) // Command Not Issued By Auto CMD12 Error +#define SDHC_AC12ERR_AC12IE MAKE_REG_MASK(0x1,4) //((uint32_t)0x00000010) // Auto CMD12 Index Error +#define SDHC_AC12ERR_AC12CE MAKE_REG_MASK(0x1,3) //((uint32_t)0x00000008) // Auto CMD12 CRC Error +#define SDHC_AC12ERR_AC12EBE MAKE_REG_MASK(0x1,2) //((uint32_t)0x00000004) // Auto CMD12 End Bit Error +#define SDHC_AC12ERR_AC12TOE MAKE_REG_MASK(0x1,1) //((uint32_t)0x00000002) // Auto CMD12 Timeout Error +#define SDHC_AC12ERR_AC12NE MAKE_REG_MASK(0x1,0) //((uint32_t)0x00000001) // Auto CMD12 Not Executed + +#define SDHC_HTCAPBLT_VS18 MAKE_REG_MASK(0x1,26) // +#define SDHC_HTCAPBLT_VS30 MAKE_REG_MASK(0x1,25) // +#define SDHC_HTCAPBLT_VS33 MAKE_REG_MASK(0x1,24) // +#define SDHC_HTCAPBLT_SRS MAKE_REG_MASK(0x1,23) // +#define SDHC_HTCAPBLT_DMAS MAKE_REG_MASK(0x1,22) // +#define SDHC_HTCAPBLT_HSS MAKE_REG_MASK(0x1,21) // +#define SDHC_HTCAPBLT_ADMAS MAKE_REG_MASK(0x1,20) // +#define SDHC_HTCAPBLT_MBL_VAL MAKE_REG_GET((USDHC1_HOST_CTRL_CAP),0x7,16) // +#define SDHC_HTCAPBLT_RETUN_MODE MAKE_REG_GET((USDHC1_HOST_CTRL_CAP),0x3,14) // +#define SDHC_HTCAPBLT_TUNE_SDR50 MAKE_REG_MASK(0x1,13) // +#define SDHC_HTCAPBLT_TIME_RETUN(n) MAKE_REG_SET(n,0xF,8) // + +#define SDHC_WML_WR_BRSTLEN_MASK MAKE_REG_MASK(0x1F,24) // +#define SDHC_WML_RD_BRSTLEN_MASK MAKE_REG_MASK(0x1F,8) // +#define SDHC_WML_WR_WML_MASK MAKE_REG_MASK(0xFF,16) // +#define SDHC_WML_RD_WML_MASK MAKE_REG_MASK(0xFF,0) // +#define SDHC_WML_WR_BRSTLEN(n) MAKE_REG_SET(n,0x1F,24) //(uint32_t)(((n) & 0x7F)<<16) // Write Burst Len +#define SDHC_WML_RD_BRSTLEN(n) MAKE_REG_SET(n,0x1F,8) //(uint32_t)(((n) & 0x7F)<<0) // Read Burst Len +#define SDHC_WML_WR_WML(n) MAKE_REG_SET(n,0xFF,16) //(uint32_t)(((n) & 0x7F)<<16) // Write Watermark Level +#define SDHC_WML_RD_WML(n) MAKE_REG_SET(n,0xFF,0) //(uint32_t)(((n) & 0x7F)<<0) // Read Watermark Level +#define SDHC_WML_WRWML(n) MAKE_REG_SET(n,0xFF,16) //(uint32_t)(((n) & 0x7F)<<16) // Write Watermark Level +#define SDHC_WML_RDWML(n) MAKE_REG_SET(n,0xFF,0) //(uint32_t)(((n) & 0x7F)<<0) // Read Watermark Level + +// Teensy 4.0 only +#define SDHC_MIX_CTRL_DMAEN MAKE_REG_MASK(0x1,0) // +#define SDHC_MIX_CTRL_BCEN MAKE_REG_MASK(0x1,1) // +#define SDHC_MIX_CTRL_AC12EN MAKE_REG_MASK(0x1,2) // +#define SDHC_MIX_CTRL_DDR_EN MAKE_REG_MASK(0x1,3) // +#define SDHC_MIX_CTRL_DTDSEL MAKE_REG_MASK(0x1,4) // +#define SDHC_MIX_CTRL_MSBSEL MAKE_REG_MASK(0x1,5) // +#define SDHC_MIX_CTRL_NIBBLE_POS MAKE_REG_MASK(0x1,6) // +#define SDHC_MIX_CTRL_AC23EN MAKE_REG_MASK(0x1,7) // + +#define SDHC_FEVT_CINT MAKE_REG_MASK(0x1,31) //((uint32_t)0x80000000) // Force Event Card Interrupt +#define SDHC_FEVT_DMAE MAKE_REG_MASK(0x1,28) //((uint32_t)0x10000000) // Force Event DMA Error +#define SDHC_FEVT_AC12E MAKE_REG_MASK(0x1,24) //((uint32_t)0x01000000) // Force Event Auto CMD12 Error +#define SDHC_FEVT_DEBE MAKE_REG_MASK(0x1,22) //((uint32_t)0x00400000) // Force Event Data End Bit Error +#define SDHC_FEVT_DCE MAKE_REG_MASK(0x1,21) //((uint32_t)0x00200000) // Force Event Data CRC Error +#define SDHC_FEVT_DTOE MAKE_REG_MASK(0x1,20) //((uint32_t)0x00100000) // Force Event Data Timeout Error +#define SDHC_FEVT_CIE MAKE_REG_MASK(0x1,19) //((uint32_t)0x00080000) // Force Event Command Index Error +#define SDHC_FEVT_CEBE MAKE_REG_MASK(0x1,18) //((uint32_t)0x00040000) // Force Event Command End Bit Error +#define SDHC_FEVT_CCE MAKE_REG_MASK(0x1,17) //((uint32_t)0x00020000) // Force Event Command CRC Error +#define SDHC_FEVT_CTOE MAKE_REG_MASK(0x1,16) //((uint32_t)0x00010000) // Force Event Command Timeout Error +#define SDHC_FEVT_CNIBAC12E MAKE_REG_MASK(0x1,7) //((uint32_t)0x00000080) // Force Event Command Not Executed By Auto Command 12 Error +#define SDHC_FEVT_AC12IE MAKE_REG_MASK(0x1,4) //((uint32_t)0x00000010) // Force Event Auto Command 12 Index Error +#define SDHC_FEVT_AC12EBE MAKE_REG_MASK(0x1,3) //((uint32_t)0x00000008) // Force Event Auto Command 12 End Bit Error +#define SDHC_FEVT_AC12CE MAKE_REG_MASK(0x1,2) //((uint32_t)0x00000004) // Force Event Auto Command 12 CRC Error +#define SDHC_FEVT_AC12TOE MAKE_REG_MASK(0x1,1) //((uint32_t)0x00000002) // Force Event Auto Command 12 Time Out Error +#define SDHC_FEVT_AC12NE MAKE_REG_MASK(0x1,0) //((uint32_t)0x00000001) // Force Event Auto Command 12 Not Executed + +#define SDHC_ADMAES_ADMADCE MAKE_REG_MASK(0x1,3) //((uint32_t)0x00000008) +#define SDHC_ADMAES_ADMALME MAKE_REG_MASK(0x1,2) //((uint32_t)0x00000004) +#define SDHC_ADMAES_ADMAES_MASK MAKE_REG_MASK(0x3,0) //((uint32_t)0x00000003) + +#define SDHC_MMCBOOT_BOOTBLKCNT(n) MAKE_REG_MASK(0xFF,16) //(uint32_t)(((n) & 0xFFF)<<16) // stop at block gap value of automatic mode +#define SDHC_MMCBOOT_AUTOSABGEN MAKE_REG_MASK(0x1,7) //((uint32_t)0x00000080) // enable auto stop at block gap function +#define SDHC_MMCBOOT_BOOTEN MAKE_REG_MASK(0x1,6) //((uint32_t)0x00000040) // Boot Mode Enable +#define SDHC_MMCBOOT_BOOTMODE MAKE_REG_MASK(0x1,5) //((uint32_t)0x00000020) // Boot Mode Select +#define SDHC_MMCBOOT_BOOTACK MAKE_REG_MASK(0x1,4) //((uint32_t)0x00000010) // Boot Ack Mode Select +#define SDHC_MMCBOOT_DTOCVACK(n) MAKE_REG_MASK(0xF,0) //(uint32_t)(((n) & 0xF)<<0) // Boot ACK Time Out Counter Value +//#define SDHC_HOSTVER (*(volatile uint32_t*)0x400B10FC) // Host Controller Version + +#define CCM_ANALOG_PFD_528_PFD0_FRAC_MASK 0x3f +#define CCM_ANALOG_PFD_528_PFD0_FRAC(n) ((n) & CCM_ANALOG_PFD_528_PFD0_FRAC_MASK) +#define CCM_ANALOG_PFD_528_PFD1_FRAC_MASK (0x3f<<8) +#define CCM_ANALOG_PFD_528_PFD1_FRAC(n) (((n)<<8) & CCM_ANALOG_PFD_528_PFD1_FRAC_MASK) +#define CCM_ANALOG_PFD_528_PFD2_FRAC_MASK (0x3f<<16) +#define CCM_ANALOG_PFD_528_PFD2_FRAC(n) (((n)<<16) & CCM_ANALOG_PFD_528_PFD2_FRAC_MASK) +#define CCM_ANALOG_PFD_528_PFD3_FRAC_MASK ((0x3f<<24) +#define CCM_ANALOG_PFD_528_PFD3_FRAC(n) (((n)<<24) & CCM_ANALOG_PFD_528_PFD3_FRAC_MASK) + +#define SDHC_DSADDR (USDHC1_DS_ADDR ) // DMA System Address register +#define SDHC_BLKATTR (USDHC1_BLK_ATT) // Block Attributes register +#define SDHC_CMDARG (USDHC1_CMD_ARG) // Command Argument register +#define SDHC_XFERTYP (USDHC1_CMD_XFR_TYP) // Transfer Type register +#define SDHC_CMDRSP0 (USDHC1_CMD_RSP0) // Command Response 0 +#define SDHC_CMDRSP1 (USDHC1_CMD_RSP1) // Command Response 1 +#define SDHC_CMDRSP2 (USDHC1_CMD_RSP2) // Command Response 2 +#define SDHC_CMDRSP3 (USDHC1_CMD_RSP3) // Command Response 3 +#define SDHC_DATPORT (USDHC1_DATA_BUFF_ACC_PORT) // Buffer Data Port register +#define SDHC_PRSSTAT (USDHC1_PRES_STATE) // Present State register +#define SDHC_PROCTL (USDHC1_PROT_CTRL) // Protocol Control register +#define SDHC_SYSCTL (USDHC1_SYS_CTRL) // System Control register +#define SDHC_IRQSTAT (USDHC1_INT_STATUS) // Interrupt Status register +#define SDHC_IRQSTATEN (USDHC1_INT_STATUS_EN) // Interrupt Status Enable register +#define SDHC_IRQSIGEN (USDHC1_INT_SIGNAL_EN) // Interrupt Signal Enable register +#define SDHC_AC12ERR (USDHC1_AUTOCMD12_ERR_STATUS) // Auto CMD12 Error Status Register +#define SDHC_HTCAPBLT (USDHC1_HOST_CTRL_CAP) // Host Controller Capabilities +#define SDHC_WML (USDHC1_WTMK_LVL) // Watermark Level Register +#define SDHC_MIX_CTRL (USDHC1_MIX_CTRL) // Mixer Control +#define SDHC_FEVT (USDHC1_FORCE_EVENT) // Force Event register +#define SDHC_ADMAES (USDHC1_ADMA_ERR_STATUS) // ADMA Error Status register +#define SDHC_ADSADDR (USDHC1_ADMA_SYS_ADDR) // ADMA System Addressregister +#define SDHC_VENDOR (USDHC1_VEND_SPEC) // Vendor Specific register +#define SDHC_MMCBOOT (USDHC1_MMC_BOOT) // MMC Boot register +#define SDHC_VENDOR2 (USDHC2_VEND_SPEC2) // Vendor Specific2 register +// +#define IRQ_SDHC IRQ_SDHC1 + +#define SDHC_MAX_DVS (0xF + 1U) +#define SDHC_MAX_CLKFS (0xFF + 1U) +#define SDHC_PREV_DVS(x) ((x) -= 1U) +#define SDHC_PREV_CLKFS(x, y) ((x) >>= (y)) + +#define CCM_CSCDR1_USDHC1_CLK_PODF_MASK (0x7<<11) +#define CCM_CSCDR1_USDHC1_CLK_PODF(n) (((n)&0x7)<<11) + +#define IOMUXC_SW_PAD_CTL_PAD_SRE ((0x1<)<0) +#define IOMUXC_SW_PAD_CTL_PAD_PKE ((0x1)<<12) +#define IOMUXC_SW_PAD_CTL_PAD_PUE ((0x1)<<13) +#define IOMUXC_SW_PAD_CTL_PAD_HYS ((0x1)<<16) +#define IOMUXC_SW_PAD_CTL_PAD_SPEED(n) (((n)&0x3)<<6) +#define IOMUXC_SW_PAD_CTL_PAD_PUS(n) (((n)&0x3)<<14) +#define IOMUXC_SW_PAD_CTL_PAD_PUS_MASK ((0x3)<<14) +#define IOMUXC_SW_PAD_CTL_PAD_DSE(n) (((n)&0x7)<<3) +#define IOMUXC_SW_PAD_CTL_PAD_DSE_MASK ((0x7)<<3) +#endif // defined(__IMXRT1062__) +#endif // SdioTeensy_h \ No newline at end of file diff --git a/avr/cores/megacommand/SdFat/SdFat.h b/avr/cores/megacommand/SdFat/SdFat.h index 99c783190..6348214e7 100644 --- a/avr/cores/megacommand/SdFat/SdFat.h +++ b/avr/cores/megacommand/SdFat/SdFat.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 20011-2017 Bill Greiman + * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -26,484 +26,489 @@ #define SdFat_h /** * \file - * \brief SdFat class + * \brief main SdFs include file. */ -#include "SysCall.h" -#include "BlockDriver.h" +#include "common/SysCall.h" +#include "SdCard/SdCard.h" +#include "ExFatLib/ExFatLib.h" #include "FatLib/FatLib.h" -#include "SdCard/SdioCard.h" +#include "FsLib/FsLib.h" +#if INCLUDE_SDIOS +#include "sdios.h" +#endif // INCLUDE_SDIOS //------------------------------------------------------------------------------ -/** SdFat version */ -#define SD_FAT_VERSION "1.0.5" +/** SdFat version for cpp use. */ +#define SD_FAT_VERSION 20200 +/** SdFat version as string. */ +#define SD_FAT_VERSION_STR "2.2.0" //============================================================================== /** - * \class SdBaseFile - * \brief Class for backward compatibility. + * \class SdBase + * \brief base SD file system template class. */ -class SdBaseFile : public FatFile { +template +class SdBase : public Vol { public: - SdBaseFile() {} - /** Create a file object and open it in the current working directory. - * - * \param[in] path A path for a file to be opened. + //---------------------------------------------------------------------------- + /** Initialize SD card and file system. * - * \param[in] oflag Values for \a oflag are constructed by a - * bitwise-inclusive OR of open flags. see - * FatFile::open(FatFile*, const char*, uint8_t). + * \param[in] csPin SD card chip select pin. + * \return true for success or false for failure. */ - SdBaseFile(const char* path, uint8_t oflag) : FatFile(path, oflag) {} -}; -//----------------------------------------------------------------------------- -#if ENABLE_ARDUINO_FEATURES -/** - * \class SdFile - * \brief Class for backward compatibility. - */ -class SdFile : public PrintFile { - public: - SdFile() {} - /** Create a file object and open it in the current working directory. - * - * \param[in] path A path for a file to be opened. + bool begin(SdCsPin_t csPin = SS) { +#ifdef BUILTIN_SDCARD + if (csPin == BUILTIN_SDCARD) { + return begin(SdioConfig(FIFO_SDIO)); + } +#endif // BUILTIN_SDCARD + return begin(SdSpiConfig(csPin, SHARED_SPI)); + } + //---------------------------------------------------------------------------- + /** Initialize SD card and file system. * - * \param[in] oflag Values for \a oflag are constructed by a - * bitwise-inclusive OR of open flags. see - * FatFile::open(FatFile*, const char*, uint8_t). - */ - SdFile(const char* path, uint8_t oflag) : PrintFile(path, oflag) {} -}; -#endif // #if ENABLE_ARDUINO_FEATURES -//----------------------------------------------------------------------------- -/** - * \class SdFileSystem - * \brief Virtual base class for %SdFat library. - */ -template -class SdFileSystem : public FatFileSystem { - public: - /** Initialize file system. - * \return true for success else false. + * \param[in] csPin SD card chip select pin. + * \param[in] maxSck Maximum SCK frequency. + * \return true for success or false for failure. */ - bool begin() { - return FatFileSystem::begin(&m_card); - } - /** \return Pointer to SD card object */ - SdDriverClass *card() { - m_card.syncBlocks(); - return &m_card; - } - /** %Print any SD error code to Serial and halt. */ - void errorHalt() { - errorHalt(&Serial); + bool begin(SdCsPin_t csPin, uint32_t maxSck) { + return begin(SdSpiConfig(csPin, SHARED_SPI, maxSck)); } - /** %Print any SD error code and halt. + //---------------------------------------------------------------------------- + /** Initialize SD card and file system for SPI mode. * - * \param[in] pr Print destination. + * \param[in] spiConfig SPI configuration. + * \return true for success or false for failure. */ - void errorHalt(Print* pr) { - errorPrint(pr); - SysCall::halt(); + bool begin(SdSpiConfig spiConfig) { + return cardBegin(spiConfig) && Vol::begin(m_card); } - /** %Print msg, any SD error code and halt. + //--------------------------------------------------------------------------- + /** Initialize SD card and file system for SDIO mode. * - * \param[in] msg Message to print. + * \param[in] sdioConfig SDIO configuration. + * \return true for success or false for failure. */ - void errorHalt(char const* msg) { - errorHalt(&Serial, msg); + bool begin(SdioConfig sdioConfig) { + return cardBegin(sdioConfig) && Vol::begin(m_card); } - /** %Print msg, any SD error code, and halt. + //---------------------------------------------------------------------------- + /** \return Pointer to SD card object. */ + SdCard* card() {return m_card;} + //---------------------------------------------------------------------------- + /** Initialize SD card in SPI mode. * - * \param[in] pr Print destination. - * \param[in] msg Message to print. + * \param[in] spiConfig SPI configuration. + * \return true for success or false for failure. */ - void errorHalt(Print* pr, char const* msg) { - errorPrint(pr, msg); - SysCall::halt(); + bool cardBegin(SdSpiConfig spiConfig) { + m_card = m_cardFactory.newCard(spiConfig); + return m_card && !m_card->errorCode(); } - /** %Print any SD error code to Serial */ - void errorPrint() { - errorPrint(&Serial); + //---------------------------------------------------------------------------- + /** Initialize SD card in SDIO mode. + * + * \param[in] sdioConfig SDIO configuration. + * \return true for success or false for failure. + */ + bool cardBegin(SdioConfig sdioConfig) { + m_card = m_cardFactory.newCard(sdioConfig); + return m_card && !m_card->errorCode(); + } + //---------------------------------------------------------------------------- + /** End use of card. */ + void end() { + Vol::end(); + if (m_card) { + m_card->end(); + } } - /** %Print any SD error code. - * \param[in] pr Print device. + //---------------------------------------------------------------------------- + /** %Print error info and halt. + * + * \param[in] pr Print destination. */ - void errorPrint(Print* pr) { - if (!cardErrorCode()) { - return; + void errorHalt(print_t* pr) { + if (sdErrorCode()) { + pr->print(F("SdError: 0X")); + pr->print(sdErrorCode(), HEX); + pr->print(F(",0X")); + pr->println(sdErrorData(), HEX); + } else if (!Vol::fatType()) { + pr->println(F("Check SD format.")); } - pr->print(F("SD errorCode: 0X")); - pr->print(cardErrorCode(), HEX); - pr->print(F(",0X")); - pr->println(cardErrorData(), HEX); + while (true) {} } - /** %Print msg, any SD error code. + //---------------------------------------------------------------------------- + /** %Print error info and halt. * + * \param[in] pr Print destination. * \param[in] msg Message to print. */ - void errorPrint(const char* msg) { - errorPrint(&Serial, msg); + void errorHalt(print_t* pr, const char* msg) { + pr->print(F("error: ")); + pr->println(msg); + errorHalt(pr); } - /** %Print msg, any SD error code. + //---------------------------------------------------------------------------- + /** %Print msg and halt. * * \param[in] pr Print destination. * \param[in] msg Message to print. */ - void errorPrint(Print* pr, char const* msg) { + void errorHalt(print_t* pr, const __FlashStringHelper* msg) { pr->print(F("error: ")); pr->println(msg); - errorPrint(pr); + errorHalt(pr); } - /** %Print any SD error code and halt. */ - void initErrorHalt() { - initErrorHalt(&Serial); + //---------------------------------------------------------------------------- + /** Format SD card + * + * \param[in] pr Print destination. + * \return true for success else false. + */ + bool format(print_t* pr = nullptr) { + Fmt fmt; + uint8_t* mem = Vol::end(); + if (!mem) { + return false; + } + bool switchSpi = hasDedicatedSpi() && !isDedicatedSpi(); + if (switchSpi && !setDedicatedSpi(true)) { + return false; + } + bool rtn = fmt.format(card(), mem, pr); + if (switchSpi && !setDedicatedSpi(false)) { + return false; + } + return rtn; + } + //---------------------------------------------------------------------------- + /** \return the free cluster count. */ + uint32_t freeClusterCount() { + bool switchSpi = hasDedicatedSpi() && !isDedicatedSpi(); + if (switchSpi && !setDedicatedSpi(true)) { + return 0; + } + uint32_t rtn = Vol::freeClusterCount(); + if (switchSpi && !setDedicatedSpi(false)) { + return 0; + } + return rtn; } - /** %Print error details and halt after begin fails. + //---------------------------------------------------------------------------- + /** \return true if can be in dedicated SPI state */ + bool hasDedicatedSpi() {return m_card ? m_card->hasDedicatedSpi() : false;} + //---------------------------------------------------------------------------- + /** %Print error info and halt. * * \param[in] pr Print destination. */ - void initErrorHalt(Print* pr) { + void initErrorHalt(print_t* pr) { initErrorPrint(pr); - SysCall::halt(); + while (true) {} } - /**Print message, error details, and halt after begin() fails. + //---------------------------------------------------------------------------- + /** %Print error info and halt. * + * \param[in] pr Print destination. * \param[in] msg Message to print. */ - void initErrorHalt(char const *msg) { - initErrorHalt(&Serial, msg); + void initErrorHalt(print_t* pr, const char* msg) { + pr->println(msg); + initErrorHalt(pr); } - /**Print message, error details, and halt after begin() fails. - * \param[in] pr Print device. + //---------------------------------------------------------------------------- + /** %Print error info and halt. + * + * \param[in] pr Print destination. * \param[in] msg Message to print. */ - void initErrorHalt(Print* pr, char const *msg) { + void initErrorHalt(print_t* pr, const __FlashStringHelper* msg) { pr->println(msg); initErrorHalt(pr); } - - /** Print error details after begin() fails. */ - void initErrorPrint() { - initErrorPrint(&Serial); - } + //---------------------------------------------------------------------------- /** Print error details after begin() fails. * * \param[in] pr Print destination. */ - void initErrorPrint(Print* pr) { - if (cardErrorCode()) { - pr->println(F("Can't access SD card. Do not reformat.")); - if (cardErrorCode() == SD_CARD_ERROR_CMD0) { - pr->println(F("No card, wrong chip select pin, or SPI problem?")); + void initErrorPrint(print_t* pr) { + pr->println(F("begin() failed")); + if (sdErrorCode()) { + pr->println(F("Do not reformat the SD.")); + if (sdErrorCode() == SD_CARD_ERROR_CMD0) { + pr->println(F("No card, wrong chip select pin, or wiring error?")); } - errorPrint(pr); - } else if (vol()->fatType() == 0) { - pr->println(F("Invalid format, reformat SD.")); - } else if (!vwd()->isOpen()) { - pr->println(F("Can't open root directory.")); - } else { - pr->println(F("No error found.")); } + errorPrint(pr); } - /**Print message and error details and halt after begin() fails. - * - * \param[in] msg Message to print. - */ - void initErrorPrint(char const *msg) { - initErrorPrint(&Serial, msg); - } - /**Print message and error details and halt after begin() fails. + //---------------------------------------------------------------------------- + /** \return true if in dedicated SPI state. */ + bool isDedicatedSpi() {return m_card ? m_card->isDedicatedSpi() : false;} + //---------------------------------------------------------------------------- + /** %Print volume FAT/exFAT type. * * \param[in] pr Print destination. - * \param[in] msg Message to print. - */ - void initErrorPrint(Print* pr, char const *msg) { - pr->println(msg); - initErrorPrint(pr); - } -#if defined(ARDUINO) || defined(DOXYGEN) - /** %Print msg, any SD error code, and halt. - * - * \param[in] msg Message to print. */ - void errorHalt(const __FlashStringHelper* msg) { - errorHalt(&Serial, msg); + void printFatType(print_t* pr) { + if (Vol::fatType() == FAT_TYPE_EXFAT) { + pr->print(F("exFAT")); + } else { + pr->print(F("FAT")); + pr->print(Vol::fatType()); + } } - /** %Print msg, any SD error code, and halt. + //---------------------------------------------------------------------------- + /** %Print SD errorCode and errorData. * * \param[in] pr Print destination. - * \param[in] msg Message to print. */ - void errorHalt(Print* pr, const __FlashStringHelper* msg) { - errorPrint(pr, msg); - SysCall::halt(); + void errorPrint(print_t* pr) { + if (sdErrorCode()) { + pr->print(F("SdError: 0X")); + pr->print(sdErrorCode(), HEX); + pr->print(F(",0X")); + pr->println(sdErrorData(), HEX); + } else if (!Vol::fatType()) { + pr->println(F("Check SD format.")); + } } - + //---------------------------------------------------------------------------- /** %Print msg, any SD error code. * + * \param[in] pr Print destination. * \param[in] msg Message to print. */ - void errorPrint(const __FlashStringHelper* msg) { - errorPrint(&Serial, msg); + void errorPrint(print_t* pr, char const* msg) { + pr->print(F("error: ")); + pr->println(msg); + errorPrint(pr); } + /** %Print msg, any SD error code. * * \param[in] pr Print destination. * \param[in] msg Message to print. */ - void errorPrint(Print* pr, const __FlashStringHelper* msg) { + void errorPrint(print_t* pr, const __FlashStringHelper* msg) { pr->print(F("error: ")); pr->println(msg); errorPrint(pr); } - /**Print message, error details, and halt after begin() fails. - * - * \param[in] msg Message to print. - */ - void initErrorHalt(const __FlashStringHelper* msg) { - initErrorHalt(&Serial, msg); + //---------------------------------------------------------------------------- + /** %Print error info and return. + * + * \param[in] pr Print destination. + */ + void printSdError(print_t* pr) { + if (sdErrorCode()) { + if (sdErrorCode() == SD_CARD_ERROR_CMD0) { + pr->println(F("No card, wrong chip select pin, or wiring error?")); + } + pr->print(F("SD error: ")); + printSdErrorSymbol(pr, sdErrorCode()); + pr->print(F(" = 0x")); + pr->print(sdErrorCode(), HEX); + pr->print(F(",0x")); + pr->println(sdErrorData(), HEX); + } else if (!Vol::fatType()) { + pr->println(F("Check SD format.")); + } } - /**Print message, error details, and halt after begin() fails. - * \param[in] pr Print device for message. - * \param[in] msg Message to print. + //---------------------------------------------------------------------------- + /** \return SD card error code. */ + uint8_t sdErrorCode() { + if (m_card) { + return m_card->errorCode(); + } + return SD_CARD_ERROR_INVALID_CARD_CONFIG; + } + //---------------------------------------------------------------------------- + /** \return SD card error data. */ + uint8_t sdErrorData() {return m_card ? m_card->errorData() : 0;} + //---------------------------------------------------------------------------- + /** Set SPI sharing state + * \param[in] value desired state. + * \return true for success else false; + */ + bool setDedicatedSpi(bool value) { + if (m_card) { + return m_card->setDedicatedSpi(value); + } + return false; + } + //---------------------------------------------------------------------------- + /** \return pointer to base volume */ + Vol* vol() {return reinterpret_cast(this);} + //---------------------------------------------------------------------------- + /** Initialize file system after call to cardBegin. + * + * \return true for success or false for failure. */ - void initErrorHalt(Print* pr, const __FlashStringHelper* msg) { - pr->println(msg); - initErrorHalt(pr); + bool volumeBegin() { + return Vol::begin(m_card); } - /**Print message and error details and halt after begin() fails. +#if ENABLE_ARDUINO_SERIAL + /** Print error details after begin() fails. */ + void initErrorPrint() { + initErrorPrint(&Serial); + } + //---------------------------------------------------------------------------- + /** %Print msg to Serial and halt. * * \param[in] msg Message to print. */ - void initErrorPrint(const __FlashStringHelper* msg) { - initErrorPrint(&Serial, msg); + void errorHalt(const __FlashStringHelper* msg) { + errorHalt(&Serial, msg); } - /**Print message and error details and halt after begin() fails. + //---------------------------------------------------------------------------- + /** %Print error info to Serial and halt. */ + void errorHalt() {errorHalt(&Serial);} + //---------------------------------------------------------------------------- + /** %Print error info and halt. * - * \param[in] pr Print destination. * \param[in] msg Message to print. */ - void initErrorPrint(Print* pr, const __FlashStringHelper* msg) { - pr->println(msg); - initErrorPrint(pr); - } -#endif // defined(ARDUINO) || defined(DOXYGEN) - /** \return The card error code */ - uint8_t cardErrorCode() { - return m_card.errorCode(); - } - /** \return the card error data */ - uint32_t cardErrorData() { - return m_card.errorData(); - } - - protected: - SdDriverClass m_card; -}; -//============================================================================== -/** - * \class SdFat - * \brief Main file system class for %SdFat library. - */ -class SdFat : public SdFileSystem { - public: -#if IMPLEMENT_SPI_PORT_SELECTION || defined(DOXYGEN) - SdFat() { - m_spi.setPort(0); - } - /** Constructor with SPI port selection. - * \param[in] spiPort SPI port number. + void errorHalt(const char* msg) {errorHalt(&Serial, msg);} + //---------------------------------------------------------------------------- + /** %Print error info and halt. */ + void initErrorHalt() {initErrorHalt(&Serial);} + //---------------------------------------------------------------------------- + /** %Print msg, any SD error code. + * + * \param[in] msg Message to print. */ - explicit SdFat(uint8_t spiPort) { - m_spi.setPort(spiPort); - } -#endif // IMPLEMENT_SPI_PORT_SELECTION - /** Initialize SD card and file system. + void errorPrint(const char* msg) {errorPrint(&Serial, msg);} + /** %Print msg, any SD error code. * - * \param[in] csPin SD card chip select pin. - * \param[in] spiSettings SPI speed, mode, and bit order. - * \return true for success else false. + * \param[in] msg Message to print. */ - bool begin(uint8_t csPin = SS, SPISettings spiSettings = SPI_FULL_SPEED) { - return m_card.begin(&m_spi, csPin, spiSettings) && - SdFileSystem::begin(); - } - /** Initialize SD card for diagnostic use only. + void errorPrint(const __FlashStringHelper* msg) {errorPrint(&Serial, msg);} + //---------------------------------------------------------------------------- + /** %Print error info and halt. * - * \param[in] csPin SD card chip select pin. - * \param[in] settings SPI speed, mode, and bit order. - * \return true for success else false. + * \param[in] msg Message to print. */ - bool cardBegin(uint8_t csPin = SS, SPISettings settings = SPI_FULL_SPEED) { - return m_card.begin(&m_spi, csPin, settings); - } - /** Initialize file system for diagnostic use only. - * \return true for success else false. + void initErrorHalt(const char* msg) {initErrorHalt(&Serial, msg);} + //---------------------------------------------------------------------------- + /** %Print error info and halt. + * + * \param[in] msg Message to print. */ - bool fsBegin() { - return FatFileSystem::begin(card()); + void initErrorHalt(const __FlashStringHelper* msg) { + initErrorHalt(&Serial, msg); } - - private: - SdFatSpiDriver m_spi; +#endif // ENABLE_ARDUINO_SERIAL + //---------------------------------------------------------------------------- + public: + SdCard* m_card = nullptr; + SdCardFactory m_cardFactory; }; -//============================================================================== -#if ENABLE_SDIO_CLASS || defined(DOXYGEN) +//------------------------------------------------------------------------------ /** - * \class SdFatSdio - * \brief SdFat class using SDIO. + * \class SdFat32 + * \brief SD file system class for FAT volumes. */ -class SdFatSdio : public SdFileSystem { +class SdFat32 : public SdBase { public: - /** Initialize SD card and file system. - * \return true for success else false. - */ - bool begin() { - return m_card.begin() && SdFileSystem::begin(); - } - /** Initialize SD card for diagnostic use only. - * - * \return true for success else false. - */ - bool cardBegin() { - return m_card.begin(); - } - /** Initialize file system for diagnostic use only. - * \return true for success else false. - */ - bool fsBegin() { - return SdFileSystem::begin(); - } }; -#if ENABLE_SDIOEX_CLASS || defined(DOXYGEN) -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ /** - * \class SdFatSdioEX - * \brief SdFat class using SDIO. + * \class SdExFat + * \brief SD file system class for exFAT volumes. */ -class SdFatSdioEX : public SdFileSystem { +class SdExFat : public SdBase { public: - /** Initialize SD card and file system. - * \return true for success else false. - */ - bool begin() { - return m_card.begin() && SdFileSystem::begin(); - } - /** \return Pointer to SD card object */ - SdioCardEX* card() { - return &m_card; - } - /** Initialize SD card for diagnostic use only. - * - * \return true for success else false. - */ - bool cardBegin() { - return m_card.begin(); - } - /** Initialize file system for diagnostic use only. - * \return true for success else false. - */ - bool fsBegin() { - return SdFileSystem::begin(); - } }; -#endif // ENABLE_SDIOEX_CLASS || defined(DOXYGEN) -#endif // ENABLE_SDIO_CLASS || defined(DOXYGEN) -//============================================================================= -#if ENABLE_SOFTWARE_SPI_CLASS || defined(DOXYGEN) +//------------------------------------------------------------------------------ /** - * \class SdFatSoftSpi - * \brief SdFat class using software SPI. + * \class SdFs + * \brief SD file system class for FAT16, FAT32, and exFAT volumes. */ -template -class SdFatSoftSpi : public SdFileSystem { +class SdFs : public SdBase { public: - /** Initialize SD card and file system. - * - * \param[in] csPin SD card chip select pin. - * \param[in] spiSettings ignored for software SPI.. - * \return true for success else false. - */ - bool begin(uint8_t csPin = SS, SPISettings spiSettings = SPI_FULL_SPEED) { - return m_card.begin(&m_spi, csPin, spiSettings) && - SdFileSystem::begin(); - } - private: - SdSpiSoftDriver m_spi; }; -#endif // #if ENABLE_SOFTWARE_SPI_CLASS || defined(DOXYGEN) -//============================================================================== -#if ENABLE_EXTENDED_TRANSFER_CLASS || defined(DOXYGEN) +//------------------------------------------------------------------------------ +#if SDFAT_FILE_TYPE == 1 || defined(DOXYGEN) +/** Select type for SdFat. */ +typedef SdFat32 SdFat; +/** Select type for SdBaseFile. */ +typedef FatFile SdBaseFile; +#elif SDFAT_FILE_TYPE == 2 +typedef SdExFat SdFat; +typedef ExFatFile SdBaseFile; +#elif SDFAT_FILE_TYPE == 3 +typedef SdFs SdFat; +typedef FsBaseFile SdBaseFile; +#else // SDFAT_FILE_TYPE +#error Invalid SDFAT_FILE_TYPE +#endif // SDFAT_FILE_TYPE +// +// Only define File if FS.h is not included. +// Line with test for __has_include must not have operators or parentheses. +#if defined __has_include +#if __has_include() +#define HAS_INCLUDE_FS_H +#warning File not defined because __has_include(FS.h) +#endif // __has_include() +#endif // defined __has_include +#ifndef HAS_INCLUDE_FS_H +#if SDFAT_FILE_TYPE == 1 || defined(DOXYGEN) +/** Select type for File. */ +typedef File32 File; +#elif SDFAT_FILE_TYPE == 2 +typedef ExFile File; +#elif SDFAT_FILE_TYPE == 3 +typedef FsFile File; +#endif // SDFAT_FILE_TYPE +#endif // HAS_INCLUDE_FS_H /** - * \class SdFatEX - * \brief SdFat class with extended SD I/O. + * \class SdFile + * \brief FAT16/FAT32 file with Print. */ -class SdFatEX : public SdFileSystem { +class SdFile : public PrintFile { public: -#if IMPLEMENT_SPI_PORT_SELECTION || defined(DOXYGEN) - SdFatEX() { - m_spi.setPort(0); - } - /** Constructor with SPI port selection. - * \param[in] spiPort SPI port number. + SdFile() {} + /** Create an open SdFile. + * \param[in] path path for file. + * \param[in] oflag open flags. */ - explicit SdFatEX(uint8_t spiPort) { - m_spi.setPort(spiPort); - } -#endif // IMPLEMENT_SPI_PORT_SELECTION - /** Initialize SD card and file system. - * - * \param[in] csPin SD card chip select pin. - * \param[in] spiSettings SPI speed, mode, and bit order. - * \return true for success else false. - */ - bool begin(uint8_t csPin = SS, SPISettings spiSettings = SPI_FULL_SPEED) { - return m_card.begin(&m_spi, csPin, spiSettings) && - SdFileSystem::begin(); + SdFile(const char* path, oflag_t oflag) { + open(path, oflag); } - - private: - SdFatSpiDriver m_spi; -}; -//============================================================================== -#if ENABLE_SOFTWARE_SPI_CLASS || defined(DOXYGEN) -/** - * \class SdFatSoftSpiEX - * \brief SdFat class using software SPI and extended SD I/O. - */ -template -class SdFatSoftSpiEX : public SdFileSystem { - public: - /** Initialize SD card and file system. + /** Set the date/time callback function + * + * \param[in] dateTime The user's call back function. The callback + * function is of the form: + * + * \code + * void dateTime(uint16_t* date, uint16_t* time) { + * uint16_t year; + * uint8_t month, day, hour, minute, second; + * + * // User gets date and time from GPS or real-time clock here + * + * // return date using FS_DATE macro to format fields + * *date = FS_DATE(year, month, day); + * + * // return time using FS_TIME macro to format fields + * *time = FS_TIME(hour, minute, second); + * } + * \endcode + * + * Sets the function that is called when a file is created or when + * a file's directory entry is modified by sync(). All timestamps, + * access, creation, and modify, are set when a file is created. + * sync() maintains the last access date and last modify date/time. * - * \param[in] csPin SD card chip select pin. - * \param[in] spiSettings ignored for software SPI. - * \return true for success else false. */ - bool begin(uint8_t csPin = SS, SPISettings spiSettings = SPI_FULL_SPEED) { - return m_card.begin(&m_spi, csPin, spiSettings) && - SdFileSystem::begin(); + static void dateTimeCallback( + void (*dateTime)(uint16_t* date, uint16_t* time)) { + FsDateTime::setCallback(dateTime); } - private: - SdSpiSoftDriver m_spi; -}; -#endif // #if ENABLE_SOFTWARE_SPI_CLASS || defined(DOXYGEN) -#endif // ENABLE_EXTENDED_TRANSFER_CLASS || defined(DOXYGEN) -//============================================================================= -/** - * \class Sd2Card - * \brief Raw access to SD and SDHC card using default SPI library. - */ -class Sd2Card : public SdSpiCard { - public: - /** Initialize the SD card. - * \param[in] csPin SD chip select pin. - * \param[in] settings SPI speed, mode, and bit order. - * \return true for success else false. - */ - bool begin(uint8_t csPin = SS, SPISettings settings = SD_SCK_MHZ(50)) { - return SdSpiCard::begin(&m_spi, csPin, settings); + /** Cancel the date/time callback function. */ + static void dateTimeCallbackCancel() { + FsDateTime::clearCallback(); } - private: - SdFatSpiDriver m_spi; }; #endif // SdFat_h diff --git a/avr/cores/megacommand/SdFat/SdFatConfig.h b/avr/cores/megacommand/SdFat/SdFatConfig.h index 9e7127d9b..4eee09a90 100644 --- a/avr/cores/megacommand/SdFat/SdFatConfig.h +++ b/avr/cores/megacommand/SdFat/SdFatConfig.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 20011-2017 Bill Greiman + * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -28,14 +28,145 @@ */ #ifndef SdFatConfig_h #define SdFatConfig_h -#include #include #ifdef __AVR__ #include #endif // __AVR__ +// +// To try UTF-8 encoded filenames. +// #define USE_UTF8_LONG_NAMES 1 +// +// For minimum flash size use these settings: +// #define USE_FAT_FILE_FLAG_CONTIGUOUS 0 +// #define ENABLE_DEDICATED_SPI 0 +// #define USE_LONG_FILE_NAMES 0 +// #define SDFAT_FILE_TYPE 1 +// #define CHECK_FLASH_PROGRAMMING 0 // May cause SD to sleep at high current. +// +// Options can be set in a makefile or an IDE like platformIO +// if they are in a #ifndef/#endif block below. //------------------------------------------------------------------------------ +/** For Debug - must be one */ +#define ENABLE_ARDUINO_FEATURES 1 +/** For Debug - must be one */ +#define ENABLE_ARDUINO_SERIAL 1 +/** For Debug - must be one */ +#define ENABLE_ARDUINO_STRING 1 +//------------------------------------------------------------------------------ +#if ENABLE_ARDUINO_FEATURES +#include "Arduino.h" +#ifdef PLATFORM_ID +// Only defined if a Particle device. +#include "application.h" +#endif // PLATFORM_ID +#endif // ENABLE_ARDUINO_FEATURES +//------------------------------------------------------------------------------ +/** + * File types for SdFat, File, SdFile, SdBaseFile, fstream, + * ifstream, and ofstream. + * + * Set SDFAT_FILE_TYPE to: + * + * 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT. + */ + +#define SDFAT_FILE_TYPE 1 +//------------------------------------------------------------------------------ +/** + * Set USE_FAT_FILE_FLAG_CONTIGUOUS nonzero to optimize access to + * contiguous files. A small amount of flash is flash is used. + */ +#define USE_FAT_FILE_FLAG_CONTIGUOUS 1 +//------------------------------------------------------------------------------ +/** + * Set ENABLE_DEDICATED_SPI non-zero to enable dedicated use of the SPI bus. + * Selecting dedicated SPI in SdSpiConfig() will produce better + * performance by using very large multi-block transfers to and + * from the SD card. + * + * Enabling dedicated SPI will cost extra flash and RAM. + */ +#define ENABLE_DEDICATED_SPI 1 +//------------------------------------------------------------------------------ +// Driver options /** - * Set USE_LONG_FILE_NAMES nonzero to use long file names (LFN). + * If the symbol SPI_DRIVER_SELECT is: + * + * 0 - An optimized custom SPI driver is used if it exists + * else the standard library driver is used. + * + * 1 - The standard library driver is always used. + * + * 2 - An external SPI driver of SoftSpiDriver template class is always used. + * + * 3 - An external SPI driver derived from SdSpiBaseClass is always used. + */ +#ifndef SPI_DRIVER_SELECT +#define SPI_DRIVER_SELECT 0 +#endif // SPI_DRIVER_SELECT +/** + * If USE_SPI_ARRAY_TRANSFER is non-zero and the standard SPI library is + * use, the array transfer function, transfer(buf, size), will be used. + * This option will allocate up to a 512 byte temporary buffer for send. + * This may be faster for some boards. Do not use this with AVR boards. + */ +#ifndef USE_SPI_ARRAY_TRANSFER +#define USE_SPI_ARRAY_TRANSFER 0 +#endif // USE_SPI_ARRAY_TRANSFER +/** + * SD maximum initialization clock rate. + */ +#ifndef SD_MAX_INIT_RATE_KHZ +#define SD_MAX_INIT_RATE_KHZ 400 +#endif // SD_MAX_INIT_RATE_KHZ +/** + * Set USE_BLOCK_DEVICE_INTERFACE nonzero to use a generic block device. + * This allow use of an external FsBlockDevice driver that is derived from + * the FsBlockDeviceInterface like this: + * + * class UsbMscDriver : public FsBlockDeviceInterface { + * ... code for USB mass storage class driver. + * }; + * + * UsbMscDriver usbMsc; + * FsVolume key; + * ... + * + * // Init USB MSC driver. + * if (!usbMsc.begin()) { + * ... handle driver init failure. + * } + * // Init FAT/exFAT volume. + * if (!key.begin(&usbMsc)) { + * ... handle FAT/exFAT failure. + * } + */ +#ifndef USE_BLOCK_DEVICE_INTERFACE +#define USE_BLOCK_DEVICE_INTERFACE 0 +#endif // USE_BLOCK_DEVICE_INTERFACE + /** + * SD_CHIP_SELECT_MODE defines how the functions + * void sdCsInit(SdCsPin_t pin) {pinMode(pin, OUTPUT);} + * and + * void sdCsWrite(SdCsPin_t pin, bool level) {digitalWrite(pin, level);} + * are defined. + * + * 0 - Internal definition is a strong symbol and can't be replaced. + * + * 1 - Internal definition is a weak symbol and can be replaced. + * + * 2 - No internal definition and must be defined in the application. + */ +#ifndef SD_CHIP_SELECT_MODE +#define SD_CHIP_SELECT_MODE 0 +#endif // SD_CHIP_SELECT_MODE +/** Type for card chip select pin. */ +typedef uint8_t SdCsPin_t; +//------------------------------------------------------------------------------ +/** + * Set USE_LONG_FILE_NAMES nonzero to use long file names (LFN) in FAT16/FAT32. + * exFAT always uses long file names. + * * Long File Name are limited to a maximum length of 255 characters. * * This implementation allows 7-bit characters in the range @@ -52,51 +183,70 @@ * * (asterisk) * */ +#ifndef USE_LONG_FILE_NAMES #define USE_LONG_FILE_NAMES 1 -//------------------------------------------------------------------------------ +#endif // USE_LONG_FILE_NAMES /** - * If the symbol ENABLE_EXTENDED_TRANSFER_CLASS is nonzero, the class SdFatEX - * will be defined. If the symbol ENABLE_SOFTWARE_SPI_CLASS is also nonzero, - * the class SdFatSoftSpiEX will be defined. + * Set USE_UTF8_LONG_NAMES nonzero to use UTF-8 file names. Use of UTF-8 names + * will require significantly more flash memory and a small amount of extra + * RAM. + * + * UTF-8 filenames allow encoding of 1,112,064 code points in Unicode using + * one to four one-byte (8-bit) code units. + * + * As of Version 13.0, the Unicode Standard defines 143,859 characters. * - * These classes used extended multi-block SD I/O for better performance. - * the SPI bus may not be shared with other devices in this mode. + * getName() will return UTF-8 strings and printName() will write UTF-8 strings. */ -#define ENABLE_EXTENDED_TRANSFER_CLASS 0 -//----------------------------------------------------------------------------- +#ifndef USE_UTF8_LONG_NAMES +#define USE_UTF8_LONG_NAMES 0 +#endif // USE_UTF8_LONG_NAMES + +#if USE_UTF8_LONG_NAMES && !USE_LONG_FILE_NAMES +#error "USE_UTF8_LONG_NAMES requires USE_LONG_FILE_NAMES to be non-zero." +#endif // USE_UTF8_LONG_NAMES && !USE_LONG_FILE_NAMES +//------------------------------------------------------------------------------ /** - * If the symbol USE_STANDARD_SPI_LIBRARY is nonzero, the classes SdFat and - * SdFatEX use the standard Arduino SPI.h library. If USE_STANDARD_SPI_LIBRARY - * is zero, an optimized custom SPI driver is used if it exists. + * Set MAINTAIN_FREE_CLUSTER_COUNT nonzero to keep the count of free clusters + * updated. This will increase the speed of the freeClusterCount() call + * after the first call. Extra flash will be required. */ -#define USE_STANDARD_SPI_LIBRARY 0 -//----------------------------------------------------------------------------- +#ifndef MAINTAIN_FREE_CLUSTER_COUNT +#define MAINTAIN_FREE_CLUSTER_COUNT 0 +#endif // MAINTAIN_FREE_CLUSTER_COUNT +//------------------------------------------------------------------------------ /** - * If the symbol ENABLE_SOFTWARE_SPI_CLASS is nonzero, the class SdFatSoftSpi - * will be defined. If ENABLE_EXTENDED_TRANSFER_CLASS is also nonzero, - * the class SdFatSoftSpiEX will be defined. + * Set the default file time stamp when a RTC callback is not used. + * A valid date and time is required by the FAT/exFAT standard. + * + * The default below is YYYY-01-01 00:00:00 midnight where YYYY is + * the compile year from the __DATE__ macro. This is easy to recognize + * as a placeholder for a correct date/time. + * + * The full compile date is: + * FS_DATE(compileYear(), compileMonth(), compileDay()) + * + * The full compile time is: + * FS_TIME(compileHour(), compileMinute(), compileSecond()) */ -#define ENABLE_SOFTWARE_SPI_CLASS 0 +#define FS_DEFAULT_DATE FS_DATE(compileYear(), 1, 1) +/** 00:00:00 midnight */ +#define FS_DEFAULT_TIME FS_TIME(0, 0, 0) //------------------------------------------------------------------------------ /** - * If CHECK_FLASH_PROGRAMMING is zero, overlap of single sector flash + * If CHECK_FLASH_PROGRAMMING is zero, overlap of single sector flash * programming and other operations will be allowed for faster write * performance. * * Some cards will not sleep in low power mode unless CHECK_FLASH_PROGRAMMING * is non-zero. */ +#ifndef CHECK_FLASH_PROGRAMMING #define CHECK_FLASH_PROGRAMMING 1 +#endif // CHECK_FLASH_PROGRAMMING //------------------------------------------------------------------------------ /** - * Set MAINTAIN_FREE_CLUSTER_COUNT nonzero to keep the count of free clusters - * updated. This will increase the speed of the freeClusterCount() call - * after the first call. Extra flash will be required. - */ -#define MAINTAIN_FREE_CLUSTER_COUNT 0 -//------------------------------------------------------------------------------ -/** - * To enable SD card CRC checking set USE_SD_CRC nonzero. + * To enable SD card CRC checking for SPI, set USE_SD_CRC nonzero. * * Set USE_SD_CRC to 1 to use a smaller CRC-CCITT function. This function * is slower for AVR but may be fast for ARM and other processors. @@ -104,33 +254,55 @@ * Set USE_SD_CRC to 2 to used a larger table driven CRC-CCITT function. This * function is faster for AVR but may be slower for ARM and other processors. */ +#ifndef USE_SD_CRC #define USE_SD_CRC 0 +#endif // USE_SD_CRC +//------------------------------------------------------------------------------ +/** If the symbol USE_FCNTL_H is nonzero, open flags for access modes O_RDONLY, + * O_WRONLY, O_RDWR and the open modifiers O_APPEND, O_CREAT, O_EXCL, O_SYNC + * will be defined by including the system file fcntl.h. + */ +#ifndef USE_FCNTL_H +#if defined(__AVR__) +// AVR fcntl.h does not define open flags. +#define USE_FCNTL_H 0 +#elif defined(PLATFORM_ID) +// Particle boards - use fcntl.h. +#define USE_FCNTL_H 1 +#elif defined(__arm__) +// ARM gcc defines open flags. +#define USE_FCNTL_H 1 +#elif defined(ESP32) +#define USE_FCNTL_H 1 +#else // defined(__AVR__) +#define USE_FCNTL_H 0 +#endif // defined(__AVR__) +#endif // USE_FCNTL_H //------------------------------------------------------------------------------ /** - * Handle Watchdog Timer for WiFi modules. - * - * Yield will be called before accessing the SPI bus if it has been more - * than WDT_YIELD_TIME_MICROS microseconds since the last yield call by SdFat. + * Set INCLUDE_SDIOS nonzero to include sdios.h in SdFat.h. + * sdios.h provides C++ style IO Streams. */ -#if defined(PLATFORM_ID) || defined(ESP8266) -// If Particle device or ESP8266 call yield. -#define WDT_YIELD_TIME_MICROS 100000 -#else -#define WDT_YIELD_TIME_MICROS 0 -#endif +#ifndef INCLUDE_SDIOS +#define INCLUDE_SDIOS 0 +#endif // INCLUDE_SDIOS //------------------------------------------------------------------------------ /** * Set FAT12_SUPPORT nonzero to enable use if FAT12 volumes. * FAT12 has not been well tested and requires additional flash. */ +#ifndef FAT12_SUPPORT #define FAT12_SUPPORT 0 +#endif // FAT12_SUPPORT //------------------------------------------------------------------------------ /** * Set DESTRUCTOR_CLOSES_FILE nonzero to close a file in its destructor. * * Causes use of lots of heap in ARM. */ +#ifndef DESTRUCTOR_CLOSES_FILE #define DESTRUCTOR_CLOSES_FILE 0 +#endif // DESTRUCTOR_CLOSES_FILE //------------------------------------------------------------------------------ /** * Call flush for endl if ENDL_CALLS_FLUSH is nonzero @@ -139,9 +311,9 @@ * SdFat. Each call to flush causes 2048 bytes of I/O to the SD. * * SdFat has a single 512 byte buffer for SD I/O so it must write the current - * data block to the SD, read the directory block from the SD, update the - * directory entry, write the directory block to the SD and read the data - * block back into the buffer. + * data sector to the SD, read the directory sector from the SD, update the + * directory entry, write the directory sector to the SD and read the data + * sector back into the buffer. * * The SD flash memory controller is not designed for this many rewrites * so performance may be reduced by more than a factor of 100. @@ -149,12 +321,25 @@ * If ENDL_CALLS_FLUSH is zero, you must call flush and/or close to force * all data to be written to the SD. */ +#ifndef ENDL_CALLS_FLUSH #define ENDL_CALLS_FLUSH 0 +#endif // ENDL_CALLS_FLUSH +//------------------------------------------------------------------------------ +/** + * Set USE_SIMPLE_LITTLE_ENDIAN nonzero for little endian processors + * with no memory alignment restrictions. + */ +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__\ + && (defined(__AVR__) || defined(__ARM_FEATURE_UNALIGNED)) +#define USE_SIMPLE_LITTLE_ENDIAN 1 +#else // __BYTE_ORDER_ +#define USE_SIMPLE_LITTLE_ENDIAN 0 +#endif // __BYTE_ORDER_ //------------------------------------------------------------------------------ /** * Set USE_SEPARATE_FAT_CACHE nonzero to use a second 512 byte cache - * for FAT table entries. This improves performance for large writes - * that are not a multiple of 512 bytes. + * for FAT16/FAT32 table entries. This improves performance for large + * writes that are not a multiple of 512 bytes. */ #ifdef __arm__ #define USE_SEPARATE_FAT_CACHE 1 @@ -163,46 +348,64 @@ #endif // __arm__ //------------------------------------------------------------------------------ /** - * Set USE_MULTI_BLOCK_IO nonzero to use multi-block SD read/write. + * Set USE_EXFAT_BITMAP_CACHE nonzero to use a second 512 byte cache + * for exFAT bitmap entries. This improves performance for large + * writes that are not a multiple of 512 bytes. + */ +#ifdef __arm__ +#define USE_EXFAT_BITMAP_CACHE 1 +#else // __arm__ +#define USE_EXFAT_BITMAP_CACHE 0 +#endif // __arm__ +//------------------------------------------------------------------------------ +/** + * Set USE_MULTI_SECTOR_IO nonzero to use multi-sector SD read/write. * - * Don't use mult-block read/write on small AVR boards. + * Don't use mult-sector read/write on small AVR boards. */ -#if defined(RAMEND) && RAMEND < 3000 -#define USE_MULTI_BLOCK_IO 0 -#else // RAMEND -#define USE_MULTI_BLOCK_IO 1 -#endif // RAMEND -//----------------------------------------------------------------------------- +#define USE_MULTI_SECTOR_IO 1 +//------------------------------------------------------------------------------ /** Enable SDIO driver if available. */ #if defined(__MK64FX512__) || defined(__MK66FX1M0__) -#define ENABLE_SDIO_CLASS 1 -#define ENABLE_SDIOEX_CLASS 1 -#else // ENABLE_SDIO_CLASS -#define ENABLE_SDIO_CLASS 0 -#endif // ENABLE_SDIO_CLASS +// Pseudo pin select for SDIO. +#ifndef BUILTIN_SDCARD +#define BUILTIN_SDCARD 254 +#endif // BUILTIN_SDCARD +// SPI for built-in card. +#ifndef SDCARD_SPI +#define SDCARD_SPI SPI1 +#define SDCARD_MISO_PIN 59 +#define SDCARD_MOSI_PIN 61 +#define SDCARD_SCK_PIN 60 +#define SDCARD_SS_PIN 62 +#endif // SDCARD_SPI +#define HAS_SDIO_CLASS 1 +#endif // defined(__MK64FX512__) || defined(__MK66FX1M0__) +#if defined(__IMXRT1062__) +#define HAS_SDIO_CLASS 1 +#endif // defined(__IMXRT1062__) //------------------------------------------------------------------------------ /** * Determine the default SPI configuration. */ -#if defined(__STM32F1__) || defined(__STM32F4__) -// has multiple SPI ports -#define SD_HAS_CUSTOM_SPI 2 -#elif defined(__AVR__)\ - || defined(__SAM3X8E__) || defined(__SAM3X8H__)\ - || (defined(__arm__) && defined(CORE_TEENSY))\ - || defined(ESP8266) +#if defined(ARDUINO_ARCH_APOLLO3)\ + || (defined(__AVR__) && defined(SPDR) && defined(SPSR) && defined(SPIF))\ + || (defined(__AVR__) && defined(SPI0) && defined(SPI_RXCIF_bm))\ + || defined(ESP8266) || defined(ESP32)\ + || defined(PLATFORM_ID)\ + || defined(ARDUINO_SAM_DUE)\ + || defined(STM32_CORE_VERSION)\ + || defined(__STM32F1__) || defined(__STM32F4__)\ + || (defined(CORE_TEENSY) && defined(__arm__)) #define SD_HAS_CUSTOM_SPI 1 #else // SD_HAS_CUSTOM_SPI // Use standard SPI library. #define SD_HAS_CUSTOM_SPI 0 #endif // SD_HAS_CUSTOM_SPI //------------------------------------------------------------------------------ -/** - * Check if API to select HW SPI port is needed. - */ -#if (USE_STANDARD_SPI_LIBRARY || SD_HAS_CUSTOM_SPI < 2) -#define IMPLEMENT_SPI_PORT_SELECTION 0 -#else // USE_STANDARD_SPI_LIBRARY -#define IMPLEMENT_SPI_PORT_SELECTION 1 -#endif // USE_STANDARD_SPI_LIBRARY +#ifndef HAS_SDIO_CLASS +/** Default is no SDIO. */ +#define HAS_SDIO_CLASS 0 +#endif // HAS_SDIO_CLASS + #endif // SdFatConfig_h diff --git a/avr/cores/megacommand/SdFat/SpiDriver/SdSpiArduinoDriver.h b/avr/cores/megacommand/SdFat/SpiDriver/SdSpiArduinoDriver.h new file mode 100644 index 000000000..cacd62e96 --- /dev/null +++ b/avr/cores/megacommand/SdFat/SpiDriver/SdSpiArduinoDriver.h @@ -0,0 +1,96 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +/** + * \file + * \brief SpiDriver classes for Arduino compatible systems. + */ +#ifndef SdSpiArduinoDriver_h +#define SdSpiArduinoDriver_h +//============================================================================== +#if SPI_DRIVER_SELECT == 0 && SD_HAS_CUSTOM_SPI +#define SD_USE_CUSTOM_SPI +#endif // SPI_DRIVER_SELECT == 0 && SD_HAS_CUSTOM_SPI +/** + * \class SdSpiArduinoDriver + * \brief Optimized SPI class for access to SD and SDHC flash memory cards. + */ +class SdSpiArduinoDriver { + public: + /** Activate SPI hardware. */ + void activate(); + /** Initialize the SPI bus. + * + * \param[in] spiConfig SD card configuration. + */ + void begin(SdSpiConfig spiConfig); + /** Deactivate SPI hardware. */ + void deactivate(); + /** End use of SPI driver after begin() call. */ + void end(); + /** Receive a byte. + * + * \return The byte. + */ + uint8_t receive(); + /** Receive multiple bytes. + * + * \param[out] buf Buffer to receive the data. + * \param[in] count Number of bytes to receive. + * + * \return Zero for no error or nonzero error code. + */ + uint8_t receive(uint8_t* buf, size_t count); + /** Send a byte. + * + * \param[in] data Byte to send + */ + void send(uint8_t data); + /** Send multiple bytes. + * + * \param[in] buf Buffer for data to be sent. + * \param[in] count Number of bytes to send. + */ + void send(const uint8_t* buf, size_t count); + /** Save high speed SPISettings after SD initialization. + * + * \param[in] maxSck Maximum SCK frequency. + */ + void setSckSpeed(uint32_t maxSck) { + m_spiSettings = SPISettings(maxSck, MSBFIRST, SPI_MODE0); + } + + private: + SPIClass *m_spi; + SPISettings m_spiSettings; +}; +/** Typedef for use of SdSpiArduinoDriver */ +typedef SdSpiArduinoDriver SdSpiDriver; +//------------------------------------------------------------------------------ +#ifndef SD_USE_CUSTOM_SPI +#include "SdSpiLibDriver.h" +#elif defined(__AVR__) +#include "SdSpiAvr.h" +#endif // __AVR__ +#endif // SdSpiArduinoDriver_h diff --git a/avr/cores/megacommand/SdFat/SpiDriver/SdSpiArtemis.cpp b/avr/cores/megacommand/SdFat/SpiDriver/SdSpiArtemis.cpp new file mode 100644 index 000000000..319908732 --- /dev/null +++ b/avr/cores/megacommand/SdFat/SpiDriver/SdSpiArtemis.cpp @@ -0,0 +1,79 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "SdSpiDriver.h" +#if defined(SD_USE_CUSTOM_SPI) && defined(ARDUINO_ARCH_APOLLO3) +//------------------------------------------------------------------------------ +void SdSpiArduinoDriver::activate() { + m_spi->beginTransaction(m_spiSettings); +} +//------------------------------------------------------------------------------ +void SdSpiArduinoDriver::begin(SdSpiConfig spiConfig) { + if (spiConfig.spiPort) { + m_spi = spiConfig.spiPort; + } else { + m_spi = &SPI; + } + m_spi->begin(); +} +//------------------------------------------------------------------------------ +void SdSpiArduinoDriver::deactivate() { + m_spi->endTransaction(); +} +//------------------------------------------------------------------------------ +void SdSpiArduinoDriver::end() { + m_spi->end(); +} +//------------------------------------------------------------------------------ +uint8_t SdSpiArduinoDriver::receive() { + return m_spi->transfer(0XFF); +} +//------------------------------------------------------------------------------ +uint8_t SdSpiArduinoDriver::receive(uint8_t* buf, size_t count) { + memset(buf, 0XFF, count); + m_spi->transfer(buf, count); + return 0; +} +//------------------------------------------------------------------------------ +void SdSpiArduinoDriver::send(uint8_t data) { + m_spi->transfer(data); +} +//------------------------------------------------------------------------------ +void SdSpiArduinoDriver::send(const uint8_t* buf, size_t count) { + // If not a multiple of four. Command with CRC used six byte send. + while (count%4) { + send(*buf++); + count--; + } + // Convert byte array to 4 byte array. + uint32_t myArray[count/4]; // NOLINT + for (int x = 0; x < count/4; x++) { + myArray[x] = ((uint32_t)buf[(x * 4) + 3] << (8 * 3)) | + ((uint32_t)buf[(x * 4) + 2] << (8 * 2)) | + ((uint32_t)buf[(x * 4) + 1] << (8 * 1)) | + ((uint32_t)buf[(x * 4) + 0] << (8 * 0)); + } + m_spi->transfer(reinterpret_cast(myArray), count); +} +#endif // defined(SD_USE_CUSTOM_SPI) && defined(ARDUINO_ARCH_APOLLO3) diff --git a/avr/cores/megacommand/SdFat/SpiDriver/SdSpiAvr.h b/avr/cores/megacommand/SdFat/SpiDriver/SdSpiAvr.h new file mode 100644 index 000000000..f07bc9f2b --- /dev/null +++ b/avr/cores/megacommand/SdFat/SpiDriver/SdSpiAvr.h @@ -0,0 +1,124 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef SdSpiAvr_h +#define SdSpiAvr_h +// Use of in-line for AVR to save flash. +#define nop asm volatile ("nop\n\t") +//------------------------------------------------------------------------------ +inline void SdSpiArduinoDriver::activate() { + SPI.beginTransaction(m_spiSettings); +} +//------------------------------------------------------------------------------ +inline void SdSpiArduinoDriver::begin(SdSpiConfig spiConfig) { + (void)spiConfig; + SPI.begin(); +} +//------------------------------------------------------------------------------ +inline void SdSpiArduinoDriver::deactivate() { + SPI.endTransaction(); +} +//------------------------------------------------------------------------------ +inline void SdSpiArduinoDriver::end() { + SPI.end(); +} +//------------------------------------------------------------------------------ +inline uint8_t SdSpiArduinoDriver::receive() { + return SPI.transfer(0XFF); +} +//------------------------------------------------------------------------------ +inline uint8_t SdSpiArduinoDriver::receive(uint8_t* buf, size_t count) { + if (count == 0) { + return 0; + } +#ifdef SPSR + SPDR = 0XFF; + while (--count) { + // nops optimize loop for 16MHz CPU 8 MHz SPI + nop; + nop; + while (!(SPSR & _BV(SPIF))) {} + uint8_t in = SPDR; + SPDR = 0XFF; + *buf++ = in; + } + while (!(SPSR & _BV(SPIF))) {} + *buf = SPDR; +#elif defined(SPI_RXCIF_bm) + SPI0.DATA = 0XFF; + while (--count) { + // nops optimize loop for ATmega4809 16MHz CPU 8 MHz SPI + nop; + nop; + nop; + nop; + while (!(SPI0.INTFLAGS & SPI_RXCIF_bm)) {} + uint8_t in = SPI0.DATA; + SPI0.DATA = 0XFF; + *buf++ = in; + } + while (!(SPI0.INTFLAGS & SPI_RXCIF_bm)) {} + *buf = SPI0.DATA; +#else // SPSR +#error Unsupported AVR CPU - edit SdFatConfig.h to use standard SPI library. +#endif // SPSR + return 0; +} +//------------------------------------------------------------------------------ +inline void SdSpiArduinoDriver::send(uint8_t data) { + SPI.transfer(data); +} +//------------------------------------------------------------------------------ +inline void SdSpiArduinoDriver::send(const uint8_t* buf , size_t count) { + if (count == 0) { + return; + } +#ifdef SPSR + SPDR = *buf++; + while (--count) { + uint8_t b = *buf++; + // nops optimize loop for 16MHz CPU 8 MHz SPI + nop; + nop; + while (!(SPSR & (1 << SPIF))) {} + SPDR = b; + } + while (!(SPSR & (1 << SPIF))) {} +#elif defined(SPI_RXCIF_bm) + SPI0.DATA = *buf++; + while (--count) { + uint8_t b = *buf++; + // nops optimize loop for ATmega4809 16MHz CPU 8 MHz SPI + nop; + nop; + nop; + while (!(SPI0.INTFLAGS & SPI_RXCIF_bm)) {} + SPI0.DATA = b; + } + while (!(SPI0.INTFLAGS & SPI_RXCIF_bm)) {} +#else // SPSR +#error Unsupported AVR CPU - edit SdFatConfig.h to use standard SPI library. +#endif // SPSR +} +#endif // SdSpiAvr_h diff --git a/avr/cores/megacommand/SdFat/SpiDriver/SdSpiBareUnoDriver.h b/avr/cores/megacommand/SdFat/SpiDriver/SdSpiBareUnoDriver.h new file mode 100644 index 000000000..ee00b26c9 --- /dev/null +++ b/avr/cores/megacommand/SdFat/SpiDriver/SdSpiBareUnoDriver.h @@ -0,0 +1,200 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef SdSpiBareUnoDriver_h +#define SdSpiBareUnoDriver_h +/** + * \file + * \brief Driver to test with no Arduino includes. + */ + +#include +#include "../common/SysCall.h" +#define nop asm volatile ("nop\n\t") +#ifndef HIGH +#define HIGH 1 +#endif // HIGH +#ifndef LOW +#define LOW 0 +#endif // LOW +#ifndef INPUT +#define INPUT 0 +#endif // INPUT +#ifndef OUTPUT +#define OUTPUT 1 +#endif // OUTPUT + +inline uint8_t unoBit(uint8_t pin) { + return 1 << (pin < 8 ? pin : pin < 14 ? pin - 8 : pin - 14); +} +inline uint8_t unoDigitalRead(uint8_t pin) { + volatile uint8_t* reg = pin < 8 ? &PIND : pin < 14 ? &PINB : &PINC; + return *reg & unoBit(pin); +} +inline void unoDigitalWrite(uint8_t pin, uint8_t value) { + volatile uint8_t* port = pin < 8 ? &PORTD : pin < 14 ? &PORTB : &PORTC; + uint8_t bit = unoBit(pin); + cli(); + if (value) { + *port |= bit; + } else { + *port &= ~bit; + } + sei(); +} + +inline void unoPinMode(uint8_t pin, uint8_t mode) { + uint8_t bit = unoBit(pin); + volatile uint8_t* reg = pin < 8 ? &DDRD : pin < 14 ? &DDRB : &DDRC; + + cli(); + if (mode == OUTPUT) { + *reg |= bit; + } else { + *reg &= ~bit; + // handle INPUT pull-up + unoDigitalWrite(pin, mode != INPUT); + } + sei(); +} + +#define UNO_SS 10 +#define UNO_MOSI 11 +#define UNO_MISO 12 +#define UNO_SCK 13 +//------------------------------------------------------------------------------ +/** + * \class SdSpiDriverBareUno + * \brief Optimized SPI class for access to SD and SDHC flash memory cards. + */ +class SdSpiDriverBareUno { + public: + /** Activate SPI hardware. */ + void activate() {} + /** Initialize the SPI bus. + * + * \param[in] spiConfig SD card configuration. + */ + void begin(SdSpiConfig spiConfig) { + m_csPin = spiConfig.csPin; + unoPinMode(m_csPin, OUTPUT); + unoDigitalWrite(m_csPin, HIGH); + unoDigitalWrite(UNO_SS, HIGH); + unoPinMode(UNO_SS, OUTPUT); + SPCR |= _BV(MSTR); + SPCR |= _BV(SPE); + SPSR = 0; + unoPinMode(UNO_SCK, OUTPUT); + unoPinMode(UNO_MOSI, OUTPUT); + } + /** Deactivate SPI hardware. */ + void deactivate() {} + /** deactivate SPI driver. */ + void end() {} + /** Receive a byte. + * + * \return The byte. + */ + uint8_t receive() { + return transfer(0XFF); + } + /** Receive multiple bytes. + * + * \param[out] buf Buffer to receive the data. + * \param[in] count Number of bytes to receive. + * + * \return Zero for no error or nonzero error code. + */ + uint8_t receive(uint8_t* buf, size_t count) { + if (count == 0) { + return 0; + } + uint8_t* pr = buf; + SPDR = 0XFF; + while (--count > 0) { + while (!(SPSR & _BV(SPIF))) {} + uint8_t in = SPDR; + SPDR = 0XFF; + *pr++ = in; + // nops to optimize loop for 16MHz CPU 8 MHz SPI + nop; + nop; + } + while (!(SPSR & _BV(SPIF))) {} + *pr = SPDR; + return 0; + } + /** Send a byte. + * + * \param[in] data Byte to send + */ + void send(uint8_t data) { + transfer(data); + } + /** Send multiple bytes. + * + * \param[in] buf Buffer for data to be sent. + * \param[in] count Number of bytes to send. + */ + void send(const uint8_t* buf, size_t count) { + if (count == 0) { + return; + } + SPDR = *buf++; + while (--count > 0) { + uint8_t b = *buf++; + while (!(SPSR & (1 << SPIF))) {} + SPDR = b; + // nops to optimize loop for 16MHz CPU 8 MHz SPI + nop; + nop; + } + while (!(SPSR & (1 << SPIF))) {} + } + /** Set CS low. */ + void select() { + unoDigitalWrite(m_csPin, LOW); + } + /** Save high speed SPISettings after SD initialization. + * + * \param[in] spiConfig SPI options. + */ + void setSckSpeed(uint32_t maxSck) { + (void)maxSck; + SPSR |= 1 << SPI2X; + } + static uint8_t transfer(uint8_t data) { + SPDR = data; + while (!(SPSR & _BV(SPIF))) {} // wait + return SPDR; + } + /** Set CS high. */ + void unselect() { + unoDigitalWrite(m_csPin, HIGH); + } + + private: + SdCsPin_t m_csPin; +}; +#endif // SdSpiBareUnoDriver_h diff --git a/avr/cores/megacommand/SdFat/SpiDriver/SdSpiBaseDriver.h b/avr/cores/megacommand/SdFat/SpiDriver/SdSpiBaseClass.h similarity index 58% rename from avr/cores/megacommand/SdFat/SpiDriver/SdSpiBaseDriver.h rename to avr/cores/megacommand/SdFat/SpiDriver/SdSpiBaseClass.h index 02ba60452..2846f183e 100644 --- a/avr/cores/megacommand/SdFat/SpiDriver/SdSpiBaseDriver.h +++ b/avr/cores/megacommand/SdFat/SpiDriver/SdSpiBaseClass.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 20011-2017 Bill Greiman + * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -22,58 +22,57 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ -#ifndef SdSpiBaseDriver_h -#define SdSpiBaseDriver_h /** - * \class SdSpiBaseDriver - * \brief SPI base driver. + * \file + * \brief Base class for external SPI driver. */ -class SdSpiBaseDriver { +#ifndef SdSpiBaseClass_h +#define SdSpiBaseClass_h +/** + * \class SdSpiBaseClass + * \brief Base class for external SPI drivers + */ +class SdSpiBaseClass { public: - /** Set SPI options for access to SD/SDHC cards. - * - */ - virtual void activate() = 0; + /** Activate SPI hardware. */ + virtual void activate() {} /** Initialize the SPI bus. * - * \param[in] chipSelectPin SD card chip select pin. + * \param[in] config SPI configuration. */ - virtual void begin(uint8_t chipSelectPin) = 0; - /** - * End SPI transaction. - */ - virtual void deactivate() = 0; + virtual void begin(SdSpiConfig config) = 0; + /** Deactivate SPI hardware. */ + virtual void deactivate() {} + /** deactivate SPI driver. */ + virtual void end() {} /** Receive a byte. * * \return The byte. */ virtual uint8_t receive() = 0; /** Receive multiple bytes. - * - * \param[out] buf Buffer to receive the data. - * \param[in] n Number of bytes to receive. - * - * \return Zero for no error or nonzero error code. - */ - virtual uint8_t receive(uint8_t* buf, size_t n) = 0; + * + * \param[out] buf Buffer to receive the data. + * \param[in] count Number of bytes to receive. + * + * \return Zero for no error or nonzero error code. + */ + virtual uint8_t receive(uint8_t* buf, size_t count) = 0; /** Send a byte. * * \param[in] data Byte to send */ virtual void send(uint8_t data) = 0; /** Send multiple bytes. - * - * \param[in] buf Buffer for data to be sent. - * \param[in] n Number of bytes to send. - */ - virtual void send(const uint8_t* buf, size_t n) = 0; - /** Set CS low. */ - virtual void select() = 0; - /** Save SPI settings. - * \param[in] spiSettings SPI speed, mode, and bit order. + * + * \param[in] buf Buffer for data to be sent. + * \param[in] count Number of bytes to send. + */ + virtual void send(const uint8_t* buf, size_t count) = 0; + /** Save high speed SPISettings after SD initialization. + * + * \param[in] maxSck Maximum SCK frequency. */ - virtual void setSpiSettings(SPISettings spiSettings) = 0; - /** Set CS high. */ - virtual void unselect() = 0; + virtual void setSckSpeed(uint32_t maxSck) {(void)maxSck;} }; -#endif // SdSpiBaseDriver_h +#endif // SdSpiBaseClass_h diff --git a/avr/cores/megacommand/SdFat/SpiDriver/SdSpiChipSelect.cpp b/avr/cores/megacommand/SdFat/SpiDriver/SdSpiChipSelect.cpp new file mode 100644 index 000000000..ea7aa343f --- /dev/null +++ b/avr/cores/megacommand/SdFat/SpiDriver/SdSpiChipSelect.cpp @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "SdSpiDriver.h" +#if ENABLE_ARDUINO_FEATURES +#if SD_CHIP_SELECT_MODE == 0 +//------------------------------------------------------------------------------ +void sdCsInit(SdCsPin_t pin) { + pinMode(pin, OUTPUT); +} +//------------------------------------------------------------------------------ +void sdCsWrite(SdCsPin_t pin, bool level) { + digitalWrite(pin, level); +} +#elif SD_CHIP_SELECT_MODE == 1 +//------------------------------------------------------------------------------ +__attribute__((weak)) +void sdCsInit(SdCsPin_t pin) { + pinMode(pin, OUTPUT); +} +//------------------------------------------------------------------------------ +__attribute__((weak)) +void sdCsWrite(SdCsPin_t pin, bool level) { + digitalWrite(pin, level); +} +#endif // SD_CHIP_SELECT_MODE == 0 +#endif // ENABLE_ARDUINO_FEATURES diff --git a/avr/cores/megacommand/SdFat/SpiDriver/SdSpiDriver.h b/avr/cores/megacommand/SdFat/SpiDriver/SdSpiDriver.h index 2181980f0..463e4fc55 100644 --- a/avr/cores/megacommand/SdFat/SpiDriver/SdSpiDriver.h +++ b/avr/cores/megacommand/SdFat/SpiDriver/SdSpiDriver.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 20011-2017 Bill Greiman + * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -28,342 +28,128 @@ */ #ifndef SdSpiDriver_h #define SdSpiDriver_h -#include -#include "SPI.h" -#include "SdSpiBaseDriver.h" -#include "SdFatConfig.h" -//----------------------------------------------------------------------------- -/** SDCARD_SPI is defined if board has built-in SD card socket */ -#ifndef SDCARD_SPI -#define SDCARD_SPI SPI -#endif // SDCARD_SPI -//----------------------------------------------------------------------------- +#include "../common/SysCall.h" /** - * \class SdSpiLibDriver - * \brief SdSpiLibDriver - use standard SPI library. + * Initialize SD chip select pin. + * + * \param[in] pin SD card chip select pin. */ -#if ENABLE_SOFTWARE_SPI_CLASS -class SdSpiLibDriver : public SdSpiBaseDriver { -#else // ENABLE_SOFTWARE_SPI_CLASS -class SdSpiLibDriver { -#endif // ENABLE_SOFTWARE_SPI_CLASS - public: - /** Activate SPI hardware. */ - void activate() { - SDCARD_SPI.beginTransaction(m_spiSettings); - } - /** Deactivate SPI hardware. */ - void deactivate() { - SDCARD_SPI.endTransaction(); - } - /** Initialize the SPI bus. - * - * \param[in] csPin SD card chip select pin. - */ - void begin(uint8_t csPin) { - m_csPin = csPin; - digitalWrite(csPin, HIGH); - pinMode(csPin, OUTPUT); - SDCARD_SPI.begin(); - } - /** Receive a byte. - * - * \return The byte. - */ - uint8_t receive() { - return SDCARD_SPI.transfer( 0XFF); - } - /** Receive multiple bytes. - * - * \param[out] buf Buffer to receive the data. - * \param[in] n Number of bytes to receive. - * - * \return Zero for no error or nonzero error code. - */ - uint8_t receive(uint8_t* buf, size_t n) { - for (size_t i = 0; i < n; i++) { - buf[i] = SDCARD_SPI.transfer(0XFF); - } - return 0; - } - /** Send a byte. - * - * \param[in] data Byte to send - */ - void send(uint8_t data) { - SDCARD_SPI.transfer(data); - } - /** Send multiple bytes. - * - * \param[in] buf Buffer for data to be sent. - * \param[in] n Number of bytes to send. - */ - void send(const uint8_t* buf, size_t n) { - for (size_t i = 0; i < n; i++) { - SDCARD_SPI.transfer(buf[i]); - } - } - /** Set CS low. */ - void select() { - digitalWrite(m_csPin, LOW); - } - /** Save SPISettings. - * - * \param[in] spiSettings SPI speed, mode, and byte order. - */ - void setSpiSettings(SPISettings spiSettings) { - m_spiSettings = spiSettings; - } - /** Set CS high. */ - void unselect() { - digitalWrite(m_csPin, HIGH); - } - - private: - SPISettings m_spiSettings; - uint8_t m_csPin; -}; -//----------------------------------------------------------------------------- +void sdCsInit(SdCsPin_t pin); /** - * \class SdSpiAltDriver - * \brief Optimized SPI class for access to SD and SDHC flash memory cards. + * Initialize SD chip select pin. + * + * \param[in] pin SD card chip select pin. + * \param[in] level SD card chip select level. */ -#if ENABLE_SOFTWARE_SPI_CLASS -class SdSpiAltDriver : public SdSpiBaseDriver { -#else // ENABLE_SOFTWARE_SPI_CLASS -class SdSpiAltDriver { -#endif // ENABLE_SOFTWARE_SPI_CLASS - public: - /** Activate SPI hardware. */ - void activate(); - /** Deactivate SPI hardware. */ - void deactivate(); - /** Initialize the SPI bus. - * - * \param[in] csPin SD card chip select pin. - */ - void begin(uint8_t csPin); - /** Receive a byte. - * - * \return The byte. - */ - uint8_t receive(); - /** Receive multiple bytes. - * - * \param[out] buf Buffer to receive the data. - * \param[in] n Number of bytes to receive. - * - * \return Zero for no error or nonzero error code. - */ - uint8_t receive(uint8_t* buf, size_t n); - /** Send a byte. - * - * \param[in] data Byte to send - */ - void send(uint8_t data); - /** Send multiple bytes. - * - * \param[in] buf Buffer for data to be sent. - * \param[in] n Number of bytes to send. - */ - void send(const uint8_t* buf, size_t n); - /** Set CS low. */ - void select() { - digitalWrite(m_csPin, LOW); - } - /** Save SPISettings. - * - * \param[in] spiSettings SPI speed, mode, and byte order. - */ - void setSpiSettings(SPISettings spiSettings) { - m_spiSettings = spiSettings; - } - /** Set CS high. */ - void unselect() { - digitalWrite(m_csPin, HIGH); - } -#if IMPLEMENT_SPI_PORT_SELECTION || defined(DOXYGEN) - /** Set SPI port number. - * \param[in] portNumber Hardware SPI port number. - */ - void setPort(uint8_t portNumber); - - private: - SPIClass* m_spi; -#else // IMPLEMENT_SPI_PORT_SELECTION - private: -#endif // IMPLEMENT_SPI_PORT_SELECTION - SPISettings m_spiSettings; - uint8_t m_csPin; -}; +void sdCsWrite(SdCsPin_t pin, bool level); +//------------------------------------------------------------------------------ +/** SPI bus is share with other devices. */ +const uint8_t SHARED_SPI = 0; +#if ENABLE_DEDICATED_SPI +/** The SD is the only device on the SPI bus. */ +const uint8_t DEDICATED_SPI = 1; +/** + * \param[in] opt option field of SdSpiConfig. + * \return true for dedicated SPI. + */ +inline bool spiOptionDedicated(uint8_t opt) {return opt & DEDICATED_SPI;} +#else // ENABLE_DEDICATED_SPI +/** + * \param[in] opt option field of SdSpiConfig. + * \return true for dedicated SPI. + */ +inline bool spiOptionDedicated(uint8_t opt) {(void)opt; return false;} +#endif // ENABLE_DEDICATED_SPI +//------------------------------------------------------------------------------ +/** SPISettings for SCK frequency in Hz. */ +#define SD_SCK_HZ(maxSpeed) (maxSpeed) +/** SPISettings for SCK frequency in MHz. */ +#define SD_SCK_MHZ(maxMhz) (1000000UL*(maxMhz)) +// SPI divisor constants - obsolete. +/** Set SCK to max rate. */ +#define SPI_FULL_SPEED SD_SCK_MHZ(50) +/** Set SCK rate to 16 MHz for Due */ +#define SPI_DIV3_SPEED SD_SCK_MHZ(16) +/** Set SCK rate to 4 MHz for AVR. */ +#define SPI_HALF_SPEED SD_SCK_MHZ(4) +/** Set SCK rate to 8 MHz for Due */ +#define SPI_DIV6_SPEED SD_SCK_MHZ(8) +/** Set SCK rate to 2 MHz for AVR. */ +#define SPI_QUARTER_SPEED SD_SCK_MHZ(2) +/** Set SCK rate to 1 MHz for AVR. */ +#define SPI_EIGHTH_SPEED SD_SCK_MHZ(1) +/** Set SCK rate to 500 kHz for AVR. */ +#define SPI_SIXTEENTH_SPEED SD_SCK_HZ(500000) +//------------------------------------------------------------------------------ +#if SPI_DRIVER_SELECT < 2 +#include "SPI.h" +/** Port type for Arduino SPI hardware driver. */ +typedef SPIClass SpiPort_t; +#elif SPI_DRIVER_SELECT == 2 +class SdSpiSoftDriver; +/** Port type for software SPI driver. */ +typedef SdSpiSoftDriver SpiPort_t; +#elif SPI_DRIVER_SELECT == 3 +class SdSpiBaseClass; +/** Port type for extrernal SPI driver. */ +typedef SdSpiBaseClass SpiPort_t; +#else // SPI_DRIVER_SELECT +typedef void* SpiPort_t; +#endif // SPI_DRIVER_SELECT //------------------------------------------------------------------------------ -#if ENABLE_SOFTWARE_SPI_CLASS || defined(DOXYGEN) -#ifdef ARDUINO -#include "SoftSPI.h" -#elif defined(PLATFORM_ID) // Only defined if a Particle device -#include "SoftSPIParticle.h" -#endif // ARDUINO /** - * \class SdSpiSoftDriver - * \brief Software SPI class for access to SD and SDHC flash memory cards. + * \class SdSpiConfig + * \brief SPI card configuration. */ -template -class SdSpiSoftDriver : public SdSpiBaseDriver { +class SdSpiConfig { public: - /** Dummy activate SPI hardware for software SPI */ - void activate() {} - /** Dummy deactivate SPI hardware for software SPI */ - void deactivate() {} - /** Initialize the SPI bus. - * - * \param[in] csPin SD card chip select pin. - */ - void begin(uint8_t csPin) { - m_csPin = csPin; - pinMode(m_csPin, OUTPUT); - digitalWrite(m_csPin, HIGH); - m_spi.begin(); - } - /** Receive a byte. + /** SdSpiConfig constructor. * - * \return The byte. + * \param[in] cs Chip select pin. + * \param[in] opt Options. + * \param[in] maxSpeed Maximum SCK frequency. + * \param[in] port The SPI port to use. */ - uint8_t receive() { - return m_spi.receive(); - } - /** Receive multiple bytes. - * - * \param[out] buf Buffer to receive the data. - * \param[in] n Number of bytes to receive. - * - * \return Zero for no error or nonzero error code. - */ - uint8_t receive(uint8_t* buf, size_t n) { - for (size_t i = 0; i < n; i++) { - buf[i] = receive(); - } - return 0; - } - /** Send a byte. + SdSpiConfig(SdCsPin_t cs, uint8_t opt, uint32_t maxSpeed, SpiPort_t* port) : + csPin(cs), options(opt), maxSck(maxSpeed), spiPort(port) {} + + /** SdSpiConfig constructor. * - * \param[in] data Byte to send + * \param[in] cs Chip select pin. + * \param[in] opt Options. + * \param[in] maxSpeed Maximum SCK frequency. */ - void send(uint8_t data) { - m_spi.send(data); - } - /** Send multiple bytes. + SdSpiConfig(SdCsPin_t cs, uint8_t opt, uint32_t maxSpeed) : + csPin(cs), options(opt), maxSck(maxSpeed) {} + /** SdSpiConfig constructor. * - * \param[in] buf Buffer for data to be sent. - * \param[in] n Number of bytes to send. + * \param[in] cs Chip select pin. + * \param[in] opt Options. */ - void send(const uint8_t* buf , size_t n) { - for (size_t i = 0; i < n; i++) { - send(buf[i]); - } - } - /** Set CS low. */ - void select() { - digitalWrite(m_csPin, LOW); - } - /** Save SPISettings. + SdSpiConfig(SdCsPin_t cs, uint8_t opt) : csPin(cs), options(opt) {} + /** SdSpiConfig constructor. * - * \param[in] spiSettings SPI speed, mode, and byte order. + * \param[in] cs Chip select pin. */ - void setSpiSettings(SPISettings spiSettings) { - (void)spiSettings; - } - /** Set CS high. */ - void unselect() { - digitalWrite(m_csPin, HIGH); - } + explicit SdSpiConfig(SdCsPin_t cs) : csPin(cs) {} - private: - uint8_t m_csPin; - SoftSPI m_spi; + /** Chip select pin. */ + const SdCsPin_t csPin; + /** Options */ + const uint8_t options = SHARED_SPI; + /** Max SCK frequency */ + const uint32_t maxSck = SD_SCK_MHZ(50); + /** SPI port */ + SpiPort_t* spiPort = nullptr; }; -#endif // ENABLE_SOFTWARE_SPI_CLASS || defined(DOXYGEN) -//----------------------------------------------------------------------------- -// Choose SPI driver for SdFat and SdFatEX classes. -#if USE_STANDARD_SPI_LIBRARY || !SD_HAS_CUSTOM_SPI -/** SdFat uses Arduino library SPI. */ -typedef SdSpiLibDriver SdFatSpiDriver; -#else // USE_STANDARD_SPI_LIBRARY || !SD_HAS_CUSTOM_SPI -/** SdFat uses custom fast SPI. */ -typedef SdSpiAltDriver SdFatSpiDriver; -#endif // USE_STANDARD_SPI_LIBRARY || !SD_HAS_CUSTOM_SPI - -/** typedef for for SdSpiCard class. */ -#if ENABLE_SOFTWARE_SPI_CLASS -// Need virtual driver. -typedef SdSpiBaseDriver SdSpiDriver; -#else // ENABLE_SOFTWARE_SPI_CLASS -// Don't need virtual driver. -typedef SdFatSpiDriver SdSpiDriver; -#endif // ENABLE_SOFTWARE_SPI_CLASS -//============================================================================= -// Use of in-line for AVR to save flash. -#ifdef __AVR__ -//------------------------------------------------------------------------------ -inline void SdSpiAltDriver::begin(uint8_t csPin) { - m_csPin = csPin; - pinMode(m_csPin, OUTPUT); - digitalWrite(m_csPin, HIGH); - SPI.begin(); -} -//------------------------------------------------------------------------------ -inline void SdSpiAltDriver::activate() { - SPI.beginTransaction(m_spiSettings); -} -//------------------------------------------------------------------------------ -inline void SdSpiAltDriver::deactivate() { - SPI.endTransaction(); -} -//------------------------------------------------------------------------------ -inline uint8_t SdSpiAltDriver::receive() { - SPDR = 0XFF; - while (!(SPSR & (1 << SPIF))) {} - return SPDR; -} -//------------------------------------------------------------------------------ -inline uint8_t SdSpiAltDriver::receive(uint8_t* buf, size_t n) { - if (n-- == 0) { - return 0; - } - SPDR = 0XFF; - for (size_t i = 0; i < n; i++) { - while (!(SPSR & (1 << SPIF))) {} - uint8_t b = SPDR; - SPDR = 0XFF; - buf[i] = b; - } - while (!(SPSR & (1 << SPIF))) {} - buf[n] = SPDR; - return 0; -} -//------------------------------------------------------------------------------ -inline void SdSpiAltDriver::send(uint8_t data) { - SPDR = data; - while (!(SPSR & (1 << SPIF))) {} -} -//------------------------------------------------------------------------------ -inline void SdSpiAltDriver::send(const uint8_t* buf , size_t n) { - if (n == 0) { - return; - } - SPDR = buf[0]; - if (n > 1) { - uint8_t b = buf[1]; - size_t i = 2; - while (1) { - while (!(SPSR & (1 << SPIF))) {} - SPDR = b; - if (i == n) { - break; - } - b = buf[i++]; - } - } - while (!(SPSR & (1 << SPIF))) {} -} -#endif // __AVR__ +#if SPI_DRIVER_SELECT < 2 +#include "SdSpiArduinoDriver.h" +#elif SPI_DRIVER_SELECT == 2 +#include "SdSpiSoftDriver.h" +#elif SPI_DRIVER_SELECT == 3 +#include "SdSpiBaseClass.h" +typedef SdSpiBaseClass SdSpiDriver; +#else // SPI_DRIVER_SELECT +#error Invalid SPI_DRIVER_SELECT +#endif // SPI_DRIVER_SELECT #endif // SdSpiDriver_h diff --git a/avr/cores/megacommand/SdFat/SpiDriver/SdSpiSAM3X.cpp b/avr/cores/megacommand/SdFat/SpiDriver/SdSpiDue.cpp similarity index 80% rename from avr/cores/megacommand/SdFat/SpiDriver/SdSpiSAM3X.cpp rename to avr/cores/megacommand/SdFat/SpiDriver/SdSpiDue.cpp index c8d79d935..b1c1c776c 100644 --- a/avr/cores/megacommand/SdFat/SpiDriver/SdSpiSAM3X.cpp +++ b/avr/cores/megacommand/SdFat/SpiDriver/SdSpiDue.cpp @@ -1,5 +1,5 @@ /** - * Copyright (c) 20011-2017 Bill Greiman + * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -23,66 +23,45 @@ * DEALINGS IN THE SOFTWARE. */ #include "SdSpiDriver.h" -#if defined(__SAM3X8E__) || defined(__SAM3X8H__) -/** Use SAM3X DMAC if nonzero */ +#if defined(SD_USE_CUSTOM_SPI) && defined(ARDUINO_SAM_DUE) +/* Use SAM3X DMAC if nonzero */ #define USE_SAM3X_DMAC 1 -/** Use extra Bus Matrix arbitration fix if nonzero */ -#define USE_SAM3X_BUS_MATRIX_FIX 0 -/** Time in ms for DMA receive timeout */ +/* Use extra Bus Matrix arbitration fix if nonzero */ +#define USE_SAM3X_BUS_MATRIX_FIX 1 +/* Time in ms for DMA receive timeout */ #define SAM3X_DMA_TIMEOUT 100 -/** chip select register number */ +/* chip select register number */ #define SPI_CHIP_SEL 3 -/** DMAC receive channel */ -#define SPI_DMAC_RX_CH 1 -/** DMAC transmit channel */ -#define SPI_DMAC_TX_CH 0 -/** DMAC Channel HW Interface Number for SPI TX. */ -#define SPI_TX_IDX 1 -/** DMAC Channel HW Interface Number for SPI RX. */ -#define SPI_RX_IDX 2 +/* DMAC receive channel */ +#define SPI_DMAC_RX_CH 1 +/* DMAC transmit channel */ +#define SPI_DMAC_TX_CH 0 +/* DMAC Channel HW Interface Number for SPI TX. */ +#define SPI_TX_IDX 1 +/* DMAC Channel HW Interface Number for SPI RX. */ +#define SPI_RX_IDX 2 //------------------------------------------------------------------------------ -/** Disable DMA Controller. */ +/* Disable DMA Controller. */ static void dmac_disable() { DMAC->DMAC_EN &= (~DMAC_EN_ENABLE); } -/** Enable DMA Controller. */ +/* Enable DMA Controller. */ static void dmac_enable() { DMAC->DMAC_EN = DMAC_EN_ENABLE; } -/** Disable DMA Channel. */ +/* Disable DMA Channel. */ static void dmac_channel_disable(uint32_t ul_num) { DMAC->DMAC_CHDR = DMAC_CHDR_DIS0 << ul_num; } -/** Enable DMA Channel. */ +/* Enable DMA Channel. */ static void dmac_channel_enable(uint32_t ul_num) { DMAC->DMAC_CHER = DMAC_CHER_ENA0 << ul_num; } -/** Poll for transfer complete. */ +/* Poll for transfer complete. */ static bool dmac_channel_transfer_done(uint32_t ul_num) { return (DMAC->DMAC_CHSR & (DMAC_CHSR_ENA0 << ul_num)) ? false : true; } //------------------------------------------------------------------------------ -void SdSpiAltDriver::begin(uint8_t csPin) { - m_csPin = csPin; - pinMode(m_csPin, OUTPUT); - digitalWrite(m_csPin, HIGH); -SPI.begin(); -#if USE_SAM3X_DMAC - pmc_enable_periph_clk(ID_DMAC); - dmac_disable(); - DMAC->DMAC_GCFG = DMAC_GCFG_ARB_CFG_FIXED; - dmac_enable(); -#if USE_SAM3X_BUS_MATRIX_FIX - MATRIX->MATRIX_WPMR = 0x4d415400; - MATRIX->MATRIX_MCFG[1] = 1; - MATRIX->MATRIX_MCFG[2] = 1; - MATRIX->MATRIX_SCFG[0] = 0x01000010; - MATRIX->MATRIX_SCFG[1] = 0x01000010; - MATRIX->MATRIX_SCFG[7] = 0x01000010; -#endif // USE_SAM3X_BUS_MATRIX_FIX -#endif // USE_SAM3X_DMAC -} -//------------------------------------------------------------------------------ // start RX DMA static void spiDmaRX(uint8_t* dst, uint16_t count) { dmac_channel_disable(SPI_DMAC_RX_CH); @@ -125,7 +104,7 @@ static void spiDmaTX(const uint8_t* src, uint16_t count) { } //------------------------------------------------------------------------------ // initialize SPI controller -void SdSpiAltDriver::activate() { +void SdSpiArduinoDriver::activate() { SPI.beginTransaction(m_spiSettings); Spi* pSpi = SPI0; @@ -143,10 +122,33 @@ void SdSpiAltDriver::activate() { pSpi->SPI_CR |= SPI_CR_SPIEN; } //------------------------------------------------------------------------------ -void SdSpiAltDriver::deactivate() { +void SdSpiArduinoDriver::begin(SdSpiConfig spiConfig) { + (void)spiConfig; + SPI.begin(); +#if USE_SAM3X_DMAC + pmc_enable_periph_clk(ID_DMAC); + dmac_disable(); + DMAC->DMAC_GCFG = DMAC_GCFG_ARB_CFG_FIXED; + dmac_enable(); +#if USE_SAM3X_BUS_MATRIX_FIX + MATRIX->MATRIX_WPMR = 0x4d415400; + MATRIX->MATRIX_MCFG[1] = 1; + MATRIX->MATRIX_MCFG[2] = 1; + MATRIX->MATRIX_SCFG[0] = 0x01000010; + MATRIX->MATRIX_SCFG[1] = 0x01000010; + MATRIX->MATRIX_SCFG[7] = 0x01000010; +#endif // USE_SAM3X_BUS_MATRIX_FIX +#endif // USE_SAM3X_DMAC +} +//------------------------------------------------------------------------------ +void SdSpiArduinoDriver::deactivate() { SPI.endTransaction(); } //------------------------------------------------------------------------------ +void SdSpiArduinoDriver::end() { + SPI.end(); +} +//------------------------------------------------------------------------------ static inline uint8_t spiTransfer(uint8_t b) { Spi* pSpi = SPI0; @@ -156,21 +158,18 @@ static inline uint8_t spiTransfer(uint8_t b) { return b; } //------------------------------------------------------------------------------ -/** SPI receive a byte */ -uint8_t SdSpiAltDriver::receive() { +uint8_t SdSpiArduinoDriver::receive() { return spiTransfer(0XFF); } //------------------------------------------------------------------------------ -/** SPI receive multiple bytes */ -uint8_t SdSpiAltDriver::receive(uint8_t* buf, size_t n) { +uint8_t SdSpiArduinoDriver::receive(uint8_t* buf, size_t count) { Spi* pSpi = SPI0; int rtn = 0; #if USE_SAM3X_DMAC // clear overrun error - uint32_t s = pSpi->SPI_SR; - - spiDmaRX(buf, n); - spiDmaTX(0, n); + while (pSpi->SPI_SR & (SPI_SR_OVRES | SPI_SR_RDRF)) {pSpi->SPI_RDR;} + spiDmaRX(buf, count); + spiDmaTX(0, count); uint32_t m = millis(); while (!dmac_channel_transfer_done(SPI_DMAC_RX_CH)) { @@ -185,7 +184,7 @@ uint8_t SdSpiAltDriver::receive(uint8_t* buf, size_t n) { rtn |= 1; } #else // USE_SAM3X_DMAC - for (size_t i = 0; i < n; i++) { + for (size_t i = 0; i < count; i++) { pSpi->SPI_TDR = 0XFF; while ((pSpi->SPI_SR & SPI_SR_RDRF) == 0) {} buf[i] = pSpi->SPI_RDR; @@ -194,25 +193,24 @@ uint8_t SdSpiAltDriver::receive(uint8_t* buf, size_t n) { return rtn; } //------------------------------------------------------------------------------ -/** SPI send a byte */ -void SdSpiAltDriver::send(uint8_t b) { - spiTransfer(b); +void SdSpiArduinoDriver::send(uint8_t data) { + spiTransfer(data); } //------------------------------------------------------------------------------ -void SdSpiAltDriver::send(const uint8_t* buf , size_t n) { +void SdSpiArduinoDriver::send(const uint8_t* buf , size_t count) { Spi* pSpi = SPI0; #if USE_SAM3X_DMAC - spiDmaTX(buf, n); + spiDmaTX(buf, count); while (!dmac_channel_transfer_done(SPI_DMAC_TX_CH)) {} #else // #if USE_SAM3X_DMAC while ((pSpi->SPI_SR & SPI_SR_TXEMPTY) == 0) {} - for (size_t i = 0; i < n; i++) { + for (size_t i = 0; i < count; i++) { pSpi->SPI_TDR = buf[i]; while ((pSpi->SPI_SR & SPI_SR_TDRE) == 0) {} } #endif // #if USE_SAM3X_DMAC while ((pSpi->SPI_SR & SPI_SR_TXEMPTY) == 0) {} // leave RDR empty - uint8_t b = pSpi->SPI_RDR; + while (pSpi->SPI_SR & (SPI_SR_OVRES | SPI_SR_RDRF)) {pSpi->SPI_RDR;} } -#endif // defined(__SAM3X8E__) || defined(__SAM3X8H__) +#endif // defined(SD_USE_CUSTOM_SPI) && defined(ARDUINO_SAM_DUE) diff --git a/avr/cores/megacommand/SdFat/SpiDriver/SdSpiESP.cpp b/avr/cores/megacommand/SdFat/SpiDriver/SdSpiESP.cpp new file mode 100644 index 000000000..04bee7d90 --- /dev/null +++ b/avr/cores/megacommand/SdFat/SpiDriver/SdSpiESP.cpp @@ -0,0 +1,96 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "SdSpiDriver.h" +#if defined(SD_USE_CUSTOM_SPI) && (defined(ESP8266) || defined(ESP32)) +#define ESP_UNALIGN_OK 1 +//------------------------------------------------------------------------------ +void SdSpiArduinoDriver::activate() { + m_spi->beginTransaction(m_spiSettings); +} +//------------------------------------------------------------------------------ +void SdSpiArduinoDriver::begin(SdSpiConfig spiConfig) { + if (spiConfig.spiPort) { + m_spi = spiConfig.spiPort; +#if defined(SDCARD_SPI) && defined(SDCARD_SS_PIN) + } else if (spiConfig.csPin == SDCARD_SS_PIN) { + m_spi = &SDCARD_SPI; +#endif // defined(SDCARD_SPI) && defined(SDCARD_SS_PIN) + } else { + m_spi = &SPI; + } + m_spi->begin(); +} +//------------------------------------------------------------------------------ +void SdSpiArduinoDriver::deactivate() { + m_spi->endTransaction(); +} +//------------------------------------------------------------------------------ +void SdSpiArduinoDriver::end() { + m_spi->end(); +} +//------------------------------------------------------------------------------ +uint8_t SdSpiArduinoDriver::receive() { + return m_spi->transfer(0XFF); +} +//------------------------------------------------------------------------------ +uint8_t SdSpiArduinoDriver::receive(uint8_t* buf, size_t count) { +#if ESP_UNALIGN_OK + m_spi->transferBytes(nullptr, buf, count); +#else // ESP_UNALIGN_OK + // Adjust to 32-bit alignment. + while ((reinterpret_cast(buf) & 0X3) && count) { + *buf++ = m_spi->transfer(0xff); + count--; + } + // Do multiple of four byte transfers. + size_t n4 = 4*(count/4); + if (n4) { + m_spi->transferBytes(nullptr, buf, n4); + } + // Transfer up to three remaining bytes. + for (buf += n4, count -= n4; count; count--) { + *buf++ = m_spi->transfer(0xff); + } +#endif // ESP_UNALIGN_OK + return 0; +} +//------------------------------------------------------------------------------ +void SdSpiArduinoDriver::send(uint8_t data) { + m_spi->transfer(data); +} +//------------------------------------------------------------------------------ +void SdSpiArduinoDriver::send(const uint8_t* buf , size_t count) { +#if !ESP_UNALIGN_OK + // Adjust to 32-bit alignment. + while ((reinterpret_cast(buf) & 0X3) && count) { + SPI.transfer(*buf++); + count--; + } +#endif // #if ESP_UNALIGN_OK + + m_spi->transferBytes(const_cast(buf), nullptr, count); +} +#endif // defined(SD_USE_CUSTOM_SPI) && (defined(ESP8266) || defined(ESP32)) diff --git a/avr/cores/megacommand/SdFat/SpiDriver/SdSpiLibDriver.h b/avr/cores/megacommand/SdFat/SpiDriver/SdSpiLibDriver.h new file mode 100644 index 000000000..05b102100 --- /dev/null +++ b/avr/cores/megacommand/SdFat/SpiDriver/SdSpiLibDriver.h @@ -0,0 +1,90 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +/** + * \file + * \brief Class using only simple SPI library functions. + */ +#ifndef SdSpiLibDriver_h +#define SdSpiLibDriver_h +//------------------------------------------------------------------------------ +inline void SdSpiArduinoDriver::activate() { + m_spi->beginTransaction(m_spiSettings); +} +//------------------------------------------------------------------------------ +inline void SdSpiArduinoDriver::begin(SdSpiConfig spiConfig) { + if (spiConfig.spiPort) { + m_spi = spiConfig.spiPort; +#if defined(SDCARD_SPI) && defined(SDCARD_SS_PIN) + } else if (spiConfig.csPin == SDCARD_SS_PIN) { + m_spi = &SDCARD_SPI; +#endif // defined(SDCARD_SPI) && defined(SDCARD_SS_PIN) + } else { + m_spi = &SPI; + } + m_spi->begin(); +} +//------------------------------------------------------------------------------ +inline void SdSpiArduinoDriver::end() { + m_spi->end(); +} +//------------------------------------------------------------------------------ +inline void SdSpiArduinoDriver::deactivate() { + m_spi->endTransaction(); +} +//------------------------------------------------------------------------------ +inline uint8_t SdSpiArduinoDriver::receive() { + return m_spi->transfer( 0XFF); +} +//------------------------------------------------------------------------------ +inline uint8_t SdSpiArduinoDriver::receive(uint8_t* buf, size_t count) { +#if USE_SPI_ARRAY_TRANSFER + memset(buf, 0XFF, count); + m_spi->transfer(buf, count); +#else // USE_SPI_ARRAY_TRANSFER + for (size_t i = 0; i < count; i++) { + buf[i] = m_spi->transfer(0XFF); + } +#endif // USE_SPI_ARRAY_TRANSFER + return 0; +} +//------------------------------------------------------------------------------ +inline void SdSpiArduinoDriver::send(uint8_t data) { + m_spi->transfer(data); +} +//------------------------------------------------------------------------------ +inline void SdSpiArduinoDriver::send(const uint8_t* buf, size_t count) { +#if USE_SPI_ARRAY_TRANSFER + if (count <= 512) { + uint8_t tmp[512]; + memcpy(tmp, buf, count); + m_spi->transfer(tmp, count); + } +#else // USE_SPI_ARRAY_TRANSFER + for (size_t i = 0; i < count; i++) { + m_spi->transfer(buf[i]); + } +#endif // USE_SPI_ARRAY_TRANSFER +} +#endif // SdSpiLibDriver_h diff --git a/avr/cores/megacommand/SdFat/SpiDriver/SdSpiParticle.cpp b/avr/cores/megacommand/SdFat/SpiDriver/SdSpiParticle.cpp new file mode 100644 index 000000000..b921f3d44 --- /dev/null +++ b/avr/cores/megacommand/SdFat/SpiDriver/SdSpiParticle.cpp @@ -0,0 +1,78 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "SdSpiDriver.h" +#if defined(SD_USE_CUSTOM_SPI) && defined(PLATFORM_ID) +static volatile bool SPI_DMA_TransferCompleted = false; +//----------------------------------------------------------------------------- +static void SD_SPI_DMA_TransferComplete_Callback() { + SPI_DMA_TransferCompleted = true; +} +//------------------------------------------------------------------------------ +void SdSpiArduinoDriver::activate() { + m_spi->beginTransaction(m_spiSettings); +} +//------------------------------------------------------------------------------ +void SdSpiArduinoDriver::begin(SdSpiConfig spiConfig) { + if (spiConfig.spiPort) { + m_spi = spiConfig.spiPort; + } else { + m_spi = &SPI; + } + m_spi->begin(); +} +//------------------------------------------------------------------------------ +void SdSpiArduinoDriver::deactivate() { + m_spi->endTransaction(); +} +//------------------------------------------------------------------------------ +void SdSpiArduinoDriver::end() { + m_spi->end(); +} +//------------------------------------------------------------------------------ +uint8_t SdSpiArduinoDriver::receive() { + return m_spi->transfer(0XFF); +} +//------------------------------------------------------------------------------ +uint8_t SdSpiArduinoDriver::receive(uint8_t* buf, size_t count) { + SPI_DMA_TransferCompleted = false; + m_spi->transfer(nullptr, buf, count, SD_SPI_DMA_TransferComplete_Callback); + while (!SPI_DMA_TransferCompleted) {} + return 0; +} +//------------------------------------------------------------------------------ +void SdSpiArduinoDriver::send(uint8_t data) { + m_spi->transfer(data); +} +//------------------------------------------------------------------------------ +void SdSpiArduinoDriver::send(const uint8_t* buf , size_t count) { + SPI_DMA_TransferCompleted = false; + + m_spi->transfer(const_cast(buf), nullptr, count, + SD_SPI_DMA_TransferComplete_Callback); + + while (!SPI_DMA_TransferCompleted) {} +} +#endif // defined(SD_USE_CUSTOM_SPI) && defined(PLATFORM_ID) + diff --git a/avr/cores/megacommand/SdFat/SpiDriver/SdSpiSTM32.cpp b/avr/cores/megacommand/SdFat/SpiDriver/SdSpiSTM32.cpp index b7f6d6bf0..9acfdd414 100644 --- a/avr/cores/megacommand/SdFat/SpiDriver/SdSpiSTM32.cpp +++ b/avr/cores/megacommand/SdFat/SpiDriver/SdSpiSTM32.cpp @@ -1,5 +1,5 @@ /** - * Copyright (c) 20011-2017 Bill Greiman + * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -22,8 +22,10 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ -#if defined(__STM32F1__) || defined(__STM32F4__) +// Driver for: https://github.com/rogerclarkmelbourne/Arduino_STM32 #include "SdSpiDriver.h" +#if defined(SD_USE_CUSTOM_SPI)\ + && (defined(__STM32F1__) || defined(__STM32F4__)) #if defined(__STM32F1__) #define USE_STM32_DMA 1 #elif defined(__STM32F4__) @@ -32,99 +34,49 @@ #error Unknown STM32 type #endif // defined(__STM32F1__) //------------------------------------------------------------------------------ -static SPIClass m_SPI1(1); -#if BOARD_NR_SPI >= 2 -static SPIClass m_SPI2(2); -#endif // BOARD_NR_SPI >= 2 -#if BOARD_NR_SPI >= 3 -static SPIClass m_SPI3(3); -#endif // BOARD_NR_SPI >= 3 -#if BOARD_NR_SPI > 3 -#error BOARD_NR_SPI too large -#endif -//------------------------------------------------------------------------------ -/** Set SPI options for access to SD/SDHC cards. - * - * \param[in] divisor SCK clock divider relative to the APB1 or APB2 clock. - */ -void SdSpiAltDriver::activate() { +void SdSpiArduinoDriver::activate() { m_spi->beginTransaction(m_spiSettings); } //------------------------------------------------------------------------------ -/** Initialize the SPI bus. - * - * \param[in] chipSelectPin SD card chip select pin. - */ -void SdSpiAltDriver::begin(uint8_t csPin) { - m_csPin = csPin; - pinMode(m_csPin, OUTPUT); - digitalWrite(m_csPin, HIGH); +void SdSpiArduinoDriver::begin(SdSpiConfig spiConfig) { + if (spiConfig.spiPort) { + m_spi = spiConfig.spiPort; + } else { + m_spi = &SPI; + } m_spi->begin(); } //------------------------------------------------------------------------------ -/** - * End SPI transaction. - */ -void SdSpiAltDriver::deactivate() { +void SdSpiArduinoDriver::deactivate() { m_spi->endTransaction(); } //------------------------------------------------------------------------------ -/** Receive a byte. - * - * \return The byte. - */ -uint8_t SdSpiAltDriver::receive() { +void SdSpiArduinoDriver::end() { + m_spi->end(); +} +//------------------------------------------------------------------------------ +uint8_t SdSpiArduinoDriver::receive() { return m_spi->transfer(0XFF); } //------------------------------------------------------------------------------ -/** Receive multiple bytes. - * - * \param[out] buf Buffer to receive the data. - * \param[in] n Number of bytes to receive. - * - * \return Zero for no error or nonzero error code. - */ -uint8_t SdSpiAltDriver::receive(uint8_t* buf, size_t n) { +uint8_t SdSpiArduinoDriver::receive(uint8_t* buf, size_t count) { #if USE_STM32_DMA - return m_spi->dmaTransfer(0, buf, n); + return m_spi->dmaTransfer(nullptr, buf, count); #else // USE_STM32_DMA - m_spi->read(buf, n); + m_spi->read(buf, count); return 0; #endif // USE_STM32_DMA } //------------------------------------------------------------------------------ -/** Send a byte. - * - * \param[in] b Byte to send - */ -void SdSpiAltDriver::send(uint8_t b) { - m_spi->transfer(b); +void SdSpiArduinoDriver::send(uint8_t data) { + m_spi->transfer(data); } //------------------------------------------------------------------------------ -/** Send multiple bytes. - * - * \param[in] buf Buffer for data to be sent. - * \param[in] n Number of bytes to send. - */ -void SdSpiAltDriver::send(const uint8_t* buf , size_t n) { +void SdSpiArduinoDriver::send(const uint8_t* buf , size_t count) { #if USE_STM32_DMA - m_spi->dmaTransfer(const_cast(buf), 0, n); + m_spi->dmaTransfer(const_cast(buf), nullptr, count); #else // USE_STM32_DMA - m_spi->write(const_cast(buf), n); + m_spi->write(const_cast(buf), count); #endif // USE_STM32_DMA } -//------------------------------------------------------------------------------ -void SdSpiAltDriver::setPort(uint8_t portNumber) { - m_spi = &m_SPI1; -#if BOARD_NR_SPI >= 2 - if (portNumber == 2) { - m_spi = &m_SPI2; - } -#endif // BOARD_NR_SPI >= 2 -#if BOARD_NR_SPI >= 3 - if (portNumber == 3) { - m_spi = &m_SPI3; - } -#endif // BOARD_NR_SPI >= 2 -} -#endif // defined(__STM32F1__) || defined(__STM32F4__) +#endif // defined(SD_USE_CUSTOM_SPI) && defined(__STM32F1__) diff --git a/avr/cores/megacommand/SdFat/SpiDriver/SdSpiESP8266.cpp b/avr/cores/megacommand/SdFat/SpiDriver/SdSpiSTM32Core.cpp similarity index 55% rename from avr/cores/megacommand/SdFat/SpiDriver/SdSpiESP8266.cpp rename to avr/cores/megacommand/SdFat/SpiDriver/SdSpiSTM32Core.cpp index b70867bce..0f0bb3d82 100644 --- a/avr/cores/megacommand/SdFat/SpiDriver/SdSpiESP8266.cpp +++ b/avr/cores/megacommand/SdFat/SpiDriver/SdSpiSTM32Core.cpp @@ -1,5 +1,5 @@ /** - * Copyright (c) 20011-2017 Bill Greiman + * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -22,72 +22,54 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ -#if defined(ESP8266) +// Driver for: https://github.com/stm32duino/Arduino_Core_STM32 #include "SdSpiDriver.h" +#if defined(SD_USE_CUSTOM_SPI) && defined(STM32_CORE_VERSION) //------------------------------------------------------------------------------ -/** Initialize the SPI bus. - * - * \param[in] chipSelectPin SD card chip select pin. - */ -void SdSpiAltDriver::begin(uint8_t csPin) { - m_csPin = csPin; - pinMode(m_csPin, OUTPUT); - digitalWrite(m_csPin, HIGH); - SPI.begin(); +void SdSpiArduinoDriver::activate() { + m_spi->beginTransaction(m_spiSettings); } //------------------------------------------------------------------------------ -/** Set SPI options for access to SD/SDHC cards. - * - */ -void SdSpiAltDriver::activate() { - SPI.beginTransaction(m_spiSettings); +void SdSpiArduinoDriver::begin(SdSpiConfig spiConfig) { + if (spiConfig.spiPort) { + m_spi = spiConfig.spiPort; + } else { + m_spi = &SPI; + } + m_spi->begin(); } //------------------------------------------------------------------------------ -void SdSpiAltDriver::deactivate() { - // Note: endTransaction is an empty function on ESP8266. - SPI.endTransaction(); +void SdSpiArduinoDriver::deactivate() { + m_spi->endTransaction(); } //------------------------------------------------------------------------------ -/** Receive a byte. - * - * \return The byte. - */ -uint8_t SdSpiAltDriver::receive() { - return SPI.transfer(0XFF); +void SdSpiArduinoDriver::end() { + m_spi->end(); } //------------------------------------------------------------------------------ -/** Receive multiple bytes. - * - * \param[out] buf Buffer to receive the data. - * \param[in] n Number of bytes to receive. - * - * \return Zero for no error or nonzero error code. - */ -uint8_t SdSpiAltDriver::receive(uint8_t* buf, size_t n) { - // Works without 32-bit alignment of buf. - SPI.transferBytes(0, buf, n); +uint8_t SdSpiArduinoDriver::receive() { + return m_spi->transfer(0XFF); +} +//------------------------------------------------------------------------------ +uint8_t SdSpiArduinoDriver::receive(uint8_t* buf, size_t count) { + // Must send 0XFF - SD looks at send data for command. + memset(buf, 0XFF, count); + m_spi->transfer(buf, count); return 0; } //------------------------------------------------------------------------------ -/** Send a byte. - * - * \param[in] b Byte to send - */ -void SdSpiAltDriver::send(uint8_t b) { - SPI.transfer(b); +void SdSpiArduinoDriver::send(uint8_t data) { + m_spi->transfer(data); } //------------------------------------------------------------------------------ -/** Send multiple bytes. - * - * \param[in] buf Buffer for data to be sent. - * \param[in] n Number of bytes to send. - */ -void SdSpiAltDriver::send(const uint8_t* buf , size_t n) { - // Adjust to 32-bit alignment. - while ((reinterpret_cast(buf) & 0X3) && n) { - SPI.transfer(*buf++); - n--; +void SdSpiArduinoDriver::send(const uint8_t* buf, size_t count) { + // Avoid stack overflow if bad count. This should cause a write error. + if (count > 512) { + return; } - SPI.transferBytes(const_cast(buf), 0, n); + // Not easy to avoid receive so use tmp RX buffer. + uint8_t rxBuf[512]; + // Discard const - STM32 not const correct. + m_spi->transfer(const_cast(buf), rxBuf, count); } -#endif // defined(ESP8266) +#endif // defined(SD_USE_CUSTOM_SPI) && defined(STM32_CORE_VERSION) diff --git a/avr/cores/megacommand/SdFat/SpiDriver/SdSpiSoftDriver.h b/avr/cores/megacommand/SdFat/SpiDriver/SdSpiSoftDriver.h new file mode 100644 index 000000000..2839c4bb6 --- /dev/null +++ b/avr/cores/megacommand/SdFat/SpiDriver/SdSpiSoftDriver.h @@ -0,0 +1,121 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +/** + * \file + * \brief Class for software SPI. + */ +#ifndef SdSpiSoftDriver_h +#define SdSpiSoftDriver_h +#include "../DigitalIO/SoftSPI.h" +/** + * \class SdSpiSoftDriver + * \brief Base class for external soft SPI. + */ +class SdSpiSoftDriver { + public: + /** Activate SPI hardware. */ + void activate() {} + /** Initialize the SPI bus. */ + virtual void begin() = 0; + /** Initialize the SPI bus. + * + * \param[in] spiConfig SD card configuration. + */ + void begin(SdSpiConfig spiConfig) { + (void)spiConfig; + begin(); + } + /** Deactivate SPI hardware. */ + void deactivate() {} + /** deactivate SPI driver. */ + void end() {} + /** Receive a byte. + * + * \return The byte. + */ + virtual uint8_t receive() = 0; + /** Receive multiple bytes. + * + * \param[out] buf Buffer to receive the data. + * \param[in] count Number of bytes to receive. + * + * \return Zero for no error or nonzero error code. + */ + uint8_t receive(uint8_t* buf, size_t count) { + for (size_t i = 0; i < count; i++) { + buf[i] = receive(); + } + return 0; + } + /** Send a byte. + * + * \param[in] data Byte to send + */ + virtual void send(uint8_t data) = 0; + /** Send multiple bytes. + * + * \param[in] buf Buffer for data to be sent. + * \param[in] count Number of bytes to send. + */ + void send(const uint8_t* buf, size_t count) { + for (size_t i = 0; i < count; i++) { + send(buf[i]); + } + } + /** Save high speed SPISettings after SD initialization. + * + * \param[in] maxSck Maximum SCK frequency. + */ + void setSckSpeed(uint32_t maxSck) { + (void)maxSck; + } +}; +//------------------------------------------------------------------------------ +/** + * \class SoftSpiDriver + * \brief Class for external soft SPI. + */ +template +class SoftSpiDriver : public SdSpiSoftDriver { + public: + /** Initialize the SPI bus. */ + void begin() {m_spi.begin();} + /** Receive a byte. + * + * \return The byte. + */ + uint8_t receive() {return m_spi.receive();} + /** Send a byte. + * + * \param[in] data Byte to send + */ + void send(uint8_t data) {m_spi.send(data);} + private: + SoftSPI m_spi; +}; + +/** Typedef for use of SdSoftSpiDriver */ +typedef SdSpiSoftDriver SdSpiDriver; +#endif // SdSpiSoftDriver_h diff --git a/avr/cores/megacommand/SdFat/SpiDriver/SdSpiTeensy3.cpp b/avr/cores/megacommand/SdFat/SpiDriver/SdSpiTeensy3.cpp index 8e13d6ab9..c0a5b2c3f 100644 --- a/avr/cores/megacommand/SdFat/SpiDriver/SdSpiTeensy3.cpp +++ b/avr/cores/megacommand/SdFat/SpiDriver/SdSpiTeensy3.cpp @@ -1,5 +1,5 @@ /** - * Copyright (c) 20011-2017 Bill Greiman + * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -23,211 +23,68 @@ * DEALINGS IN THE SOFTWARE. */ #include "SdSpiDriver.h" -#if defined(__arm__) && defined(CORE_TEENSY) -// SPI definitions -#include "kinetis.h" - +#if defined(SD_USE_CUSTOM_SPI) && defined(__arm__) && defined(CORE_TEENSY) +#define USE_BLOCK_TRANSFER 1 //------------------------------------------------------------------------------ -void SdSpiAltDriver::activate() { - SPI.beginTransaction(m_spiSettings); +void SdSpiArduinoDriver::activate() { + m_spi->beginTransaction(m_spiSettings); } //------------------------------------------------------------------------------ -void SdSpiAltDriver::begin(uint8_t chipSelectPin) { - m_csPin = chipSelectPin; - pinMode(m_csPin, OUTPUT); - digitalWrite(m_csPin, HIGH); - SPI.begin(); +void SdSpiArduinoDriver::begin(SdSpiConfig spiConfig) { + if (spiConfig.spiPort) { + m_spi = spiConfig.spiPort; +#if defined(SDCARD_SPI) && defined(SDCARD_SS_PIN) + } else if (spiConfig.csPin == SDCARD_SS_PIN) { + m_spi = &SDCARD_SPI; + m_spi->setMISO(SDCARD_MISO_PIN); + m_spi->setMOSI(SDCARD_MOSI_PIN); + m_spi->setSCK(SDCARD_SCK_PIN); +#endif // defined(SDCARD_SPI) && defined(SDCARD_SS_PIN) + } else { + m_spi = &SPI; + } + m_spi->begin(); } //------------------------------------------------------------------------------ -void SdSpiAltDriver::deactivate() { - SPI.endTransaction(); +void SdSpiArduinoDriver::deactivate() { + m_spi->endTransaction(); } -//============================================================================== -#ifdef KINETISK - -// use 16-bit frame if SPI_USE_8BIT_FRAME is zero -#define SPI_USE_8BIT_FRAME 0 -// Limit initial fifo to three entries to avoid fifo overrun -#define SPI_INITIAL_FIFO_DEPTH 3 -// define some symbols that are not in mk20dx128.h -#ifndef SPI_SR_RXCTR -#define SPI_SR_RXCTR 0XF0 -#endif // SPI_SR_RXCTR -#ifndef SPI_PUSHR_CONT -#define SPI_PUSHR_CONT 0X80000000 -#endif // SPI_PUSHR_CONT -#ifndef SPI_PUSHR_CTAS -#define SPI_PUSHR_CTAS(n) (((n) & 7) << 28) -#endif // SPI_PUSHR_CTAS //------------------------------------------------------------------------------ -/** SPI receive a byte */ -uint8_t SdSpiAltDriver::receive() { - SPI0_MCR |= SPI_MCR_CLR_RXF; - SPI0_SR = SPI_SR_TCF; - SPI0_PUSHR = 0xFF; - while (!(SPI0_SR & SPI_SR_TCF)) {} - return SPI0_POPR; +void SdSpiArduinoDriver::end() { + m_spi->end(); } //------------------------------------------------------------------------------ -/** SPI receive multiple bytes */ -uint8_t SdSpiAltDriver::receive(uint8_t* buf, size_t n) { - // clear any data in RX FIFO - SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_CLR_RXF | SPI_MCR_PCSIS(0x1F); -#if SPI_USE_8BIT_FRAME - // initial number of bytes to push into TX FIFO - int nf = n < SPI_INITIAL_FIFO_DEPTH ? n : SPI_INITIAL_FIFO_DEPTH; - for (int i = 0; i < nf; i++) { - SPI0_PUSHR = 0XFF; - } - // limit for pushing dummy data into TX FIFO - uint8_t* limit = buf + n - nf; - while (buf < limit) { - while (!(SPI0_SR & SPI_SR_RXCTR)) {} - SPI0_PUSHR = 0XFF; - *buf++ = SPI0_POPR; - } - // limit for rest of RX data - limit += nf; - while (buf < limit) { - while (!(SPI0_SR & SPI_SR_RXCTR)) {} - *buf++ = SPI0_POPR; - } -#else // SPI_USE_8BIT_FRAME - // use 16 bit frame to avoid TD delay between frames - // get one byte if n is odd - if (n & 1) { - *buf++ = receive(); - n--; - } - // initial number of words to push into TX FIFO - int nf = n/2 < SPI_INITIAL_FIFO_DEPTH ? n/2 : SPI_INITIAL_FIFO_DEPTH; - for (int i = 0; i < nf; i++) { - SPI0_PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | 0XFFFF; - } - uint8_t* limit = buf + n - 2*nf; - while (buf < limit) { - while (!(SPI0_SR & SPI_SR_RXCTR)) {} - SPI0_PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | 0XFFFF; - uint16_t w = SPI0_POPR; - *buf++ = w >> 8; - *buf++ = w & 0XFF; - } - // limit for rest of RX data - limit += 2*nf; - while (buf < limit) { - while (!(SPI0_SR & SPI_SR_RXCTR)) {} - uint16_t w = SPI0_POPR; - *buf++ = w >> 8; - *buf++ = w & 0XFF; - } -#endif // SPI_USE_8BIT_FRAME - return 0; +uint8_t SdSpiArduinoDriver::receive() { + return m_spi->transfer(0XFF); } //------------------------------------------------------------------------------ -/** SPI send a byte */ -void SdSpiAltDriver::send(uint8_t b) { - SPI0_MCR |= SPI_MCR_CLR_RXF; - SPI0_SR = SPI_SR_TCF; - SPI0_PUSHR = b; - while (!(SPI0_SR & SPI_SR_TCF)) {} +uint8_t SdSpiArduinoDriver::receive(uint8_t* buf, size_t count) { +#if USE_BLOCK_TRANSFER + memset(buf, 0XFF, count); + m_spi->transfer(buf, count); +#else // USE_BLOCK_TRANSFER + for (size_t i = 0; i < count; i++) { + buf[i] = m_spi->transfer(0XFF); + } +#endif // USE_BLOCK_TRANSFER + return 0; } //------------------------------------------------------------------------------ -/** SPI send multiple bytes */ -void SdSpiAltDriver::send(const uint8_t* buf , size_t n) { - // clear any data in RX FIFO - SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_CLR_RXF | SPI_MCR_PCSIS(0x1F); -#if SPI_USE_8BIT_FRAME - // initial number of bytes to push into TX FIFO - int nf = n < SPI_INITIAL_FIFO_DEPTH ? n : SPI_INITIAL_FIFO_DEPTH; - // limit for pushing data into TX fifo - const uint8_t* limit = buf + n; - for (int i = 0; i < nf; i++) { - SPI0_PUSHR = *buf++; - } - // write data to TX FIFO - while (buf < limit) { - while (!(SPI0_SR & SPI_SR_RXCTR)) {} - SPI0_PUSHR = *buf++; - SPI0_POPR; - } - // wait for data to be sent - while (nf) { - while (!(SPI0_SR & SPI_SR_RXCTR)) {} - SPI0_POPR; - nf--; - } -#else // SPI_USE_8BIT_FRAME - // use 16 bit frame to avoid TD delay between frames - // send one byte if n is odd - if (n & 1) { - send(*buf++); - n--; - } - // initial number of words to push into TX FIFO - int nf = n/2 < SPI_INITIAL_FIFO_DEPTH ? n/2 : SPI_INITIAL_FIFO_DEPTH; - // limit for pushing data into TX fifo - const uint8_t* limit = buf + n; - for (int i = 0; i < nf; i++) { - uint16_t w = (*buf++) << 8; - w |= *buf++; - SPI0_PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | w; - } - // write data to TX FIFO - while (buf < limit) { - uint16_t w = *buf++ << 8; - w |= *buf++; - while (!(SPI0_SR & SPI_SR_RXCTR)) {} - SPI0_PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | w; - SPI0_POPR; - } - // wait for data to be sent - while (nf) { - while (!(SPI0_SR & SPI_SR_RXCTR)) {} - SPI0_POPR; - nf--; - } -#endif // SPI_USE_8BIT_FRAME +void SdSpiArduinoDriver::send(uint8_t data) { + m_spi->transfer(data); } -#else // KINETISK -//============================================================================== -// Use standard SPI library if not KINETISK //------------------------------------------------------------------------------ -/** Receive a byte. - * - * \return The byte. - */ -uint8_t SdSpiAltDriver::receive() { - return SPI.transfer(0XFF); -} -/** Receive multiple bytes. - * - * \param[out] buf Buffer to receive the data. - * \param[in] n Number of bytes to receive. - * - * \return Zero for no error or nonzero error code. - */ -uint8_t SdSpiAltDriver::receive(uint8_t* buf, size_t n) { - for (size_t i = 0; i < n; i++) { - buf[i] = SPI.transfer(0XFF); - } - return 0; -} -/** Send a byte. - * - * \param[in] b Byte to send - */ -void SdSpiAltDriver::send(uint8_t b) { - SPI.transfer(b); -} -/** Send multiple bytes. - * - * \param[in] buf Buffer for data to be sent. - * \param[in] n Number of bytes to send. - */ -void SdSpiAltDriver::send(const uint8_t* buf , size_t n) { - for (size_t i = 0; i < n; i++) { - SPI.transfer(buf[i]); +void SdSpiArduinoDriver::send(const uint8_t* buf , size_t count) { +#if USE_BLOCK_TRANSFER + uint32_t tmp[128]; + if (0 < count && count <= 512) { + memcpy(tmp, buf, count); + m_spi->transfer(tmp, count); + return; + } +#endif // USE_BLOCK_TRANSFER + for (size_t i = 0; i < count; i++) { + m_spi->transfer(buf[i]); } } -#endif // KINETISK -#endif // defined(__arm__) && defined(CORE_TEENSY) +#endif // defined(SD_USE_CUSTOM_SPI) && defined(__arm__) &&defined(CORE_TEENSY) diff --git a/avr/cores/megacommand/SdFat/common/ArduinoFiles.h b/avr/cores/megacommand/SdFat/common/ArduinoFiles.h new file mode 100644 index 000000000..75ee2920e --- /dev/null +++ b/avr/cores/megacommand/SdFat/common/ArduinoFiles.h @@ -0,0 +1,159 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef ArduinoFiles_h +#define ArduinoFiles_h +#include "SysCall.h" +//------------------------------------------------------------------------------ +/** Arduino SD.h style flag for open for read. */ +#ifndef FILE_READ +#define FILE_READ O_RDONLY +#endif // FILE_READ +/** Arduino SD.h style flag for open at EOF for read/write with create. */ +#ifndef FILE_WRITE +#define FILE_WRITE (O_RDWR | O_CREAT | O_AT_END) +#endif // FILE_WRITE +//------------------------------------------------------------------------------ +/** + * \class PrintFile + * \brief PrintFile class. + */ +template +class PrintFile : public print_t, public BaseFile { + public: + using BaseFile::clearWriteError; + using BaseFile::getWriteError; + using BaseFile::read; + using BaseFile::write; + /** Write a single byte. + * \param[in] b byte to write. + * \return one for success. + */ + size_t write(uint8_t b) { + return BaseFile::write(&b, 1); + } +}; +//------------------------------------------------------------------------------ +/** + * \class StreamFile + * \brief StreamFile class. + */ +template +class StreamFile : public stream_t, public BaseFile { + public: + using BaseFile::clearWriteError; + using BaseFile::getWriteError; + using BaseFile::read; + using BaseFile::write; + + StreamFile() {} + + /** \return number of bytes available from the current position to EOF + * or INT_MAX if more than INT_MAX bytes are available. + */ + int available() { + return BaseFile::available(); + } + /** Ensure that any bytes written to the file are saved to the SD card. */ + void flush() { + BaseFile::sync(); + } + /** This function reports if the current file is a directory or not. + * \return true if the file is a directory. + */ + bool isDirectory() { + return BaseFile::isDir(); + } + /** No longer implemented due to Long File Names. + * + * Use getName(char* name, size_t size). + * \return a pointer to replacement suggestion. + */ +#ifndef DOXYGEN_SHOULD_SKIP_THIS + char* __attribute__((error("use getName(name, size)"))) name(); +#endif // DOXYGEN_SHOULD_SKIP_THIS + /** Return the next available byte without consuming it. + * + * \return The byte if no error and not at eof else -1; + */ + int peek() { + return BaseFile::peek(); + } + /** \return the current file position. */ + PosType position() { + return BaseFile::curPosition(); + } + /** Read the next byte from a file. + * + * \return For success return the next byte in the file as an int. + * If an error occurs or end of file is reached return -1. + */ + int read() { + return BaseFile::read(); + } + /** Rewind a file if it is a directory */ + void rewindDirectory() { + if (BaseFile::isDir()) { + BaseFile::rewind(); + } + } + /** + * Seek to a new position in the file, which must be between + * 0 and the size of the file (inclusive). + * + * \param[in] pos the new file position. + * \return true for success or false for failure. + */ + bool seek(PosType pos) { + return BaseFile::seekSet(pos); + } + /** \return the file's size. */ + PosType size() { + return BaseFile::fileSize(); + } + /** Write a byte to a file. Required by the Arduino Print class. + * \param[in] b the byte to be written. + * Use getWriteError to check for errors. + * \return 1 for success and 0 for failure. + */ + size_t write(uint8_t b) { + return BaseFile::write(b); + } + /** Write data to an open file. + * + * \note Data is moved to the cache but may not be written to the + * storage device until sync() is called. + * + * \param[in] buffer Pointer to the location of the data to be written. + * + * \param[in] size Number of bytes to write. + * + * \return For success write() returns the number of bytes written, always + * \a size. + */ + size_t write(const uint8_t* buffer, size_t size) { + return BaseFile::write(buffer, size); + } +}; +#endif // ArduinoFiles_h diff --git a/avr/cores/megacommand/SdFat/common/CPPLINT.cfg b/avr/cores/megacommand/SdFat/common/CPPLINT.cfg new file mode 100644 index 000000000..f274762db --- /dev/null +++ b/avr/cores/megacommand/SdFat/common/CPPLINT.cfg @@ -0,0 +1,3 @@ +exclude_files=PrintBasic.cpp +exclude_files=PrintBasic.h +exclude_files=PrintTemplates.h \ No newline at end of file diff --git a/avr/cores/megacommand/SdFat/common/CompileDateTime.h b/avr/cores/megacommand/SdFat/common/CompileDateTime.h new file mode 100644 index 000000000..55f9f1fef --- /dev/null +++ b/avr/cores/megacommand/SdFat/common/CompileDateTime.h @@ -0,0 +1,74 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef CompileDateTime_h +#define CompileDateTime_h +// Note - these functions will compile to a few bytes +// since they are evaluated at compile time. + +/** \return year field of the __DATE__ macro. */ +constexpr uint16_t compileYear() { + return 1000*(__DATE__[7] - '0') + + 100*(__DATE__[8] - '0') + + 10*(__DATE__[9] - '0') + + (__DATE__[10] - '0'); +} +/** \return true if str equals the month field of the __DATE__ macro. */ +constexpr bool compileMonthIs(const char* str) { + return __DATE__[0] == str[0] + && __DATE__[1] == str[1] + && __DATE__[2] == str[2]; +} +/** \return month field of the __DATE__ macro. */ +constexpr uint8_t compileMonth() { + return compileMonthIs("Jan") ? 1 : + compileMonthIs("Feb") ? 2 : + compileMonthIs("Mar") ? 3 : + compileMonthIs("Apr") ? 4 : + compileMonthIs("May") ? 5 : + compileMonthIs("Jun") ? 6 : + compileMonthIs("Jul") ? 7 : + compileMonthIs("Aug") ? 8 : + compileMonthIs("Sep") ? 9 : + compileMonthIs("Oct") ? 10 : + compileMonthIs("Nov") ? 11 : + compileMonthIs("Dec") ? 12 : 0; +} +/** \return day field of the __DATE__ macro. */ +constexpr uint8_t compileDay() { + return 10*(__DATE__[4] == ' ' ? 0 : __DATE__[4] - '0') + (__DATE__[5] - '0'); +} +/** \return hour field of the __TIME__ macro. */ +constexpr uint8_t compileHour() { + return 10*(__TIME__[0] - '0') + __TIME__[1] - '0'; +} +/** \return minute field of the __TIME__ macro. */ +constexpr uint8_t compileMinute() { + return 10*(__TIME__[3] - '0') + __TIME__[4] - '0'; +} +/** \return second field of the __TIME__ macro. */ +constexpr uint8_t compileSecond() { + return 10*(__TIME__[6] - '0') + __TIME__[7] - '0'; +} +#endif // CompileDateTime_h diff --git a/avr/cores/megacommand/SdFat/common/DebugMacros.h b/avr/cores/megacommand/SdFat/common/DebugMacros.h new file mode 100644 index 000000000..af12e8aef --- /dev/null +++ b/avr/cores/megacommand/SdFat/common/DebugMacros.h @@ -0,0 +1,74 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef DebugMacros_h +#define DebugMacros_h +#include "SysCall.h" + +// 0 - disable, 1 - fail, halt 2 - fail, halt, warn +#define USE_DBG_MACROS 0 + +#if USE_DBG_MACROS +#include "Arduino.h" +#ifndef DBG_FILE +#error DBG_FILE not defined +#endif // DBG_FILE + +__attribute__((unused)) static void dbgFail(uint16_t line) { + Serial.print(F("DBG_FAIL: ")); + Serial.print(F(DBG_FILE)); + Serial.write('.'); + Serial.println(line); +} +__attribute__((unused)) static void dbgHalt(uint16_t line) { + Serial.print(F("DBG_HALT: ")); + Serial.print(F(DBG_FILE)); + Serial.write('.'); + Serial.println(line); + while (true) {} +} +#define DBG_FAIL_MACRO dbgFail(__LINE__) +#define DBG_HALT_MACRO dbgHalt(__LINE__) +#define DBG_HALT_IF(b) if (b) {dbgHalt(__LINE__);} + +#else // USE_DBG_MACROS +#define DBG_FAIL_MACRO +#define DBG_HALT_MACRO +#define DBG_HALT_IF(b) +#endif // USE_DBG_MACROS + +#if USE_DBG_MACROS > 1 +__attribute__((unused)) static void dbgWarn(uint16_t line) { + Serial.print(F("DBG_WARN: ")); + Serial.print(F(DBG_FILE)); + Serial.write('.'); + Serial.println(line); +} +#define DBG_WARN_MACRO dbgWarn(__LINE__) +#define DBG_WARN_IF(b) if (b) {dbgWarn(__LINE__);} +#else // USE_DBG_MACROS > 1 +#define DBG_WARN_MACRO +#define DBG_WARN_IF(b) +#endif // USE_DBG_MACROS > 1 +#endif // DebugMacros_h diff --git a/avr/cores/megacommand/SdFat/FatLib/FmtNumber.cpp b/avr/cores/megacommand/SdFat/common/FmtNumber.cpp similarity index 71% rename from avr/cores/megacommand/SdFat/FatLib/FmtNumber.cpp rename to avr/cores/megacommand/SdFat/common/FmtNumber.cpp index e16c47a6f..f77338ab5 100644 --- a/avr/cores/megacommand/SdFat/FatLib/FmtNumber.cpp +++ b/avr/cores/megacommand/SdFat/common/FmtNumber.cpp @@ -1,5 +1,5 @@ /** - * Copyright (c) 20011-2017 Bill Greiman + * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -23,6 +23,9 @@ * DEALINGS IN THE SOFTWARE. */ #include "FmtNumber.h" +// always use fmtBase10() - seems fast even on teensy 3.6. +#define USE_FMT_BASE10 1 + // Use Stimmer div/mod 10 on avr #ifdef __AVR__ #include @@ -131,13 +134,12 @@ void divmod10(uint32_t in, uint32_t &div, uint32_t &mod) q = q >> 3; // determine error - uint32_t r = in - ((q << 3) + (q << 1)); // r = in - q*10; + uint32_t r = in - ((q << 3) + (q << 1)); // r = in - q*10; div = q + (r > 9); if (r > 9) mod = r - 10; else mod = r; } -// Hackers delight function is here: -// http://www.hackersdelight.org/hdcodetxt/divuc.c.txt +// See: https://github.com/hcs0/Hackers-Delight // Code below uses 8/10 = 0.1100 1100 1100 1100 1100 1100 1100 1100. // 15 ops including the multiply, or 17 elementary ops. unsigned divu10(unsigned n) { @@ -154,41 +156,8 @@ unsigned divu10(unsigned n) { } */ //------------------------------------------------------------------------------ -#ifndef DOXYGEN_SHOULD_SKIP_THIS -#ifdef __AVR__ -static const float m[] PROGMEM = {1e-1, 1e-2, 1e-4, 1e-8, 1e-16, 1e-32}; -static const float p[] PROGMEM = {1e+1, 1e+2, 1e+4, 1e+8, 1e+16, 1e+32}; -#else // __AVR__ -static const float m[] = {1e-1, 1e-2, 1e-4, 1e-8, 1e-16, 1e-32}; -static const float p[] = {1e+1, 1e+2, 1e+4, 1e+8, 1e+16, 1e+32}; -#endif // __AVR__ -#endif // DOXYGEN_SHOULD_SKIP_THIS -// scale float v by power of ten. return v*10^n -float scale10(float v, int8_t n) { - const float *s; - if (n < 0) { - n = -n; - s = m; - } else { - s = p; - } - n &= 63; - for (uint8_t i = 0; n; n >>= 1, i++) { -#ifdef __AVR__ - if (n & 1) { - v *= pgm_read_float(&s[i]); - } -#else // __AVR__ - if (n & 1) { - v *= s[i]; - } -#endif // __AVR__ - } - return v; -} -//------------------------------------------------------------------------------ // Format 16-bit unsigned -char* fmtDec(uint16_t n, char* p) { +char* fmtBase10(char* str, uint16_t n) { while (n > 9) { #ifdef USE_STIMMER uint8_t tmp8, r; @@ -206,15 +175,15 @@ char* fmtDec(uint16_t n, char* p) { r -= 10; } #endif // USE_STIMMER - *--p = r + '0'; + *--str = r + '0'; } - *--p = n + '0'; - return p; + *--str = n + '0'; + return str; } //------------------------------------------------------------------------------ // format 32-bit unsigned -char* fmtDec(uint32_t n, char* p) { - while (n >> 16) { +char* fmtBase10(char* str, uint32_t n) { + while (n > 0XFFFF) { #ifdef USE_STIMMER uint8_t tmp8, r; divmod10_asm32(n, r, tmp8); @@ -231,55 +200,95 @@ char* fmtDec(uint32_t n, char* p) { r -= 10; } #endif // USE_STIMMER - *--p = r + '0'; + *--str = r + '0'; } - return fmtDec((uint16_t)n, p); + return fmtBase10(str, (uint16_t)n); } //------------------------------------------------------------------------------ -char* fmtFloat(float value, char* p, uint8_t prec) { - char sign = value < 0 ? '-' : 0; - if (sign) { - value = -value; +char* fmtHex(char* str, uint32_t n) { + do { + uint8_t h = n & 0XF; + *--str = h + (h < 10 ? '0' : 'A' - 10); + n >>= 4; + } while (n); + return str; +} +//------------------------------------------------------------------------------ +char* fmtSigned(char* str, int32_t num, uint8_t base, bool caps) { + bool neg = base == 10 && num < 0; + if (neg) { + num = -num; + } + str = fmtUnsigned(str, num, base, caps); + if (neg) { + *--str = '-'; } + return str; +} +//----------------------------------------------------------------------------- +char* fmtUnsigned(char* str, uint32_t num, uint8_t base, bool caps) { +#if USE_FMT_BASE10 + if (base == 10) return fmtBase10(str, (uint32_t)num); +#endif // USE_FMT_BASE10 + do { + int c = num%base; + *--str = c + (c < 10 ? '0' : caps ? 'A' - 10 : 'a' - 10); + } while (num /= base); + return str; +} +//----------------------------------------------------------------------------- - if (isnan(value)) { - *--p = 'n'; - *--p = 'a'; - *--p = 'n'; - return p; +static const double powTen[] = {1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9}; +static const double rnd[] = + {5e-1, 5e-2, 5e-3, 5e-4, 5e-5, 5e-6, 5e-7, 5e-8, 5e-9, 5e-10}; +static const size_t MAX_PREC = sizeof(powTen)/sizeof(powTen[0]); + +char *fmtDouble(char *str, double num, uint8_t prec, bool altFmt) { + bool neg = num < 0; + if (neg) { + num = -num; } - if (isinf(value)) { - *--p = 'f'; - *--p = 'n'; - *--p = 'i'; - return p; + if (isnan(num)) { + *--str = 'n'; + *--str = 'a'; + *--str = 'n'; + return str; } - if (value > 4294967040.0) { - *--p = 'f'; - *--p = 'v'; - *--p = 'o'; - return p; + if (isinf(num)) { + *--str = 'f'; + *--str = 'n'; + *--str = 'i'; + return str; } - if (prec > 9) { - prec = 9; + // last float < 2^32 + if (num > 4294967040.0) { + *--str = 'f'; + *--str = 'v'; + *--str = 'o'; + return str; } - value += scale10(0.5, -prec); - uint32_t whole = value; + if (prec > MAX_PREC) { + prec = MAX_PREC; + } + num += rnd[prec]; + uint32_t ul = num; if (prec) { - char* tmp = p - prec; - uint32_t fraction = scale10(value - whole, prec); - p = fmtDec(fraction, p); - while (p > tmp) { - *--p = '0'; + char* s = str - prec; + uint32_t f = (num - ul)*powTen[prec - 1]; + str = fmtBase10(str, f); + while (str > s) { + *--str = '0'; } - *--p = '.'; } - p = fmtDec(whole, p); - if (sign) { - *--p = sign; + if (prec || altFmt) { + *--str = '.'; + } + str = fmtBase10(str, ul); + if (neg) { + *--str = '-'; } - return p; + return str; } //------------------------------------------------------------------------------ /** Print a number followed by a field terminator. @@ -289,50 +298,71 @@ char* fmtFloat(float value, char* p, uint8_t prec) { * \param[in] expChar Use exp format if non zero. * \return Pointer to first character of result. */ -char* fmtFloat(float value, char* ptr, uint8_t prec, char expChar) { +char* fmtDouble(char* str, double value, + uint8_t prec, bool altFmt, char expChar) { + if (expChar != 'e' && expChar != 'E') { + expChar = 0; + } bool neg = value < 0; if (neg) { value = -value; } - // check for nan inf ovf if (isnan(value)) { - *--ptr = 'n'; - *--ptr = 'a'; - *--ptr = 'n'; - return ptr; + *--str = 'n'; + *--str = 'a'; + *--str = 'n'; + return str; } if (isinf(value)) { - *--ptr = 'f'; - *--ptr = 'n'; - *--ptr = 'i'; - return ptr; + *--str = 'f'; + *--str = 'n'; + *--str = 'i'; + return str; } if (!expChar && value > 4294967040.0) { - *--ptr = 'f'; - *--ptr = 'v'; - *--ptr = 'o'; - return ptr; + *--str = 'f'; + *--str = 'v'; + *--str = 'o'; + return str; } if (prec > 9) { prec = 9; } - float round = scale10(0.5, -prec); if (expChar) { int8_t exp = 0; bool expNeg = false; if (value) { - while (value > 10.0) { - value *= 0.1; - exp++; - } - while (value < 1.0) { - value *= 10.0; - exp--; + if (value > 10.0L) { + while (value > 1e16L) { + value *= 1e-16L; + exp += 16; + } + while (value > 1e4L) { + value *= 1e-4L; + exp += 4; + } + while (value > 10.0L) { + value *= 0.1L; + exp++; + } + } else if (value < 1.0L) { + while (value < 1e-16L) { + value *= 1e16L; + exp -= 16; + } + while (value < 1e-4L) { + value *= 1e4L; + exp -= 4; + } + while (value < 1.0L) { + value *= 10.0L; + exp--; + } } - value += round; - if (value > 10.0) { - value *= 0.1; + value += rnd[prec]; + if (value >= 10.0L) { + value *= 0.1L; exp++; } expNeg = exp < 0; @@ -340,43 +370,70 @@ char* fmtFloat(float value, char* ptr, uint8_t prec, char expChar) { exp = -exp; } } - ptr = fmtDec((uint16_t)exp, ptr); + str = fmtBase10(str, (uint16_t)exp); if (exp < 10) { - *--ptr = '0'; + *--str = '0'; } - *--ptr = expNeg ? '-' : '+'; - *--ptr = expChar; + *--str = expNeg ? '-' : '+'; + *--str = expChar; } else { // round value - value += round; + value += rnd[prec]; } + uint32_t whole = value; if (prec) { - char* tmp = ptr - prec; - uint32_t fraction = scale10(value - whole, prec); - ptr = fmtDec(fraction, ptr); - while (ptr > tmp) { - *--ptr = '0'; + char* tmp = str - prec; + uint32_t fraction = (value - whole)*powTen[prec - 1]; + str = fmtBase10(str, fraction); + while (str > tmp) { + *--str = '0'; } - *--ptr = '.'; } - ptr = fmtDec(whole, ptr); + if (prec || altFmt)*--str = '.'; + str = fmtBase10(str, whole); if (neg) { - *--ptr = '-'; + *--str = '-'; } - return ptr; + return str; } +//============================================================================== +// functions below not used //------------------------------------------------------------------------------ -char* fmtHex(uint32_t n, char* p) { - do { - uint8_t h = n & 0XF; - *--p = h + (h < 10 ? '0' : 'A' - 10); - n >>= 4; - } while (n); - return p; +#ifndef DOXYGEN_SHOULD_SKIP_THIS +#ifdef __AVR__ +static const float m[] PROGMEM = {1e-1, 1e-2, 1e-4, 1e-8, 1e-16, 1e-32}; +static const float p[] PROGMEM = {1e+1, 1e+2, 1e+4, 1e+8, 1e+16, 1e+32}; +#else // __AVR__ +static const float m[] = {1e-1, 1e-2, 1e-4, 1e-8, 1e-16, 1e-32}; +static const float p[] = {1e+1, 1e+2, 1e+4, 1e+8, 1e+16, 1e+32}; +#endif // __AVR__ +#endif // DOXYGEN_SHOULD_SKIP_THIS +// scale float v by power of ten. return v*10^n +float scale10(float v, int8_t n) { + const float *s; + if (n < 0) { + n = -n; + s = m; + } else { + s = p; + } + n &= 63; + for (uint8_t i = 0; n; n >>= 1, i++) { +#ifdef __AVR__ + if (n & 1) { + v *= pgm_read_float(&s[i]); + } +#else // __AVR__ + if (n & 1) { + v *= s[i]; + } +#endif // __AVR__ + } + return v; } //------------------------------------------------------------------------------ -float scanFloat(const char* str, char** ptr) { +float scanFloat(const char* str, const char** ptr) { int16_t const EXP_LIMIT = 100; bool digit = false; bool dot = false; @@ -389,7 +446,7 @@ float scanFloat(const char* str, char** ptr) { const char* successPtr = str; if (ptr) { - *ptr = const_cast(str); + *ptr = str; } while (isSpace((c = *str++))) {} @@ -448,13 +505,11 @@ float scanFloat(const char* str, char** ptr) { fracExp += expNeg ? -exp : exp; } if (ptr) { - *ptr = const_cast(successPtr); + *ptr = successPtr; } v = scale10(static_cast(fract), fracExp); return neg ? -v : v; -fail: + fail: return 0; } - - diff --git a/avr/cores/megacommand/SdFat/FatLib/FmtNumber.h b/avr/cores/megacommand/SdFat/common/FmtNumber.h similarity index 71% rename from avr/cores/megacommand/SdFat/FatLib/FmtNumber.h rename to avr/cores/megacommand/SdFat/common/FmtNumber.h index c873ce155..6fa412488 100644 --- a/avr/cores/megacommand/SdFat/FatLib/FmtNumber.h +++ b/avr/cores/megacommand/SdFat/common/FmtNumber.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 20011-2017 Bill Greiman + * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -24,20 +24,20 @@ */ #ifndef FmtNumber_h #define FmtNumber_h -// #include +#include +#include +#include inline bool isDigit(char c) { - return '0' <= c && c <= '9'; + return '0' <= (c) && (c) <= '9'; } inline bool isSpace(char c) { - return c == ' ' || (0X9 <= c && c <= 0XD); + return (c) == ' ' || (0X9 <= (c) && (c) <= 0XD); } -#include -#include -char* fmtDec(uint16_t n, char* p); -char* fmtDec(uint32_t n, char* p); -char* fmtFloat(float value, char* p, uint8_t prec); -char* fmtFloat(float value, char* ptr, uint8_t prec, char expChar); -char* fmtHex(uint32_t n, char* p); -float scale10(float v, int8_t n); -float scanFloat(const char* str, char** ptr); +char* fmtBase10(char* str, uint16_t n); +char* fmtBase10(char* str, uint32_t n); +char* fmtDouble(char *str, double d, uint8_t prec, bool altFmt); +char* fmtDouble(char* str, double d, uint8_t prec, bool altFmt, char expChar); +char* fmtHex(char* str, uint32_t n); +char* fmtSigned(char* str, int32_t n, uint8_t base, bool caps); +char* fmtUnsigned(char* str, uint32_t n, uint8_t base, bool caps); #endif // FmtNumber_h diff --git a/avr/cores/megacommand/SdFat/FatLib/FatApiConstants.h b/avr/cores/megacommand/SdFat/common/FsApiConstants.h similarity index 55% rename from avr/cores/megacommand/SdFat/FatLib/FatApiConstants.h rename to avr/cores/megacommand/SdFat/common/FsApiConstants.h index f4080ac10..8ad0d454a 100644 --- a/avr/cores/megacommand/SdFat/FatLib/FatApiConstants.h +++ b/avr/cores/megacommand/SdFat/common/FsApiConstants.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 20011-2017 Bill Greiman + * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -22,36 +22,49 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ -#ifndef FatApiConstants_h -#define FatApiConstants_h -//------------------------------------------------------------------------------ -// use the gnu style oflag in open() -/** open() oflag for reading */ -const uint8_t O_READ = 0X01; -/** open() oflag - same as O_IN */ -const uint8_t O_RDONLY = O_READ; -/** open() oflag for write */ -const uint8_t O_WRITE = 0X02; -/** open() oflag - same as O_WRITE */ -const uint8_t O_WRONLY = O_WRITE; -/** open() oflag for reading and writing */ -const uint8_t O_RDWR = (O_READ | O_WRITE); -/** open() oflag mask for access modes */ -const uint8_t O_ACCMODE = (O_READ | O_WRITE); -/** The file offset shall be set to the end of the file prior to each write. */ -const uint8_t O_APPEND = 0X04; -/** synchronous writes - call sync() after each write */ -const uint8_t O_SYNC = 0X08; -/** truncate the file to zero length */ -const uint8_t O_TRUNC = 0X10; -/** set the initial position at the end of the file */ -const uint8_t O_AT_END = 0X20; -/** create the file if nonexistent */ -const uint8_t O_CREAT = 0X40; -/** If O_CREAT and O_EXCL are set, open() shall fail if the file exists */ -const uint8_t O_EXCL = 0X80; +#ifndef FsApiConstants_h +#define FsApiConstants_h +#include "SysCall.h" +#if USE_FCNTL_H +#include +/* values for GNU Arm Embedded Toolchain. + * O_RDONLY: 0x0 + * O_WRONLY: 0x1 + * O_RDWR: 0x2 + * O_ACCMODE: 0x3 + * O_APPEND: 0x8 + * O_CREAT: 0x200 + * O_TRUNC: 0x400 + * O_EXCL: 0x800 + * O_SYNC: 0x2000 + * O_NONBLOCK: 0x4000 + */ +/** Use O_NONBLOCK for open at EOF */ +#define O_AT_END O_NONBLOCK ///< Open at EOF. +typedef int oflag_t; +#else // USE_FCNTL_H +#define O_RDONLY 0X00 ///< Open for reading only. +#define O_WRONLY 0X01 ///< Open for writing only. +#define O_RDWR 0X02 ///< Open for reading and writing. +#define O_AT_END 0X04 ///< Open at EOF. +#define O_APPEND 0X08 ///< Set append mode. +#define O_CREAT 0x10 ///< Create file if it does not exist. +#define O_TRUNC 0x20 ///< Truncate file to zero length. +#define O_EXCL 0x40 ///< Fail if the file exists. +#define O_SYNC 0x80 ///< Synchronized write I/O operations. + +#define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR) ///< Mask for access mode. +typedef uint8_t oflag_t; +#endif // USE_FCNTL_H + +#define O_READ O_RDONLY +#define O_WRITE O_WRONLY + +inline bool isWriteMode(oflag_t oflag) { + oflag &= O_ACCMODE; + return oflag == O_WRONLY || oflag == O_RDWR; +} -// FatFile class static and const definitions // flags for ls() /** ls() flag for list all files including hidden. */ const uint8_t LS_A = 1; @@ -62,11 +75,11 @@ const uint8_t LS_SIZE = 4; /** ls() flag for recursive list of subdirectories */ const uint8_t LS_R = 8; -// flags for timestamp +// flags for time-stamp /** set the file's last access date */ const uint8_t T_ACCESS = 1; /** set the file's creation date and time */ const uint8_t T_CREATE = 2; /** Set the file's write date and time */ const uint8_t T_WRITE = 4; -#endif // FatApiConstants_h +#endif // FsApiConstants_h diff --git a/avr/cores/megacommand/SdFat/common/FsBlockDevice.h b/avr/cores/megacommand/SdFat/common/FsBlockDevice.h new file mode 100644 index 000000000..e89c5b023 --- /dev/null +++ b/avr/cores/megacommand/SdFat/common/FsBlockDevice.h @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef FsBlockDevice_h +#define FsBlockDevice_h +#include "SdCard/SdCard.h" +#if HAS_SDIO_CLASS || USE_BLOCK_DEVICE_INTERFACE +typedef FsBlockDeviceInterface FsBlockDevice; +#else +typedef SdCard FsBlockDevice; +#endif +#endif // FsBlockDevice_h diff --git a/avr/cores/megacommand/SdFat/common/FsBlockDeviceInterface.h b/avr/cores/megacommand/SdFat/common/FsBlockDeviceInterface.h new file mode 100644 index 000000000..252a487ba --- /dev/null +++ b/avr/cores/megacommand/SdFat/common/FsBlockDeviceInterface.h @@ -0,0 +1,95 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +/** + * \file + * \brief FsBlockDeviceInterface include file. + */ +#ifndef FsBlockDeviceInterface_h +#define FsBlockDeviceInterface_h +#include +#include +/** + * \class FsBlockDeviceInterface + * \brief FsBlockDeviceInterface class. + */ +class FsBlockDeviceInterface { + public: + virtual ~FsBlockDeviceInterface() {} + + /** end use of device */ + virtual void end() {} + /** + * Check for FsBlockDevice busy. + * + * \return true if busy else false. + */ + virtual bool isBusy() = 0; + /** + * Read a sector. + * + * \param[in] sector Logical sector to be read. + * \param[out] dst Pointer to the location that will receive the data. + * \return true for success or false for failure. + */ + virtual bool readSector(uint32_t sector, uint8_t* dst) = 0; + + /** + * Read multiple sectors. + * + * \param[in] sector Logical sector to be read. + * \param[in] ns Number of sectors to be read. + * \param[out] dst Pointer to the location that will receive the data. + * \return true for success or false for failure. + */ + virtual bool readSectors(uint32_t sector, uint8_t* dst, size_t ns) = 0; + + /** \return device size in sectors. */ + virtual uint32_t sectorCount() = 0; + + /** End multi-sector transfer and go to idle state. + * \return true for success or false for failure. + */ + virtual bool syncDevice() = 0; + + /** + * Writes a sector. + * + * \param[in] sector Logical sector to be written. + * \param[in] src Pointer to the location of the data to be written. + * \return true for success or false for failure. + */ + virtual bool writeSector(uint32_t sector, const uint8_t* src) = 0; + + /** + * Write multiple sectors. + * + * \param[in] sector Logical sector to be written. + * \param[in] ns Number of sectors to be written. + * \param[in] src Pointer to the location of the data to be written. + * \return true for success or false for failure. + */ + virtual bool writeSectors(uint32_t sector, const uint8_t* src, size_t ns) = 0; +}; +#endif // FsBlockDeviceInterface_h diff --git a/avr/cores/megacommand/SdFat/common/FsCache.cpp b/avr/cores/megacommand/SdFat/common/FsCache.cpp new file mode 100644 index 000000000..1678f6bd3 --- /dev/null +++ b/avr/cores/megacommand/SdFat/common/FsCache.cpp @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#define DBG_FILE "FsCache.cpp" +#include "DebugMacros.h" +#include "FsCache.h" +//------------------------------------------------------------------------------ +uint8_t* FsCache::prepare(uint32_t sector, uint8_t option) { + if (!m_blockDev) { + DBG_FAIL_MACRO; + goto fail; + } + if (m_sector != sector) { + if (!sync()) { + DBG_FAIL_MACRO; + goto fail; + } + if (!(option & CACHE_OPTION_NO_READ)) { + if (!m_blockDev->readSector(sector, m_buffer)) { + DBG_FAIL_MACRO; + goto fail; + } + } + m_status = 0; + m_sector = sector; + } + m_status |= option & CACHE_STATUS_MASK; + return m_buffer; + + fail: + return nullptr; +} +//------------------------------------------------------------------------------ +bool FsCache::sync() { + if (m_status & CACHE_STATUS_DIRTY) { + if (!m_blockDev->writeSector(m_sector, m_buffer)) { + DBG_FAIL_MACRO; + goto fail; + } + // mirror second FAT + if (m_status & CACHE_STATUS_MIRROR_FAT) { + uint32_t sector = m_sector + m_mirrorOffset; + if (!m_blockDev->writeSector(sector, m_buffer)) { + DBG_FAIL_MACRO; + goto fail; + } + } + m_status &= ~CACHE_STATUS_DIRTY; + } + return true; + + fail: + return false; +} diff --git a/avr/cores/megacommand/SdFat/common/FsCache.h b/avr/cores/megacommand/SdFat/common/FsCache.h new file mode 100644 index 000000000..6f8d864ee --- /dev/null +++ b/avr/cores/megacommand/SdFat/common/FsCache.h @@ -0,0 +1,184 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef FsCache_h +#define FsCache_h +/** + * \file + * \brief Common cache code for exFAT and FAT. + */ +#include "SysCall.h" +#include "FsBlockDevice.h" +/** + * \class FsCache + * \brief Sector cache. + */ +class FsCache { + public: + /** Cached sector is dirty */ + static const uint8_t CACHE_STATUS_DIRTY = 1; + /** Cashed sector is FAT entry and must be mirrored in second FAT. */ + static const uint8_t CACHE_STATUS_MIRROR_FAT = 2; + /** Cache sector status bits */ + static const uint8_t CACHE_STATUS_MASK = + CACHE_STATUS_DIRTY | CACHE_STATUS_MIRROR_FAT; + /** Sync existing sector but do not read new sector. */ + static const uint8_t CACHE_OPTION_NO_READ = 4; + /** Cache sector for read. */ + static const uint8_t CACHE_FOR_READ = 0; + /** Cache sector for write. */ + static const uint8_t CACHE_FOR_WRITE = CACHE_STATUS_DIRTY; + /** Reserve cache sector for write - do not read from sector device. */ + static const uint8_t CACHE_RESERVE_FOR_WRITE = + CACHE_STATUS_DIRTY | CACHE_OPTION_NO_READ; + //---------------------------------------------------------------------------- + /** \return Cache buffer address. */ + uint8_t* cacheBuffer() { + return m_buffer; + } + /** + * Cache safe read of a sector. + * + * \param[in] sector Logical sector to be read. + * \param[out] dst Pointer to the location that will receive the data. + * \return true for success or false for failure. + */ + bool cacheSafeRead(uint32_t sector, uint8_t* dst) { + if (isCached(sector)) { + memcpy(dst, m_buffer, 512); + return true; + } + return m_blockDev->readSector(sector, dst); + } + /** + * Cache safe read of multiple sectors. + * + * \param[in] sector Logical sector to be read. + * \param[in] count Number of sectors to be read. + * \param[out] dst Pointer to the location that will receive the data. + * \return true for success or false for failure. + */ + bool cacheSafeRead(uint32_t sector, uint8_t* dst, size_t count) { + if (isCached(sector, count) && !sync()) { + return false; + } + return m_blockDev->readSectors(sector, dst, count); + } + /** + * Cache safe write of a sectors. + * + * \param[in] sector Logical sector to be written. + * \param[in] src Pointer to the location of the data to be written. + * \return true for success or false for failure. + */ + bool cacheSafeWrite(uint32_t sector, const uint8_t* src) { + if (isCached(sector)) { + invalidate(); + } + return m_blockDev->writeSector(sector, src); + } + /** + * Cache safe write of multiple sectors. + * + * \param[in] sector Logical sector to be written. + * \param[in] src Pointer to the location of the data to be written. + * \param[in] count Number of sectors to be written. + * \return true for success or false for failure. + */ + bool cacheSafeWrite(uint32_t sector, const uint8_t* src, size_t count) { + if (isCached(sector, count)) { + invalidate(); + } + return m_blockDev->writeSectors(sector, src, count); + } + /** \return Clear the cache and returns a pointer to the cache. */ + uint8_t* clear() { + if (isDirty() && !sync()) { + return nullptr; + } + invalidate(); + return m_buffer; + } + /** Set current sector dirty. */ + void dirty() { + m_status |= CACHE_STATUS_DIRTY; + } + /** Initialize the cache. + * \param[in] blockDev Block device for this cache. + */ + void init(FsBlockDevice* blockDev) { + m_blockDev = blockDev; + invalidate(); + } + /** Invalidate current cache sector. */ + void invalidate() { + m_status = 0; + m_sector = 0XFFFFFFFF; + } + /** Check if a sector is in the cache. + * \param[in] sector Sector to checked. + * \return true if the sector is cached. + */ + bool isCached(uint32_t sector) const {return sector == m_sector;} + /** Check if the cache contains a sector from a range. + * \param[in] sector Start sector of the range. + * \param[in] count Number of sectors in the range. + * \return true if a sector in the range is cached. + */ + bool isCached(uint32_t sector, size_t count) { + return sector <= m_sector && m_sector < (sector + count); + } + /** \return dirty status */ + bool isDirty() { + return m_status & CACHE_STATUS_DIRTY; + } + /** Prepare cache to access sector. + * \param[in] sector Sector to read. + * \param[in] option mode for cached sector. + * \return Address of cached sector. + */ + uint8_t* prepare(uint32_t sector, uint8_t option); + /** \return Logical sector number for cached sector. */ + uint32_t sector() { + return m_sector; + } + /** Set the offset to the second FAT for mirroring. + * \param[in] offset Sector offset to second FAT. + */ + void setMirrorOffset(uint32_t offset) { + m_mirrorOffset = offset; + } + /** Write current sector if dirty. + * \return true for success or false for failure. + */ + bool sync(); + + private: + uint8_t m_status; + FsBlockDevice* m_blockDev; + uint32_t m_mirrorOffset; + uint32_t m_sector; + uint8_t m_buffer[512]; +}; +#endif // FsCache_h diff --git a/avr/cores/megacommand/SdFat/common/FsDateTime.cpp b/avr/cores/megacommand/SdFat/common/FsDateTime.cpp new file mode 100644 index 000000000..bd9a3886b --- /dev/null +++ b/avr/cores/megacommand/SdFat/common/FsDateTime.cpp @@ -0,0 +1,175 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "SysCall.h" +#include "FsDateTime.h" +#include "FmtNumber.h" + +static void dateTimeMs10(uint16_t* date, uint16_t* time, uint8_t* ms10) { + *ms10 = 0; + FsDateTime::callback2(date, time); +} +//------------------------------------------------------------------------------ +/** Date time callback. */ +namespace FsDateTime { + void (*callback)(uint16_t* date, uint16_t* time, uint8_t* ms10) = nullptr; + void (*callback2)(uint16_t* date, uint16_t* time) = nullptr; + void clearCallback() { + callback = nullptr; + } + void setCallback(void (*dateTime)(uint16_t* date, uint16_t* time)) { + callback = dateTimeMs10; + callback2 = dateTime; + } + void setCallback( + void (*dateTime)(uint16_t* date, uint16_t* time, uint8_t* ms10)) { + callback = dateTime; + } +} // namespace FsDateTime +//------------------------------------------------------------------------------ +static char* fsFmtField(char* str, uint16_t n, char sep) { + if (sep) { + *--str = sep; + } + str = fmtBase10(str, n); + if (n < 10) { + *--str = '0'; + } + return str; +} +//------------------------------------------------------------------------------ +char* fsFmtDate(char* str, uint16_t date) { + str = fsFmtField(str, date & 31, 0); + date >>= 5; + str = fsFmtField(str, date & 15, '-'); + date >>= 4; + return fsFmtField(str, 1980 + date, '-'); +} +//------------------------------------------------------------------------------ +char* fsFmtTime(char* str, uint16_t time) { + time >>= 5; + str = fsFmtField(str, time & 63, 0); + return fsFmtField(str, time >> 6, ':'); +} +//------------------------------------------------------------------------------ +char* fsFmtTime(char* str, uint16_t time, uint8_t sec100) { + str = fsFmtField(str, 2*(time & 31) + (sec100 < 100 ? 0 : 1), 0); + *--str = ':'; + return fsFmtTime(str, time); +} +//------------------------------------------------------------------------------ +char* fsFmtTimeZone(char* str, int8_t tz) { + char sign; + if (tz & 0X80) { + if (tz & 0X40) { + sign = '-'; + tz = -tz; + } else { + sign = '+'; + tz &= 0X7F; + } + if (tz) { + str = fsFmtField(str, 15*(tz%4), 0); + str = fsFmtField(str, tz/4, ':'); + *--str = sign; + } + *--str = 'C'; + *--str = 'T'; + *--str = 'U'; + } + return str; +} +//------------------------------------------------------------------------------ +size_t fsPrintDate(print_t* pr, uint16_t date) { + // Allow YYYY-MM-DD + char buf[sizeof("YYYY-MM-DD") -1]; + char* str = buf + sizeof(buf); + if (date) { + str = fsFmtDate(str, date); + } else { + do { + *--str = ' '; + } while (str > buf); + } + return pr->write(reinterpret_cast(str), buf + sizeof(buf) - str); +} +//------------------------------------------------------------------------------ +size_t fsPrintDateTime(print_t* pr, uint16_t date, uint16_t time) { + // Allow YYYY-MM-DD hh:mm + char buf[sizeof("YYYY-MM-DD hh:mm") -1]; + char* str = buf + sizeof(buf); + if (date) { + str = fsFmtTime(str, time); + *--str = ' '; + str = fsFmtDate(str, date); + } else { + do { + *--str = ' '; + } while (str > buf); + } + return pr->write(reinterpret_cast(str), buf + sizeof(buf) - str); +} +//------------------------------------------------------------------------------ +size_t fsPrintDateTime(print_t* pr, uint32_t dateTime) { + return fsPrintDateTime(pr, dateTime >> 16, dateTime & 0XFFFF); +} +//------------------------------------------------------------------------------ +size_t fsPrintDateTime(print_t* pr, + uint32_t dateTime, uint8_t s100, int8_t tz) { + // Allow YYYY-MM-DD hh:mm:ss UTC+hh:mm + char buf[sizeof("YYYY-MM-DD hh:mm:ss UTC+hh:mm") -1]; + char* str = buf + sizeof(buf); + if (tz) { + str = fsFmtTimeZone(str, tz); + *--str = ' '; + } + str = fsFmtTime(str, (uint16_t)dateTime, s100); + *--str = ' '; + str = fsFmtDate(str, (uint16_t)(dateTime >> 16)); + return pr->write(reinterpret_cast(str), buf + sizeof(buf) - str); +} +//------------------------------------------------------------------------------ +size_t fsPrintTime(print_t* pr, uint16_t time) { + // Allow hh:mm + char buf[sizeof("hh:mm") -1]; + char* str = buf + sizeof(buf); + str = fsFmtTime(str, time); + return pr->write(reinterpret_cast(str), buf + sizeof(buf) - str); +} +//------------------------------------------------------------------------------ +size_t fsPrintTime(print_t* pr, uint16_t time, uint8_t sec100) { + // Allow hh:mm:ss + char buf[sizeof("hh:mm:ss") -1]; + char* str = buf + sizeof(buf); + str = fsFmtTime(str, time, sec100); + return pr->write(reinterpret_cast(str), buf + sizeof(buf) - str); +} +//------------------------------------------------------------------------------ +size_t fsPrintTimeZone(print_t* pr, int8_t tz) { + // Allow UTC+hh:mm + char buf[sizeof("UTC+hh:mm") -1]; + char* str = buf + sizeof(buf); + str = fsFmtTimeZone(str, tz); + return pr->write(reinterpret_cast(str), buf + sizeof(buf) - str); +} diff --git a/avr/cores/megacommand/SdFat/common/FsDateTime.h b/avr/cores/megacommand/SdFat/common/FsDateTime.h new file mode 100644 index 000000000..aac33f333 --- /dev/null +++ b/avr/cores/megacommand/SdFat/common/FsDateTime.h @@ -0,0 +1,193 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef FsDateTime_h +#define FsDateTime_h +#include +#include "CompileDateTime.h" +#include "SysCall.h" + +/** Backward compatible definition. */ +#define FAT_DATE(y, m, d) FS_DATE(y, m, d) + +/** Backward compatible definition. */ +#define FAT_TIME(h, m, s) FS_TIME(h, m, s) + +/** Date time callback */ +namespace FsDateTime { + /** Date time callback. */ + extern void (*callback)(uint16_t* date, uint16_t* time, uint8_t* ms10); + /** Date time callback. */ + extern void (*callback2)(uint16_t* date, uint16_t* time); + /** Cancel callback. */ + void clearCallback(); + /** Set the date/time callback function. + * + * \param[in] dateTime The user's call back function. The callback. + * function is of the form: + * + * \code + * void dateTime(uint16_t* date, uint16_t* time) { + * uint16_t year; + * uint8_t month, day, hour, minute, second; + * + * // User gets date and time from GPS or real-time clock here. + * + * // Return date using FS_DATE macro to format fields. + * *date = FS_DATE(year, month, day); + * + * // Return time using FS_TIME macro to format fields. + * *time = FS_TIME(hour, minute, second); + * } + * \endcode + * + * Sets the function that is called when a file is created or when + * a file's directory entry is modified by sync(). All timestamps, + * access, creation, and modify, are set when a file is created. + * sync() maintains the last access date and last modify date/time. + * + */ + void setCallback(void (*dateTime)(uint16_t* date, uint16_t* time)); + /** Set the date/time callback function. + * + * \param[in] dateTime The user's call back function. The callback + * function is of the form: + * + * \code + * void dateTime(uint16_t* date, uint16_t* time, uint8_t* ms10) { + * uint16_t year; + * uint8_t month, day, hour, minute, second; + * + * // User gets date and time from GPS or real-time clock here. + * + * // Return date using FS_DATE macro to format fields + * *date = FS_DATE(year, month, day); + * + * // Return time using FS_TIME macro to format fields + * *time = FS_TIME(hour, minute, second); + * + * // Return the time since the last even second in units of 10 ms. + * // The granularity of the seconds part of FS_TIME is 2 seconds so + * // this field is a count of hundredth of a second and its valid + * // range is 0-199 inclusive. + * // For a simple RTC return 100*(seconds & 1). + * *ms10 = + * } + * \endcode + * + * Sets the function that is called when a file is created or when + * a file's directory entry is modified by sync(). All timestamps, + * access, creation, and modify, are set when a file is created. + * sync() maintains the last access date and last modify date/time. + * + */ + void setCallback( + void (*dateTime)(uint16_t* date, uint16_t* time, uint8_t* ms10)); +} // namespace FsDateTime + +/** date field for directory entry + * \param[in] year [1980,2107] + * \param[in] month [1,12] + * \param[in] day [1,31] + * + * \return Packed date for directory entry. + */ +static inline uint16_t FS_DATE(uint16_t year, uint8_t month, uint8_t day) { + year -= 1980; + return year > 127 || month > 12 || day > 31 ? 0 : + year << 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 FS_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 FS_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 FS_DAY(uint16_t fatDate) { + return fatDate & 0X1F; +} +/** time field for directory entry + * \param[in] hour [0,23] + * \param[in] minute [0,59] + * \param[in] second [0,59] + * + * \return Packed time for directory entry. + */ +static inline uint16_t FS_TIME(uint8_t hour, uint8_t minute, uint8_t second) { + return hour > 23 || minute > 59 || second > 59 ? 0 : + 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 FS_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 FS_MINUTE(uint16_t fatTime) { + return (fatTime >> 5) & 0X3F; +} +/** second part of FAT directory time field + * N\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 FS_SECOND(uint16_t fatTime) { + return 2*(fatTime & 0X1F); +} +char* fsFmtDate(char* str, uint16_t date); +char* fsFmtTime(char* str, uint16_t time); +char* fsFmtTime(char* str, uint16_t time, uint8_t sec100); +char* fsFmtTimeZone(char* str, int8_t tz); +size_t fsPrintDate(print_t* pr, uint16_t date); +size_t fsPrintDateTime(print_t* pr, uint16_t date, uint16_t time); +size_t fsPrintDateTime(print_t* pr, uint32_t dateTime); +size_t fsPrintDateTime(print_t* pr, uint32_t dateTime, uint8_t s100, int8_t tz); +size_t fsPrintTime(print_t* pr, uint16_t time); +size_t fsPrintTime(print_t* pr, uint16_t time, uint8_t sec100); +size_t fsPrintTimeZone(print_t* pr, int8_t tz); +#endif // FsDateTime_h diff --git a/avr/cores/megacommand/SdFat/common/FsName.cpp b/avr/cores/megacommand/SdFat/common/FsName.cpp new file mode 100644 index 000000000..8356c24c4 --- /dev/null +++ b/avr/cores/megacommand/SdFat/common/FsName.cpp @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "FsName.h" +#include "FsUtf.h" +#if USE_UTF8_LONG_NAMES +uint16_t FsName::get16() { + uint16_t rtn; + if (ls) { + rtn = ls; + ls = 0; + } else if (next >= end) { + rtn = 0; + } else { + uint32_t cp; + const char* ptr = FsUtf::mbToCp(next, end, &cp); + if (!ptr) { + goto fail; + } + next = ptr; + if (cp <= 0XFFFF) { + rtn = cp; + } else { + ls = FsUtf::lowSurrogate(cp); + rtn = FsUtf::highSurrogate(cp); + } + } + return rtn; + + fail: + return 0XFFFF; +} +#endif // USE_UTF8_LONG_NAMES diff --git a/avr/cores/megacommand/SdFat/common/FsName.h b/avr/cores/megacommand/SdFat/common/FsName.h new file mode 100644 index 000000000..e90d1e567 --- /dev/null +++ b/avr/cores/megacommand/SdFat/common/FsName.h @@ -0,0 +1,66 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef FsName_h +#define FsName_h +#include "SysCall.h" +#include +/** + * \file + * \brief FsName class. + */ +/** + * \class FsName + * \brief Handle UTF-8 file names. + */ +class FsName { + public: + /** Beginning of LFN. */ + const char* begin; + /** Next LFN character of end. */ + const char* next; + /** Position one beyond last LFN character. */ + const char* end; +#if !USE_UTF8_LONG_NAMES + /** \return true if at end. */ + bool atEnd() {return next == end;} + /** Reset to start of LFN. */ + void reset() {next = begin;} + /** \return next char of LFN. */ + char getch() {return atEnd() ? 0 : *next++;} + /** \return next UTF-16 unit of LFN. */ + uint16_t get16() {return atEnd() ? 0 : *next++;} +#else // !USE_UTF8_LONG_NAMES + uint16_t ls = 0; + bool atEnd() { + return !ls && next == end; + } + void reset() { + next = begin; + ls = 0; // lowSurrogate + } + uint16_t get16(); +#endif // !USE_UTF8_LONG_NAMES +}; +#endif // FsName_h diff --git a/avr/cores/megacommand/SdFat/common/FsStructs.cpp b/avr/cores/megacommand/SdFat/common/FsStructs.cpp new file mode 100644 index 000000000..294dbe253 --- /dev/null +++ b/avr/cores/megacommand/SdFat/common/FsStructs.cpp @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "FsStructs.h" +// bgnLba = relSector; +// endLba = relSector + partSize - 1; +void lbaToMbrChs(uint8_t* chs, uint32_t capacityMB, uint32_t lba) { + uint32_t c; + uint8_t h; + uint8_t s; + + uint8_t numberOfHeads; + uint8_t sectorsPerTrack = capacityMB <= 256 ? 32 : 63; + if (capacityMB <= 16) { + numberOfHeads = 2; + } else if (capacityMB <= 32) { + numberOfHeads = 4; + } else if (capacityMB <= 128) { + numberOfHeads = 8; + } else if (capacityMB <= 504) { + numberOfHeads = 16; + } else if (capacityMB <= 1008) { + numberOfHeads = 32; + } else if (capacityMB <= 2016) { + numberOfHeads = 64; + } else if (capacityMB <= 4032) { + numberOfHeads = 128; + } else { + numberOfHeads = 255; + } + c = lba / (numberOfHeads * sectorsPerTrack); + if (c <= 1023) { + h = (lba % (numberOfHeads * sectorsPerTrack)) / sectorsPerTrack; + s = (lba % sectorsPerTrack) + 1; + } else { + c = 1023; + h = 254; + s = 63; + } + chs[0] = h; + chs[1] = ((c >> 2) & 0XC0) | s; + chs[2] = c; +} diff --git a/avr/cores/megacommand/SdFat/common/FsStructs.h b/avr/cores/megacommand/SdFat/common/FsStructs.h new file mode 100644 index 000000000..93056ffd6 --- /dev/null +++ b/avr/cores/megacommand/SdFat/common/FsStructs.h @@ -0,0 +1,416 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef FsStructs_h +#define FsStructs_h +#include +#include +//------------------------------------------------------------------------------ +void lbaToMbrChs(uint8_t* chs, uint32_t capacityMB, uint32_t lba); +//------------------------------------------------------------------------------ +#if !defined(USE_SIMPLE_LITTLE_ENDIAN) || USE_SIMPLE_LITTLE_ENDIAN +// assumes CPU is little-endian and handles alignment issues. +inline uint16_t getLe16(const uint8_t* src) { + return *reinterpret_cast(src); +} +inline uint32_t getLe32(const uint8_t* src) { + return *reinterpret_cast(src); +} +inline uint64_t getLe64(const uint8_t* src) { + return *reinterpret_cast(src); +} +inline void setLe16(uint8_t* dst, uint16_t src) { + *reinterpret_cast(dst) = src; +} +inline void setLe32(uint8_t* dst, uint32_t src) { + *reinterpret_cast(dst) = src; +} +inline void setLe64(uint8_t* dst, uint64_t src) { + *reinterpret_cast(dst) = src; +} +#else // USE_SIMPLE_LITTLE_ENDIAN +inline uint16_t getLe16(const uint8_t* src) { + return (uint16_t)src[0] << 0 | + (uint16_t)src[1] << 8; +} +inline uint32_t getLe32(const uint8_t* src) { + return (uint32_t)src[0] << 0 | + (uint32_t)src[1] << 8 | + (uint32_t)src[2] << 16 | + (uint32_t)src[3] << 24; +} +inline uint64_t getLe64(const uint8_t* src) { + return (uint64_t)src[0] << 0 | + (uint64_t)src[1] << 8 | + (uint64_t)src[2] << 16 | + (uint64_t)src[3] << 24 | + (uint64_t)src[4] << 32 | + (uint64_t)src[5] << 40 | + (uint64_t)src[6] << 48 | + (uint64_t)src[7] << 56; +} +inline void setLe16(uint8_t* dst, uint16_t src) { + dst[0] = src >> 0; + dst[1] = src >> 8; +} +inline void setLe32(uint8_t* dst, uint32_t src) { + dst[0] = src >> 0; + dst[1] = src >> 8; + dst[2] = src >> 16; + dst[3] = src >> 24; +} +inline void setLe64(uint8_t* dst, uint64_t src) { + dst[0] = src >> 0; + dst[1] = src >> 8; + dst[2] = src >> 16; + dst[3] = src >> 24; + dst[4] = src >> 32; + dst[5] = src >> 40; + dst[6] = src >> 48; + dst[7] = src >> 56; +} +#endif // USE_SIMPLE_LITTLE_ENDIAN +//------------------------------------------------------------------------------ +// Size of FAT and exFAT directory structures. +const size_t FS_DIR_SIZE = 32; +//------------------------------------------------------------------------------ +// Reserved characters for exFAT names and FAT LFN. +inline bool lfnReservedChar(uint8_t c) { + return c < 0X20 || c == '"' || c == '*' || c == '/' || c == ':' + || c == '<' || c == '>' || c == '?' || c == '\\'|| c == '|'; +} +//------------------------------------------------------------------------------ +// Reserved characters for FAT short 8.3 names. +inline bool sfnReservedChar(uint8_t c) { + if (c == '"' || c == '|' || c == '[' || c == '\\' || c == ']') { + return true; + } + // *+,./ or :;<=>? + if ((0X2A <= c && c <= 0X2F && c != 0X2D) || (0X3A <= c && c <= 0X3F)) { + return true; + } + // Reserved if not in range (0X20, 0X7F). + return !(0X20 < c && c < 0X7F); +} +//------------------------------------------------------------------------------ +const uint16_t MBR_SIGNATURE = 0xAA55; +const uint16_t PBR_SIGNATURE = 0xAA55; + +typedef struct mbrPartition { + uint8_t boot; + uint8_t beginCHS[3]; + uint8_t type; + uint8_t endCHS[3]; + uint8_t relativeSectors[4]; + uint8_t totalSectors[4]; +} MbrPart_t; +//------------------------------------------------------------------------------ +typedef struct masterBootRecordSector { + uint8_t bootCode[446]; + MbrPart_t part[4]; + uint8_t signature[2]; +} MbrSector_t; +//------------------------------------------------------------------------------ +typedef struct partitionBootSector { + uint8_t jmpInstruction[3]; + char oemName[8]; + uint8_t bpb[109]; + uint8_t bootCode[390]; + uint8_t signature[2]; +} pbs_t; +//------------------------------------------------------------------------------ +typedef struct { + uint8_t type; + uint8_t data[31]; +} DirGeneric_t; +//============================================================================== +typedef struct { + uint64_t position; + uint32_t cluster; +} fspos_t; +//============================================================================== +const uint8_t EXTENDED_BOOT_SIGNATURE = 0X29; +typedef struct biosParameterBlockFat16 { + uint8_t bytesPerSector[2]; + uint8_t sectorsPerCluster; + uint8_t reservedSectorCount[2]; + uint8_t fatCount; + uint8_t rootDirEntryCount[2]; + uint8_t totalSectors16[2]; + uint8_t mediaType; + uint8_t sectorsPerFat16[2]; + uint8_t sectorsPerTrtack[2]; + uint8_t headCount[2]; + uint8_t hidddenSectors[4]; + uint8_t totalSectors32[4]; + + uint8_t physicalDriveNumber; + uint8_t extReserved; + uint8_t extSignature; + uint8_t volumeSerialNumber[4]; + uint8_t volumeLabel[11]; + uint8_t volumeType[8]; +} BpbFat16_t; +//------------------------------------------------------------------------------ +typedef struct biosParameterBlockFat32 { + uint8_t bytesPerSector[2]; + uint8_t sectorsPerCluster; + uint8_t reservedSectorCount[2]; + uint8_t fatCount; + uint8_t rootDirEntryCount[2]; + uint8_t totalSectors16[2]; + uint8_t mediaType; + uint8_t sectorsPerFat16[2]; + uint8_t sectorsPerTrtack[2]; + uint8_t headCount[2]; + uint8_t hidddenSectors[4]; + uint8_t totalSectors32[4]; + + uint8_t sectorsPerFat32[4]; + uint8_t fat32Flags[2]; + uint8_t fat32Version[2]; + uint8_t fat32RootCluster[4]; + uint8_t fat32FSInfoSector[2]; + uint8_t fat32BackBootSector[2]; + uint8_t fat32Reserved[12]; + + uint8_t physicalDriveNumber; + uint8_t extReserved; + uint8_t extSignature; + uint8_t volumeSerialNumber[4]; + uint8_t volumeLabel[11]; + uint8_t volumeType[8]; +} BpbFat32_t; +//------------------------------------------------------------------------------ +typedef struct partitionBootSectorFat { + uint8_t jmpInstruction[3]; + char oemName[8]; + union { + uint8_t bpb[109]; + BpbFat16_t bpb16; + BpbFat32_t bpb32; + } bpb; + uint8_t bootCode[390]; + uint8_t signature[2]; +} PbsFat_t; +//------------------------------------------------------------------------------ +const uint32_t FSINFO_LEAD_SIGNATURE = 0X41615252; +const uint32_t FSINFO_STRUCT_SIGNATURE = 0x61417272; +const uint32_t FSINFO_TRAIL_SIGNATURE = 0xAA550000; +typedef struct FsInfoSector { + uint8_t leadSignature[4]; + uint8_t reserved1[480]; + uint8_t structSignature[4]; + uint8_t freeCount[4]; + uint8_t nextFree[4]; + uint8_t reserved2[12]; + uint8_t trailSignature[4]; +} FsInfo_t; +//============================================================================== +/** Attributes common to FAT and exFAT */ +const uint8_t FS_ATTRIB_READ_ONLY = 0x01; +const uint8_t FS_ATTRIB_HIDDEN = 0x02; +const uint8_t FS_ATTRIB_SYSTEM = 0x04; +const uint8_t FS_ATTRIB_DIRECTORY = 0x10; +const uint8_t FS_ATTRIB_ARCHIVE = 0x20; +// Attributes that users can change. +const uint8_t FS_ATTRIB_USER_SETTABLE = FS_ATTRIB_READ_ONLY | + FS_ATTRIB_HIDDEN | FS_ATTRIB_SYSTEM | FS_ATTRIB_ARCHIVE; +// Attributes to copy when a file is opened. +const uint8_t FS_ATTRIB_COPY = FS_ATTRIB_USER_SETTABLE | FS_ATTRIB_DIRECTORY; +//============================================================================== +/** name[0] value for entry that is free and no allocated entries follow */ +const uint8_t FAT_NAME_FREE = 0X00; +/** name[0] value for entry that is free after being "deleted" */ +const uint8_t FAT_NAME_DELETED = 0XE5; +// Directiry attribute of volume label. +const uint8_t FAT_ATTRIB_LABEL = 0x08; +const uint8_t FAT_ATTRIB_LONG_NAME = 0X0F; +/** Filename base-name is all lower case */ +const uint8_t FAT_CASE_LC_BASE = 0X08; +/** Filename extension is all lower case.*/ +const uint8_t FAT_CASE_LC_EXT = 0X10; + +typedef struct { + uint8_t name[11]; + uint8_t attributes; + uint8_t caseFlags; + uint8_t createTimeMs; + uint8_t createTime[2]; + uint8_t createDate[2]; + uint8_t accessDate[2]; + uint8_t firstClusterHigh[2]; + uint8_t modifyTime[2]; + uint8_t modifyDate[2]; + uint8_t firstClusterLow[2]; + uint8_t fileSize[4]; +} DirFat_t; + +static inline bool isFatFile(const DirFat_t* dir) { + return (dir->attributes & (FS_ATTRIB_DIRECTORY | FAT_ATTRIB_LABEL)) == 0; +} +static inline bool isFatFileOrSubdir(const DirFat_t* dir) { + return (dir->attributes & FAT_ATTRIB_LABEL) == 0; +} +static inline uint8_t isFatLongName(const DirFat_t* dir) { + return dir->attributes == FAT_ATTRIB_LONG_NAME; +} +static inline bool isFatSubdir(const DirFat_t* dir) { + return (dir->attributes & (FS_ATTRIB_DIRECTORY | FAT_ATTRIB_LABEL)) + == FS_ATTRIB_DIRECTORY; +} +//------------------------------------------------------------------------------ +/** + * Order mask that indicates the entry is the last long dir entry in a + * set of long dir entries. All valid sets of long dir entries must + * begin with an entry having this mask. + */ +const uint8_t FAT_ORDER_LAST_LONG_ENTRY = 0X40; +/** Max long file name length */ + +const uint8_t FAT_MAX_LFN_LENGTH = 255; +typedef struct { + uint8_t order; + uint8_t unicode1[10]; + uint8_t attributes; + uint8_t mustBeZero1; + uint8_t checksum; + uint8_t unicode2[12]; + uint8_t mustBeZero2[2]; + uint8_t unicode3[4]; +} DirLfn_t; +//============================================================================== +inline uint32_t exFatChecksum(uint32_t sum, uint8_t data) { + return (sum << 31) + (sum >> 1) + data; +} +//------------------------------------------------------------------------------ +typedef struct biosParameterBlockExFat { + uint8_t mustBeZero[53]; + uint8_t partitionOffset[8]; + uint8_t volumeLength[8]; + uint8_t fatOffset[4]; + uint8_t fatLength[4]; + uint8_t clusterHeapOffset[4]; + uint8_t clusterCount[4]; + uint8_t rootDirectoryCluster[4]; + uint8_t volumeSerialNumber[4]; + uint8_t fileSystemRevision[2]; + uint8_t volumeFlags[2]; + uint8_t bytesPerSectorShift; + uint8_t sectorsPerClusterShift; + uint8_t numberOfFats; + uint8_t driveSelect; + uint8_t percentInUse; + uint8_t reserved[7]; +} BpbExFat_t; +//------------------------------------------------------------------------------ +typedef struct ExFatBootSector { + uint8_t jmpInstruction[3]; + char oemName[8]; + BpbExFat_t bpb; + uint8_t bootCode[390]; + uint8_t signature[2]; +} ExFatPbs_t; +//------------------------------------------------------------------------------ +const uint32_t EXFAT_EOC = 0XFFFFFFFF; + +const uint8_t EXFAT_TYPE_BITMAP = 0X81; +typedef struct { + uint8_t type; + uint8_t flags; + uint8_t reserved[18]; + uint8_t firstCluster[4]; + uint8_t size[8]; +} DirBitmap_t; +//------------------------------------------------------------------------------ +const uint8_t EXFAT_TYPE_UPCASE = 0X82; +typedef struct { + uint8_t type; + uint8_t reserved1[3]; + uint8_t checksum[4]; + uint8_t reserved2[12]; + uint8_t firstCluster[4]; + uint8_t size[8]; +} DirUpcase_t; +//------------------------------------------------------------------------------ +const uint8_t EXFAT_TYPE_LABEL = 0X83; +typedef struct { + uint8_t type; + uint8_t labelLength; + uint8_t unicode[22]; + uint8_t reserved[8]; +} DirLabel_t; +//------------------------------------------------------------------------------ +// Last entry in directory. +const uint8_t EXFAT_TYPE_END_DIR = 0X00; +// Entry is used if bit is set. +const uint8_t EXFAT_TYPE_USED = 0X80; +const uint8_t EXFAT_TYPE_FILE = 0X85; +// File attribute reserved since used for FAT volume label. +const uint8_t EXFAT_ATTRIB_RESERVED = 0x08; + +typedef struct { + uint8_t type; + uint8_t setCount; + uint8_t setChecksum[2]; + uint8_t attributes[2]; + uint8_t reserved1[2]; + uint8_t createTime[2]; + uint8_t createDate[2]; + uint8_t modifyTime[2]; + uint8_t modifyDate[2]; + uint8_t accessTime[2]; + uint8_t accessDate[2]; + uint8_t createTimeMs; + uint8_t modifyTimeMs; + uint8_t createTimezone; + uint8_t modifyTimezone; + uint8_t accessTimezone; + uint8_t reserved2[7]; +} DirFile_t; + +const uint8_t EXFAT_TYPE_STREAM = 0XC0; +const uint8_t EXFAT_FLAG_ALWAYS1 = 0x01; +const uint8_t EXFAT_FLAG_CONTIGUOUS = 0x02; +typedef struct { + uint8_t type; + uint8_t flags; + uint8_t reserved1; + uint8_t nameLength; + uint8_t nameHash[2]; + uint8_t reserved2[2]; + uint8_t validLength[8]; + uint8_t reserved3[4]; + uint8_t firstCluster[4]; + uint8_t dataLength[8]; +} DirStream_t; + +const uint8_t EXFAT_TYPE_NAME = 0XC1; +const uint8_t EXFAT_MAX_NAME_LENGTH = 255; +typedef struct { + uint8_t type; + uint8_t mustBeZero; + uint8_t unicode[30]; +} DirName_t; +#endif // FsStructs_h diff --git a/avr/cores/megacommand/SdFat/common/FsUtf.cpp b/avr/cores/megacommand/SdFat/common/FsUtf.cpp new file mode 100644 index 000000000..b6d2e89f4 --- /dev/null +++ b/avr/cores/megacommand/SdFat/common/FsUtf.cpp @@ -0,0 +1,115 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "FsUtf.h" +namespace FsUtf { + //---------------------------------------------------------------------------- + char* cpToMb(uint32_t cp, char* str, char* end) { + size_t n = end - str; + if (cp < 0X80) { + if (n < 1) goto fail; + *(str++) = static_cast(cp); + } else if (cp < 0X800) { + if (n < 2) goto fail; + *(str++) = static_cast((cp >> 6) | 0XC0); + *(str++) = static_cast((cp & 0X3F) | 0X80); + } else if (cp < 0X10000) { + if (n < 3) goto fail; + *(str++) = static_cast((cp >> 12) | 0XE0); + *(str++) = static_cast(((cp >> 6) & 0X3F) | 0X80); + *(str++) = static_cast((cp & 0X3F) | 0X80); + } else { + if (n < 4) goto fail; + *(str++) = static_cast((cp >> 18) | 0XF0); + *(str++) = static_cast(((cp >> 12) & 0X3F)| 0X80); + *(str++) = static_cast(((cp >> 6) & 0X3F) | 0X80); + *(str++) = static_cast((cp & 0X3F) | 0X80); + } + return str; + + fail: + return nullptr; + } + //---------------------------------------------------------------------------- + // to do? improve error check + const char* mbToCp(const char* str, const char* end, uint32_t* rtn) { + size_t n; + uint32_t cp; + if (str >= end) { + return nullptr; + } + uint8_t ch = str[0]; + if ((ch & 0X80) == 0) { + *rtn = ch; + return str + 1; + } + if ((ch & 0XE0) == 0XC0) { + cp = ch & 0X1F; + n = 2; + } else if ((ch & 0XF0) == 0XE0) { + cp = ch & 0X0F; + n = 3; + } else if ((ch & 0XF8) == 0XF0) { + cp = ch & 0X07; + n = 4; + } else { + return nullptr; + } + if ((str + n) > end) { + return nullptr; + } + for (size_t i = 1; i < n; i++) { + ch = str[i]; + if ((ch & 0XC0) != 0X80) { + return nullptr; + } + cp <<= 6; + cp |= ch & 0X3F; + } + // Don't allow over long as ASCII. + if (cp < 0X80 || !isValidCp(cp)) { + return nullptr; + } + *rtn = cp; + return str + n; + } + //---------------------------------------------------------------------------- + const char* mbToU16(const char* str, + const char* end, uint16_t* hs, uint16_t* ls) { + uint32_t cp; + const char* ptr = mbToCp(str, end, &cp); + if (!ptr) { + return nullptr; + } + if (cp <= 0XFFFF) { + *hs = cp; + *ls = 0; + } else { + *hs = highSurrogate(cp); + *ls = lowSurrogate(cp); + } + return ptr; + } +} // namespace FsUtf + diff --git a/avr/cores/megacommand/SdFat/common/FsUtf.h b/avr/cores/megacommand/SdFat/common/FsUtf.h new file mode 100644 index 000000000..6a69743b0 --- /dev/null +++ b/avr/cores/megacommand/SdFat/common/FsUtf.h @@ -0,0 +1,109 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef FsUtf_h +#define FsUtf_h +/** +* \file +* \brief Unicode Transformation Format functions. +*/ +#include +#include +namespace FsUtf { + /** High surrogate for a code point. + * \param{in} cp code point. + * \return high surrogate. + */ + inline uint16_t highSurrogate(uint32_t cp) { + return (cp >> 10) + (0XD800 - (0X10000 >> 10)); + } + /** Low surrogate for a code point. + * \param{in} cp code point. + * \return low surrogate. + */ + inline uint16_t lowSurrogate(uint32_t cp) { + return (cp & 0X3FF) + 0XDC00; + } + /** Check for a valid code point. + * \param[in] cp code point. + * \return true if valid else false. + */ + inline bool isValidCp(uint32_t cp) { + return cp <= 0x10FFFF && (cp < 0XD800 || cp > 0XDFFF); + } + /** Check for UTF-16 surrogate. + * \param[in] c UTF-16 unit. + * \return true if c is a surrogate else false. + */ + inline bool isSurrogate(uint16_t c) { + return 0XD800 <= c && c <= 0XDFFF; + } + /** Check for UTF-16 high surrogate. + * \param[in] c UTF-16 unit.. + * \return true if c is a high surrogate else false. + */ + inline bool isHighSurrogate(uint16_t c) { + return 0XD800 <= c && c <= 0XDBFF; + } + /** Check for UTF-16 low surrogate. + * \param[in] c UTF-16 unit.. + * \return true if c is a low surrogate else false. + */ + inline bool isLowSurrogate(uint16_t c) { + return 0XDC00 <= c && c <= 0XDFFF; + } + /** Convert UFT-16 surrogate pair to code point. + * \param[in] hs high surrogate. + * \param[in] ls low surrogate. + * \return code point. + */ + inline uint32_t u16ToCp(uint16_t hs, uint16_t ls) { + return 0X10000 + (((hs & 0X3FF) << 10) | (ls & 0X3FF)); + } + /** Encodes a 32 bit code point as a UTF-8 sequence. + * \param[in] cp code point to encode. + * \param[out] str location for UTF-8 sequence. + * \param[in] end location following last character of str. + * \return location one beyond last encoded character. + */ + char* cpToMb(uint32_t cp, char* str, char* end); + /** Get next code point from a UTF-8 sequence. + * \param[in] str location for UTF-8 sequence. + * \param[in] end location following last character of str. + * May be nullptr if str is zero terminated. + * \param[out] rtn location for the code point. + * \return location of next UTF-8 character in str of nullptr for error. + */ + const char* mbToCp(const char* str, const char* end, uint32_t* rtn); + /** Get next code point from a UTF-8 sequence as UTF-16. + * \param[in] str location for UTF-8 sequence. + * \param[in] end location following last character of str. + * \param[out] hs location for the code point or high surrogate. + * \param[out] ls location for zero or high surrogate. + * \return location of next UTF-8 character in str of nullptr for error. + */ + const char* mbToU16(const char* str, + const char* end, uint16_t* hs, uint16_t* ls); +} // namespace FsUtf +#endif // FsUtf_h diff --git a/avr/cores/megacommand/SdFat/common/PrintBasic.cpp b/avr/cores/megacommand/SdFat/common/PrintBasic.cpp new file mode 100644 index 000000000..c0c6d5812 --- /dev/null +++ b/avr/cores/megacommand/SdFat/common/PrintBasic.cpp @@ -0,0 +1,91 @@ +/** + * Copyright (c) 2011-2020 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "PrintBasic.h" +#if ENABLE_ARDUINO_FEATURES == 0 +#include + +size_t PrintBasic::print(long n, uint8_t base) { + if (n < 0 && base == 10) { + return print('-') + printNum(-n, base); + } + return printNum(n, base); +} +size_t PrintBasic::printNum(unsigned long n, uint8_t base) { + const uint8_t DIM = 8*sizeof(long); + char buf[DIM]; + char *str = &buf[DIM]; + + if (base < 2) return 0; + + do { + char c = n%base; + n /= base; + *--str = c + (c < 10 ? '0' : 'A' - 10); + } while (n); + return write(str, &buf[DIM] - str); +} + +size_t PrintBasic::printDouble(double n, uint8_t prec) { + // Max printable 32-bit floating point number. AVR uses 32-bit double. + const double maxfp = static_cast(0XFFFFFF00UL); + size_t rtn = 0; + + if (isnan(n)) { + return write("NaN"); + } + if (n < 0) { + n = -n; + rtn += print('-'); + } + if (isinf(n)) { + return rtn + write("Inf"); + } + if (n > maxfp) { + return rtn + write("Ovf"); + } + + double round = 0.5; + for (uint8_t i = 0; i < prec; ++i) { + round *= 0.1; + } + + n += round; + + uint32_t whole = (uint32_t)n; + rtn += print(whole); + + if (prec) { + rtn += print('.'); + double fraction = n - static_cast(whole); + for (uint8_t i = 0; i < prec; i++) { + fraction *= 10.0; + uint8_t digit = fraction; + rtn += print(digit); + fraction -= digit; + } + } + return rtn; +} +#endif // ENABLE_ARDUINO_FEATURES == 0 diff --git a/avr/cores/megacommand/SdFat/common/PrintBasic.h b/avr/cores/megacommand/SdFat/common/PrintBasic.h new file mode 100644 index 000000000..f17437158 --- /dev/null +++ b/avr/cores/megacommand/SdFat/common/PrintBasic.h @@ -0,0 +1,169 @@ +/** + * Copyright (c) 2011-2020 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef PrintBasic_h +#define PrintBasic_h +/** + * \file + * \brief Stream/Print like replacement for non-Arduino systems. + */ +#include +#include +#include +#include "../SdFatConfig.h" + +#ifndef F +#if defined(__AVR__) +#include +class __FlashStringHelper; +#define F(string_literal) (reinterpret_cast(PSTR(string_literal))) +#else // defined(__AVR__) +#define F(str) (str) +#endif // defined(__AVR__) +#endif // F + +#ifdef BIN +#undef BIN +#endif // BIN +#define BIN 2 +#define OCT 8 +#define DEC 10 +#define HEX 16 + +class PrintBasic { + public: + PrintBasic() : m_error(0) {} + + void clearWriteError() { + setWriteError(0); + } + int getWriteError() { + return m_error; + } + size_t print(char c) { + return write(c); + } + size_t print(const char* str) { + return write(str); + } + size_t print(const __FlashStringHelper *str) { +#ifdef __AVR__ + PGM_P p = reinterpret_cast(str); + size_t n = 0; + for (uint8_t c; (c = pgm_read_byte(p + n)) && write(c); n++) {} + return n; +#else // __AVR__ + return print(reinterpret_cast(str)); +#endif // __AVR__ + } + size_t println(const __FlashStringHelper *str) { +#ifdef __AVR__ + return print(str) + println(); +#else // __AVR__ + return println(reinterpret_cast(str)); +#endif // __AVR__ + } + size_t print(double n, uint8_t prec = 2) { + return printDouble(n, prec); + } + size_t print(signed char n, uint8_t base = 10) { + return print((long)n, base); + } + size_t print(unsigned char n, uint8_t base = 10) { + return print((unsigned long)n, base); + } + size_t print(int n, uint8_t base = 10) { + return print((long)n, base); + } + size_t print(unsigned int n, uint8_t base = 10) { + return print((unsigned long)n, base); + } + size_t print(long n, uint8_t base = 10); + size_t print(unsigned long n, uint8_t base = 10) { + return printNum(n, base); + } + size_t println() { + return write("\r\n"); + } + size_t println(char c) { + return write(c) + println(); + } + size_t println(const char* str) { + return print(str) + println(); + } + size_t println(double n, uint8_t prec = 2) { + return print(n, prec) + println(); + } + size_t println(signed char n, uint8_t base = 10) { + return print(n, base) + println(); + } + size_t println(unsigned char n, uint8_t base = 10) { + return print(n, base) + println(); + } + size_t println(int n, uint8_t base = 10) { + return print(n, base) + println(); + } + size_t println(unsigned int n, uint8_t base = 10) { + return print(n, base) + println(); + } + size_t println(long n, uint8_t base = 10) { + return print(n, base) + println(); + } + size_t println(unsigned long n, uint8_t base = 10) { + return print(n, base) + println(); + } + size_t write(const char *str) { + return write(str, strlen(str)); + } + virtual size_t write(uint8_t b) = 0; + + virtual size_t write(const uint8_t* buffer, size_t size) { + size_t i; + for (i = 0; i < size; i++) { + if (!write(buffer[i])) break; + } + return i; + } + size_t write(const char *buffer, size_t size) { + return write((const uint8_t*)buffer, size); + } + + protected: + void setWriteError(int err = 1) { + m_error = err; + } + + private: + size_t printDouble(double n, uint8_t prec); + size_t printNum(unsigned long n, uint8_t base); + int m_error; +}; +//------------------------------------------------------------------------------ +class StreamBasic : public PrintBasic { + public: + virtual int available() = 0; + virtual int peek() = 0; + virtual int read() = 0; +}; +#endif // PrintBasic_h diff --git a/avr/cores/megacommand/SdFat/SysCall.h b/avr/cores/megacommand/SdFat/common/SysCall.h similarity index 59% rename from avr/cores/megacommand/SdFat/SysCall.h rename to avr/cores/megacommand/SdFat/common/SysCall.h index 02af4c0c5..9e5da9c49 100644 --- a/avr/cores/megacommand/SdFat/SysCall.h +++ b/avr/cores/megacommand/SdFat/common/SysCall.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 20011-2017 Bill Greiman + * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -22,67 +22,41 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ -#ifndef SysCall_h -#define SysCall_h /** * \file * \brief SysCall class */ +#ifndef SysCall_h +#define SysCall_h +#include +#include +#include "../SdFatConfig.h" +#if __cplusplus < 201103 +#warning nullptr defined +/** Define nullptr if not C++11 */ +#define nullptr NULL +#endif // __cplusplus < 201103 +//------------------------------------------------------------------------------ +#if ENABLE_ARDUINO_FEATURES #if defined(ARDUINO) -#include -#include -#elif defined(PLATFORM_ID) // Only defined if a Particle device -#include "application.h" +/** Use Arduino Print. */ +typedef Print print_t; +/** Use Arduino Stream. */ +typedef Stream stream_t; #else // defined(ARDUINO) #error "Unknown system" #endif // defined(ARDUINO) -//----------------------------------------------------------------------------- -#ifdef ESP8266 -// undefine F macro if ESP8266. -#undef F -#endif // ESP8266 -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ #ifndef F /** Define macro for strings stored in flash. */ #define F(str) (str) #endif // F -//----------------------------------------------------------------------------- -/** \return the time in milliseconds. */ -inline uint16_t curTimeMS() { - return millis(); -} -//----------------------------------------------------------------------------- -/** - * \class SysCall - * \brief SysCall - Class to wrap system calls. - */ -class SysCall { - public: - /** Halt execution of this thread. */ - static void halt() { - while (1) { - yield(); - } - } - /** Yield to other threads. */ - static void yield(); -}; - -#if defined(ESP8266) -inline void SysCall::yield() { - // Avoid ESP8266 bug - delay(0); -} -#elif defined(ARDUINO) -inline void SysCall::yield() { - // Use the external Arduino yield() function. - ::yield(); -} -#elif defined(PLATFORM_ID) // Only defined if a Particle device -inline void SysCall::yield() { - Particle.process(); -} -#else // ESP8266 -inline void SysCall::yield() {} -#endif // ESP8266 +//------------------------------------------------------------------------------ +#else // ENABLE_ARDUINO_FEATURES +#include "PrintBasic.h" +/** If not Arduino */ +typedef PrintBasic print_t; +/** If not Arduino */ +typedef PrintBasic stream_t; +#endif // ENABLE_ARDUINO_FEATURES #endif // SysCall_h diff --git a/avr/cores/megacommand/SdFat/common/upcase.cpp b/avr/cores/megacommand/SdFat/common/upcase.cpp new file mode 100644 index 000000000..3756cff24 --- /dev/null +++ b/avr/cores/megacommand/SdFat/common/upcase.cpp @@ -0,0 +1,228 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include +#include "upcase.h" +#ifdef __AVR__ +#include +#define TABLE_MEM PROGMEM +#define readTable8(sym) pgm_read_byte(&sym) +#define readTable16(sym) pgm_read_word(&sym) +#else // __AVR__ +#define TABLE_MEM +#define readTable8(sym) (sym) +#define readTable16(sym) (sym) +#endif // __AVR__ + +struct map16 { + uint16_t base; + int8_t off; + uint8_t count; +}; +typedef struct map16 map16_t; + +struct pair16 { + uint16_t key; + uint16_t val; +}; +typedef struct pair16 pair16_t; +//------------------------------------------------------------------------------ +static const map16_t mapTable[] TABLE_MEM = { + {0X0061, -32, 26}, + {0X00E0, -32, 23}, + {0X00F8, -32, 7 }, + {0X0100, 1, 48}, + {0X0132, 1, 6}, + {0X0139, 1, 16}, + {0X014A, 1, 46}, + {0X0179, 1, 6}, + {0X0182, 1, 4}, + {0X01A0, 1, 6}, + {0X01B3, 1, 4}, + {0X01CD, 1, 16}, + {0X01DE, 1, 18}, + {0X01F8, 1, 40}, + {0X0222, 1, 18}, + {0X0246, 1, 10}, + {0X03AD, -37, 3}, + {0X03B1, -32, 17}, + {0X03C3, -32, 9}, + {0X03D8, 1, 24}, + {0X0430, -32, 32}, + {0X0450, -80, 16}, + {0X0460, 1, 34}, + {0X048A, 1, 54}, + {0X04C1, 1, 14}, + {0X04D0, 1, 68}, + {0X0561, -48, 38}, + {0X1E00, 1, 150}, + {0X1EA0, 1, 90}, + {0X1F00, 8, 8}, + {0X1F10, 8, 6}, + {0X1F20, 8, 8}, + {0X1F30, 8, 8}, + {0X1F40, 8, 6}, + {0X1F60, 8, 8}, + {0X1F70, 74, 2}, + {0X1F72, 86, 4}, + {0X1F76, 100, 2}, + {0X1F7A, 112, 2}, + {0X1F7C, 126, 2}, + {0X1F80, 8, 8}, + {0X1F90, 8, 8}, + {0X1FA0, 8, 8}, + {0X1FB0, 8, 2}, + {0X1FD0, 8, 2}, + {0X1FE0, 8, 2}, + {0X2170, -16, 16}, + {0X24D0, -26, 26}, + {0X2C30, -48, 47}, + {0X2C67, 1, 6}, + {0X2C80, 1, 100}, + {0X2D00, 0, 38}, + {0XFF41, -32, 26}, +}; +const size_t MAP_DIM = sizeof(mapTable)/sizeof(map16_t); +//------------------------------------------------------------------------------ +static const pair16_t lookupTable[] TABLE_MEM = { + {0X00FF, 0X0178}, + {0X0180, 0X0243}, + {0X0188, 0X0187}, + {0X018C, 0X018B}, + {0X0192, 0X0191}, + {0X0195, 0X01F6}, + {0X0199, 0X0198}, + {0X019A, 0X023D}, + {0X019E, 0X0220}, + {0X01A8, 0X01A7}, + {0X01AD, 0X01AC}, + {0X01B0, 0X01AF}, + {0X01B9, 0X01B8}, + {0X01BD, 0X01BC}, + {0X01BF, 0X01F7}, + {0X01C6, 0X01C4}, + {0X01C9, 0X01C7}, + {0X01CC, 0X01CA}, + {0X01DD, 0X018E}, + {0X01F3, 0X01F1}, + {0X01F5, 0X01F4}, + {0X023A, 0X2C65}, + {0X023C, 0X023B}, + {0X023E, 0X2C66}, + {0X0242, 0X0241}, + {0X0253, 0X0181}, + {0X0254, 0X0186}, + {0X0256, 0X0189}, + {0X0257, 0X018A}, + {0X0259, 0X018F}, + {0X025B, 0X0190}, + {0X0260, 0X0193}, + {0X0263, 0X0194}, + {0X0268, 0X0197}, + {0X0269, 0X0196}, + {0X026B, 0X2C62}, + {0X026F, 0X019C}, + {0X0272, 0X019D}, + {0X0275, 0X019F}, + {0X027D, 0X2C64}, + {0X0280, 0X01A6}, + {0X0283, 0X01A9}, + {0X0288, 0X01AE}, + {0X0289, 0X0244}, + {0X028A, 0X01B1}, + {0X028B, 0X01B2}, + {0X028C, 0X0245}, + {0X0292, 0X01B7}, + {0X037B, 0X03FD}, + {0X037C, 0X03FE}, + {0X037D, 0X03FF}, + {0X03AC, 0X0386}, + {0X03C2, 0X03A3}, + {0X03CC, 0X038C}, + {0X03CD, 0X038E}, + {0X03CE, 0X038F}, + {0X03F2, 0X03F9}, + {0X03F8, 0X03F7}, + {0X03FB, 0X03FA}, + {0X04CF, 0X04C0}, + {0X1D7D, 0X2C63}, + {0X1F51, 0X1F59}, + {0X1F53, 0X1F5B}, + {0X1F55, 0X1F5D}, + {0X1F57, 0X1F5F}, + {0X1F78, 0X1FF8}, + {0X1F79, 0X1FF9}, + {0X1FB3, 0X1FBC}, + {0X1FCC, 0X1FC3}, + {0X1FE5, 0X1FEC}, + {0X1FFC, 0X1FF3}, + {0X214E, 0X2132}, + {0X2184, 0X2183}, + {0X2C61, 0X2C60}, + {0X2C76, 0X2C75}, +}; +const size_t LOOKUP_DIM = sizeof(lookupTable)/sizeof(pair16_t); +//------------------------------------------------------------------------------ +static size_t searchPair16(const pair16_t* table, size_t size, uint16_t key) { + size_t left = 0; + size_t right = size; + size_t mid; + while (right - left > 1) { + mid = left + (right - left)/2; + if (readTable16(table[mid].key) <= key) { + left = mid; + } else { + right = mid; + } + } + return left; +} +//------------------------------------------------------------------------------ +uint16_t toUpcase(uint16_t chr) { + uint16_t i, first; + // Optimize for simple ASCII. + if (chr < 127) { + return chr - ('a' <= chr && chr <= 'z' ? 'a' - 'A' : 0); + } + i = searchPair16(reinterpret_cast(mapTable), MAP_DIM, chr); + first = readTable16(mapTable[i].base); + if (first <= chr && (chr - first) < readTable8(mapTable[i].count)) { + int8_t off = readTable8(mapTable[i].off); + if (off == 1) { + return chr - ((chr - first) & 1); + } + return chr + (off ? off : -0x1C60); + } + i = searchPair16(lookupTable, LOOKUP_DIM, chr); + if (readTable16(lookupTable[i].key) == chr) { + return readTable16(lookupTable[i].val); + } + return chr; +} +//------------------------------------------------------------------------------ +uint32_t upcaseChecksum(uint16_t uc, uint32_t sum) { + sum = (sum << 31) + (sum >> 1) + (uc & 0XFF); + sum = (sum << 31) + (sum >> 1) + (uc >> 8); + return sum; +} diff --git a/avr/cores/megacommand/SdFat/common/upcase.h b/avr/cores/megacommand/SdFat/common/upcase.h new file mode 100644 index 000000000..1a8ece9a5 --- /dev/null +++ b/avr/cores/megacommand/SdFat/common/upcase.h @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef upcase_h +#define upcase_h +#include +uint16_t toUpcase(uint16_t chr); +uint32_t upcaseChecksum(uint16_t unicode, uint32_t checksum); +#endif // upcase_h diff --git a/avr/cores/megacommand/SdFat/FatLib/ArduinoStream.h b/avr/cores/megacommand/SdFat/iostream/ArduinoStream.h similarity index 94% rename from avr/cores/megacommand/SdFat/FatLib/ArduinoStream.h rename to avr/cores/megacommand/SdFat/iostream/ArduinoStream.h index 8cbb9985c..735f36f11 100644 --- a/avr/cores/megacommand/SdFat/FatLib/ArduinoStream.h +++ b/avr/cores/megacommand/SdFat/iostream/ArduinoStream.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 20011-2017 Bill Greiman + * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -28,8 +28,6 @@ * \file * \brief ArduinoInStream and ArduinoOutStream classes */ -#include "FatLibConfig.h" -#if ENABLE_ARDUINO_FEATURES #include "bufstream.h" //============================================================================== /** @@ -112,7 +110,7 @@ class ArduinoOutStream : public ostream { * * \param[in] pr Print object for this ArduinoOutStream. */ - explicit ArduinoOutStream(Print& pr) : m_pr(&pr) {} + explicit ArduinoOutStream(print_t& pr) : m_pr(&pr) {} protected: /// @cond SHOW_PROTECTED @@ -147,7 +145,6 @@ class ArduinoOutStream : public ostream { /// @endcond private: ArduinoOutStream() {} - Print* m_pr; + print_t* m_pr; }; -#endif // ENABLE_ARDUINO_FEATURES #endif // ArduinoStream_h diff --git a/avr/cores/megacommand/SdFat/FatLib/StdioStream.cpp b/avr/cores/megacommand/SdFat/iostream/StdioStream.cpp similarity index 73% rename from avr/cores/megacommand/SdFat/FatLib/StdioStream.cpp rename to avr/cores/megacommand/SdFat/iostream/StdioStream.cpp index 0fbfc1829..9c36f35eb 100644 --- a/avr/cores/megacommand/SdFat/FatLib/StdioStream.cpp +++ b/avr/cores/megacommand/SdFat/iostream/StdioStream.cpp @@ -1,5 +1,5 @@ /** - * Copyright (c) 20011-2017 Bill Greiman + * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -23,30 +23,30 @@ * DEALINGS IN THE SOFTWARE. */ #include "StdioStream.h" -#include "FmtNumber.h" +#include "../common/FmtNumber.h" //------------------------------------------------------------------------------ int StdioStream::fclose() { int rtn = 0; - if (!m_flags) { + if (!m_status) { return EOF; } - if (m_flags & F_SWR) { + if (m_status & S_SWR) { if (!flushBuf()) { rtn = EOF; } } - if (!FatFile::close()) { + if (!StreamBaseFile::close()) { rtn = EOF; } m_r = 0; m_w = 0; - m_flags = 0; + m_status = 0; return rtn; } //------------------------------------------------------------------------------ int StdioStream::fflush() { - if ((m_flags & (F_SWR | F_SRW)) && !(m_flags & F_SRD)) { - if (flushBuf() && FatFile::sync()) { + if ((m_status & (S_SWR | S_SRW)) && !(m_status & S_SRD)) { + if (flushBuf() && StreamBaseFile::sync()) { return 0; } } @@ -95,21 +95,25 @@ char* StdioStream::fgets(char* str, size_t num, size_t* len) { } //------------------------------------------------------------------------------ bool StdioStream::fopen(const char* path, const char* mode) { - uint8_t oflag; + oflag_t oflag; + uint8_t m; switch (*mode++) { case 'a': - m_flags = F_SWR; - oflag = O_WRITE | O_CREAT | O_APPEND | O_AT_END; + m = O_WRONLY; + oflag = O_CREAT | O_APPEND; + m_status = S_SWR; break; case 'r': - m_flags = F_SRD; - oflag = O_READ; + m = O_RDONLY; + oflag = 0; + m_status = S_SRD; break; case 'w': - m_flags = F_SWR; - oflag = O_WRITE | O_CREAT | O_TRUNC; + m = O_WRONLY; + oflag = O_CREAT | O_TRUNC; + m_status = S_SWR; break; default: @@ -118,8 +122,8 @@ bool StdioStream::fopen(const char* path, const char* mode) { while (*mode) { switch (*mode++) { case '+': - m_flags |= F_SRW; - oflag |= O_RDWR; + m_status = S_SRW; + m = O_RDWR; break; case 'b': @@ -133,10 +137,8 @@ bool StdioStream::fopen(const char* path, const char* mode) { goto fail; } } - if ((oflag & O_EXCL) && !(oflag & O_WRITE)) { - goto fail; - } - if (!FatFile::open(path, oflag)) { + oflag |= m; + if (!StreamBaseFile::open(path, oflag)) { goto fail; } m_r = 0; @@ -144,8 +146,8 @@ bool StdioStream::fopen(const char* path, const char* mode) { m_p = m_buf; return true; -fail: - m_flags = 0; + fail: + m_status = 0; return false; } //------------------------------------------------------------------------------ @@ -178,7 +180,7 @@ size_t StdioStream::fread(void* ptr, size_t size, size_t count) { //------------------------------------------------------------------------------ int StdioStream::fseek(int32_t offset, int origin) { int32_t pos; - if (m_flags & F_SWR) { + if (m_status & S_SWR) { if (!flushBuf()) { goto fail; } @@ -190,19 +192,19 @@ int StdioStream::fseek(int32_t offset, int origin) { goto fail; } pos += offset; - if (!FatFile::seekCur(pos)) { + if (!StreamBaseFile::seekCur(pos)) { goto fail; } break; case SEEK_SET: - if (!FatFile::seekSet(offset)) { + if (!StreamBaseFile::seekSet(offset)) { goto fail; } break; case SEEK_END: - if (!FatFile::seekEnd(offset)) { + if (!StreamBaseFile::seekEnd(offset)) { goto fail; } break; @@ -214,18 +216,18 @@ int StdioStream::fseek(int32_t offset, int origin) { m_p = m_buf; return 0; -fail: + fail: return EOF; } //------------------------------------------------------------------------------ int32_t StdioStream::ftell() { - uint32_t pos = FatFile::curPosition(); - if (m_flags & F_SRD) { + uint32_t pos = StreamBaseFile::curPosition(); + if (m_status & S_SRD) { if (m_r > pos) { return -1L; } pos -= m_r; - } else if (m_flags & F_SWR) { + } else if (m_status & S_SWR) { pos += m_p - m_buf; } return pos; @@ -233,28 +235,6 @@ int32_t StdioStream::ftell() { //------------------------------------------------------------------------------ size_t StdioStream::fwrite(const void* ptr, size_t size, size_t count) { return write(ptr, count*size) < 0 ? EOF : count; -#if 0 //////////////////////////////////////////////////////////////////////////////////// - const uint8_t* src = static_cast(ptr); - size_t total = count*size; - if (total == 0) { - return 0; - } - size_t todo = total; - - while (todo > m_w) { - memcpy(m_p, src, m_w); - m_p += m_w; - src += m_w; - todo -= m_w; - if (!flushBuf()) { - return (total - todo)/size; - } - } - memcpy(m_p, src, todo); - m_p += todo; - m_w -= todo; - return count; -#endif ////////////////////////////////////////////////////////////////////////////////// } //------------------------------------------------------------------------------ int StdioStream::write(const void* buf, size_t count) { @@ -292,7 +272,7 @@ size_t StdioStream::print(const __FlashStringHelper *str) { //------------------------------------------------------------------------------ int StdioStream::printDec(float value, uint8_t prec) { char buf[24]; - char *ptr = fmtFloat(value, buf + sizeof(buf), prec); + char *ptr = fmtDouble(buf + sizeof(buf), value, prec, false); return write(ptr, buf + sizeof(buf) - ptr); } //------------------------------------------------------------------------------ @@ -323,26 +303,10 @@ int StdioStream::printDec(int16_t n) { } //------------------------------------------------------------------------------ int StdioStream::printDec(uint16_t n) { -#define NEW_WAY -#ifdef NEW_WAY char buf[5]; - char *ptr = fmtDec(n, buf + sizeof(buf)); + char *ptr = fmtBase10(buf + sizeof(buf), n); uint8_t len = buf + sizeof(buf) - ptr; return write(ptr, len); -#else - uint8_t len; - if (n < 100) { - len = n < 10 ? 1 : 2; - } else { - len = n < 1000 ? 3 : n < 10000 ? 4 : 5; - } - char* str = fmtSpace(len); - if (!str) { - return -1; - } - fmtDec(n, str); - return len; -#endif } //------------------------------------------------------------------------------ int StdioStream::printDec(int32_t n) { @@ -359,65 +323,26 @@ int StdioStream::printDec(int32_t n) { } //------------------------------------------------------------------------------ int StdioStream::printDec(uint32_t n) { -#ifdef NEW_WAY char buf[10]; - char *ptr = fmtDec(n, buf + sizeof(buf)); + char *ptr = fmtBase10(buf + sizeof(buf), n); uint8_t len = buf + sizeof(buf) - ptr; return write(ptr, len); -#else - uint8_t len; - if (n < 0X10000) { - return printDec((uint16_t)n); - } - if (n < 10000000) { - len = n < 100000 ? 5 : n < 1000000 ? 6 : 7; - } else { - len = n < 100000000 ? 8 : n < 1000000000 ? 9 : 10; - } - - char* str = fmtSpace(len); - if (!str) { - return -1; - } - fmtDec(n, str); - return len; -#endif } //------------------------------------------------------------------------------ int StdioStream::printHex(uint32_t n) { -#ifdef NEW_WAY char buf[8]; - char *ptr = fmtHex(n, buf + sizeof(buf)); + char *ptr = fmtHex(buf + sizeof(buf), n); uint8_t len = buf + sizeof(buf) - ptr; return write(ptr, len); -#else - size_t len; - if (n < 0X10000) { - len = n < 0X10 ? 1 : n < 0X100 ? 2 : n < 0X1000 ? 3 : 4; - } else { - len = n < 0X100000 ? 5 : n < 0X1000000 ? 6 : n < 0X10000000 ? 7 : 8; - } - char* str = fmtSpace(len); - if (!str) { - return -1; - } - - do { - uint8_t h = n & 0XF; - *str-- = h + (h < 10 ? '0' : 'A' - 10); - n >>= 4; - } while (n); - return len; -#endif } //------------------------------------------------------------------------------ bool StdioStream::rewind() { - if (m_flags & F_SWR) { + if (m_status & S_SWR) { if (!flushBuf()) { return false; } } - FatFile::seekSet(0); + StreamBaseFile::seekSet(0); m_r = 0; return true; } @@ -428,7 +353,7 @@ int StdioStream::ungetc(int c) { return EOF; } // error if not reading. - if ((m_flags & F_SRD) == 0) { + if ((m_status & S_SRD) == 0) { return EOF; } // error if no space. @@ -436,7 +361,7 @@ int StdioStream::ungetc(int c) { return EOF; } m_r++; - m_flags &= ~F_EOF; + m_status &= ~S_EOF; return *--m_p = (uint8_t)c; } //============================================================================== @@ -452,25 +377,25 @@ int StdioStream::fillGet() { //------------------------------------------------------------------------------ // private bool StdioStream::fillBuf() { - if (!(m_flags & - F_SRD)) { // check for F_ERR and F_EOF ??///////////////// - if (!(m_flags & F_SRW)) { - m_flags |= F_ERR; + if (!(m_status & + S_SRD)) { // check for S_ERR and S_EOF ??///////////////// + if (!(m_status & S_SRW)) { + m_status |= S_ERR; return false; } - if (m_flags & F_SWR) { + if (m_status & S_SWR) { if (!flushBuf()) { return false; } - m_flags &= ~F_SWR; - m_flags |= F_SRD; + m_status &= ~S_SWR; + m_status |= S_SRD; m_w = 0; } } m_p = m_buf + UNGETC_BUF_SIZE; - int nr = FatFile::read(m_p, sizeof(m_buf) - UNGETC_BUF_SIZE); + int nr = StreamBaseFile::read(m_p, sizeof(m_buf) - UNGETC_BUF_SIZE); if (nr <= 0) { - m_flags |= nr < 0 ? F_ERR : F_EOF; + m_status |= nr < 0 ? S_ERR : S_EOF; m_r = 0; return false; } @@ -480,14 +405,14 @@ bool StdioStream::fillBuf() { //------------------------------------------------------------------------------ // private bool StdioStream::flushBuf() { - if (!(m_flags & - F_SWR)) { // check for F_ERR ??//////////////////////// - if (!(m_flags & F_SRW)) { - m_flags |= F_ERR; + if (!(m_status & + S_SWR)) { // check for S_ERR ??//////////////////////// + if (!(m_status & S_SRW)) { + m_status |= S_ERR; return false; } - m_flags &= ~F_SRD; - m_flags |= F_SWR; + m_status &= ~S_SRD; + m_status |= S_SWR; m_r = 0; m_w = sizeof(m_buf); m_p = m_buf; @@ -496,10 +421,10 @@ bool StdioStream::flushBuf() { uint8_t n = m_p - m_buf; m_p = m_buf; m_w = sizeof(m_buf); - if (FatFile::write(m_buf, n) == n) { + if (StreamBaseFile::write(m_buf, n) == n) { return true; } - m_flags |= F_ERR; + m_status |= S_ERR; return false; } //------------------------------------------------------------------------------ @@ -524,4 +449,3 @@ char* StdioStream::fmtSpace(uint8_t len) { m_w -= len; return reinterpret_cast(m_p); } - diff --git a/avr/cores/megacommand/SdFat/FatLib/StdioStream.h b/avr/cores/megacommand/SdFat/iostream/StdioStream.h similarity index 96% rename from avr/cores/megacommand/SdFat/FatLib/StdioStream.h rename to avr/cores/megacommand/SdFat/iostream/StdioStream.h index 6d903e7b4..b5b52951f 100644 --- a/avr/cores/megacommand/SdFat/FatLib/StdioStream.h +++ b/avr/cores/megacommand/SdFat/iostream/StdioStream.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 20011-2017 Bill Greiman + * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -29,7 +29,7 @@ * \brief StdioStream class */ #include -#include "FatFile.h" +#include "ios.h" //------------------------------------------------------------------------------ /** Total size of stream buffer. The entire buffer is used for output. * During input UNGETC_BUF_SIZE of this space is reserved for ungetc. @@ -95,7 +95,7 @@ const uint8_t UNGETC_BUF_SIZE = 2; /** Seek relative to current position. */ #define SEEK_CUR 1 #endif // SEEK_CUR -#ifndef SEEK_END +#ifndef SEEK_END /** Seek relative to end-of-file. */ #define SEEK_END 2 #endif // SEEK_END @@ -109,20 +109,16 @@ const uint8_t UNGETC_BUF_SIZE = 2; * * StdioStream does not support subdirectories or long file names. */ -class StdioStream : private FatFile { +class StdioStream : private StreamBaseFile { public: /** Constructor * */ - StdioStream() { - m_w = m_r = 0; - m_p = m_buf; - m_flags = 0; - } + StdioStream() {} //---------------------------------------------------------------------------- /** Clear the stream's end-of-file and error indicators. */ void clearerr() { - m_flags &= ~(F_ERR | F_EOF); + m_status &= ~(S_ERR | S_EOF); } //---------------------------------------------------------------------------- /** Close a stream. @@ -142,14 +138,14 @@ class StdioStream : private FatFile { * \return non-zero if and only if the end-of-file indicator is set. */ int feof() { - return (m_flags & F_EOF) != 0; + return (m_status & S_EOF) != 0; } //---------------------------------------------------------------------------- /** Test the stream's error indicator. * \return return non-zero if and only if the error indicator is set. */ int ferror() { - return (m_flags & F_ERR) != 0; + return (m_status & S_ERR) != 0; } //---------------------------------------------------------------------------- /** Flush the stream. @@ -259,7 +255,7 @@ class StdioStream : private FatFile { * * \return true for success or false for failure. */ - bool fopen(const char* path, const char * mode); + bool fopen(const char* path, const char* mode); //---------------------------------------------------------------------------- /** Write a byte to a stream. * @@ -444,7 +440,7 @@ class StdioStream : private FatFile { return n > 0 ? n : 0; } //---------------------------------------------------------------------------- - /** Print a number. + /** Print a number. * * \param[in] val the number to be printed. * @@ -650,18 +646,18 @@ class StdioStream : private FatFile { char* fmtSpace(uint8_t len); int write(const void* buf, size_t count); //---------------------------------------------------------------------------- - // F_SRD and F_WR are never simultaneously asserted - static const uint8_t F_SRD = 0x01; // OK to read - static const uint8_t F_SWR = 0x02; // OK to write - static const uint8_t F_SRW = 0x04; // open for reading & writing - static const uint8_t F_EOF = 0x10; // found EOF - static const uint8_t F_ERR = 0x20; // found error + // S_SRD and S_WR are never simultaneously asserted + static const uint8_t S_SRD = 0x01; // OK to read + static const uint8_t S_SWR = 0x02; // OK to write + static const uint8_t S_SRW = 0x04; // open for reading & writing + static const uint8_t S_EOF = 0x10; // found EOF + static const uint8_t S_ERR = 0x20; // found error //---------------------------------------------------------------------------- - uint8_t m_flags; - uint8_t* m_p; - uint8_t m_r; - uint8_t m_w; uint8_t m_buf[STREAM_BUF_SIZE]; + uint8_t m_status = 0; + uint8_t* m_p = m_buf; + uint8_t m_r = 0; + uint8_t m_w; }; //------------------------------------------------------------------------------ #endif // StdioStream_h diff --git a/avr/cores/megacommand/SdFat/FatLib/fstream.cpp b/avr/cores/megacommand/SdFat/iostream/StreamBaseClass.cpp similarity index 73% rename from avr/cores/megacommand/SdFat/FatLib/fstream.cpp rename to avr/cores/megacommand/SdFat/iostream/StreamBaseClass.cpp index 55053f538..6d1839601 100644 --- a/avr/cores/megacommand/SdFat/FatLib/fstream.cpp +++ b/avr/cores/megacommand/SdFat/iostream/StreamBaseClass.cpp @@ -1,5 +1,5 @@ /** - * Copyright (c) 20011-2017 Bill Greiman + * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -23,11 +23,10 @@ * DEALINGS IN THE SOFTWARE. */ #include "fstream.h" -//============================================================================== -/// @cond SHOW_PROTECTED -int16_t FatStreamBase::getch() { +//------------------------------------------------------------------------------ +int16_t StreamBaseClass::getch() { uint8_t c; - int8_t s = read(&c, 1); + int8_t s = StreamBaseFile::read(&c, 1); if (s != 1) { if (s < 0) { setstate(badbit); @@ -39,66 +38,67 @@ int16_t FatStreamBase::getch() { if (c != '\r' || (getmode() & ios::binary)) { return c; } - s = read(&c, 1); + s = StreamBaseFile::read(&c, 1); if (s == 1 && c == '\n') { return c; } if (s == 1) { - seekCur(-1); + StreamBaseFile::seekCur(-1); } return '\r'; } //------------------------------------------------------------------------------ -void FatStreamBase::open(const char* path, ios::openmode mode) { - uint8_t flags; +void StreamBaseClass::open(const char* path, ios::openmode mode) { + oflag_t oflag; + clearWriteError(); switch (mode & (app | in | out | trunc)) { case app | in: case app | in | out: - flags = O_RDWR | O_APPEND | O_CREAT; + oflag = O_RDWR | O_APPEND | O_CREAT; break; case app: case app | out: - flags = O_WRITE | O_APPEND | O_CREAT; + oflag = O_WRONLY | O_APPEND | O_CREAT; break; case in: - flags = O_READ; + oflag = O_RDONLY; break; case in | out: - flags = O_RDWR; + oflag = O_RDWR | O_CREAT; break; case in | out | trunc: - flags = O_RDWR | O_TRUNC | O_CREAT; + oflag = O_RDWR | O_TRUNC | O_CREAT; break; case out: case out | trunc: - flags = O_WRITE | O_TRUNC | O_CREAT; + oflag = O_WRONLY | O_TRUNC | O_CREAT; break; default: goto fail; } if (mode & ios::ate) { - flags |= O_AT_END; + oflag |= O_AT_END; } - if (!FatFile::open(path, flags)) { + if (!StreamBaseFile::open(path, oflag)) { goto fail; } setmode(mode); clear(); return; -fail: - FatFile::close(); + fail: + StreamBaseFile::close(); setstate(failbit); return; } //------------------------------------------------------------------------------ -void FatStreamBase::putch(char c) { +void StreamBaseClass::putch(char c) { if (c == '\n' && !(getmode() & ios::binary)) { write('\r'); } @@ -108,7 +108,7 @@ void FatStreamBase::putch(char c) { } } //------------------------------------------------------------------------------ -void FatStreamBase::putstr(const char* str) { +void StreamBaseClass::putstr(const char* str) { size_t n = 0; while (1) { char c = str[n]; @@ -130,11 +130,7 @@ void FatStreamBase::putstr(const char* str) { } } //------------------------------------------------------------------------------ -/** Internal do not use - * \param[in] off - * \param[in] way - */ -bool FatStreamBase::seekoff(off_type off, seekdir way) { +bool StreamBaseClass::seekoff(off_type off, seekdir way) { pos_type pos; switch (way) { case beg: @@ -142,11 +138,11 @@ bool FatStreamBase::seekoff(off_type off, seekdir way) { break; case cur: - pos = curPosition() + off; + pos = StreamBaseFile::curPosition() + off; break; case end: - pos = fileSize() + off; + pos = StreamBaseFile::fileSize() + off; break; default: @@ -155,18 +151,10 @@ bool FatStreamBase::seekoff(off_type off, seekdir way) { return seekpos(pos); } //------------------------------------------------------------------------------ -/** Internal do not use - * \param[in] pos - */ -bool FatStreamBase::seekpos(pos_type pos) { - return seekSet(pos); -} -//------------------------------------------------------------------------------ -int FatStreamBase::write(const void* buf, size_t n) { - return FatFile::write(buf, n); +int StreamBaseClass::write(const void* buf, size_t n) { + return StreamBaseFile::write(buf, n); } //------------------------------------------------------------------------------ -void FatStreamBase::write(char c) { - write(&c, 1); +void StreamBaseClass::write(char c) { + StreamBaseFile::write(&c, 1); } -/// @endcond diff --git a/avr/cores/megacommand/SdFat/FatLib/bufstream.h b/avr/cores/megacommand/SdFat/iostream/bufstream.h similarity index 93% rename from avr/cores/megacommand/SdFat/FatLib/bufstream.h rename to avr/cores/megacommand/SdFat/iostream/bufstream.h index b189d30a2..5045ec38f 100644 --- a/avr/cores/megacommand/SdFat/FatLib/bufstream.h +++ b/avr/cores/megacommand/SdFat/iostream/bufstream.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 20011-2017 Bill Greiman + * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -38,7 +38,7 @@ class ibufstream : public istream { public: /** Constructor */ - ibufstream() : m_buf(0), m_len(0) {} + ibufstream() {} /** Constructor * \param[in] str pointer to string to be parsed * Warning: The string will not be copied so must stay in scope. @@ -66,7 +66,7 @@ class ibufstream : public istream { setstate(eofbit); return -1; } - void getpos(FatPos_t *pos) { + void getpos(pos_t* pos) { pos->position = m_pos; } bool seekoff(off_type off, seekdir way) { @@ -81,7 +81,7 @@ class ibufstream : public istream { } return false; } - void setpos(FatPos_t *pos) { + void setpos(pos_t* pos) { m_pos = pos->position; } pos_type tellpos() { @@ -89,8 +89,8 @@ class ibufstream : public istream { } /// @endcond private: - const char* m_buf; - size_t m_len; + const char* m_buf = nullptr; + size_t m_len = 0; size_t m_pos; }; //============================================================================== @@ -101,7 +101,7 @@ class ibufstream : public istream { class obufstream : public ostream { public: /** constructor */ - obufstream() : m_in(0) {} + obufstream() {} /** Constructor * \param[in] buf buffer for formatted string * \param[in] size buffer size @@ -131,7 +131,7 @@ class obufstream : public ostream { protected: /// @cond SHOW_PROTECTED void putch(char c) { - if (m_in >= (m_size - 1)) { + if ((m_in + 1) >= m_size) { setstate(badbit); return; } @@ -159,14 +159,13 @@ class obufstream : public ostream { bool sync() { return true; } - pos_type tellpos() { return m_in; } /// @endcond private: - char *m_buf; - size_t m_size; - size_t m_in; + char *m_buf = nullptr; + size_t m_size = 0; + size_t m_in = 0; }; #endif // bufstream_h diff --git a/avr/cores/megacommand/SdFat/FatLib/fstream.h b/avr/cores/megacommand/SdFat/iostream/fstream.h similarity index 75% rename from avr/cores/megacommand/SdFat/FatLib/fstream.h rename to avr/cores/megacommand/SdFat/iostream/fstream.h index cfd76c6d6..52318609e 100644 --- a/avr/cores/megacommand/SdFat/FatLib/fstream.h +++ b/avr/cores/megacommand/SdFat/iostream/fstream.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 20011-2017 Bill Greiman + * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -22,25 +22,30 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ -#ifndef fstream_h -#define fstream_h /** * \file - * \brief \ref fstream, \ref ifstream, and \ref ofstream classes + * \brief iostreams for files. */ -#include "FatFile.h" +#ifndef fstream_h +#define fstream_h #include "iostream.h" -//============================================================================== +//------------------------------------------------------------------------------ /** - * \class FatStreamBase - * \brief Base class for C++ style streams + * \class StreamBaseClass + * \brief base type for FAT and exFAT streams */ -class FatStreamBase : protected FatFile, virtual public ios { +class StreamBaseClass : protected StreamBaseFile, virtual public ios { protected: - /// @cond SHOW_PROTECTED + void clearWriteError() { + StreamBaseFile::clearWriteError(); + } + /* Internal do not use + * \return mode + */ int16_t getch(); - void putch(char c); - void putstr(const char *str); + bool getWriteError() { + return StreamBaseFile::getWriteError(); + } void open(const char* path, ios::openmode mode); /** Internal do not use * \return mode @@ -48,17 +53,24 @@ class FatStreamBase : protected FatFile, virtual public ios { ios::openmode getmode() { return m_mode; } + void putch(char c); + void putstr(const char *str); + bool seekoff(off_type off, seekdir way); + /** Internal do not use + * \param[in] pos + */ + bool seekpos(pos_type pos) { + return StreamBaseFile::seekSet(pos); + } /** Internal do not use * \param[in] mode */ void setmode(ios::openmode mode) { m_mode = mode; } - bool seekoff(off_type off, seekdir way); - bool seekpos(pos_type pos); int write(const void* buf, size_t n); void write(char c); - /// @endcond + private: ios::openmode m_mode; }; @@ -67,13 +79,12 @@ class FatStreamBase : protected FatFile, virtual public ios { * \class fstream * \brief file input/output stream. */ -class fstream : public iostream, FatStreamBase { +class fstream : public iostream, StreamBaseClass { public: using iostream::peek; fstream() {} /** Constructor with open - * - * \param[in] path path to open + * \param[in] path file to open * \param[in] mode open mode */ explicit fstream(const char* path, openmode mode = in | out) { @@ -87,16 +98,16 @@ class fstream : public iostream, FatStreamBase { */ void clear(iostate state = goodbit) { ios::clear(state); - FatFile::clearWriteError(); + StreamBaseClass::clearWriteError(); } /** Close a file and force cached data and directory information * to be written to the storage device. */ void close() { - FatFile::close(); + StreamBaseClass::close(); } /** Open a fstream - * \param[in] path file to open + * \param[in] path path to open * \param[in] mode open mode * * Valid open modes are (at end, ios::ate, and/or ios::binary may be added): @@ -118,11 +129,11 @@ class fstream : public iostream, FatStreamBase { * create text file for update, writing at end of file. */ void open(const char* path, openmode mode = in | out) { - FatStreamBase::open(path, mode); + StreamBaseClass::open(path, mode); } /** \return True if stream is open else false. */ bool is_open() { - return FatFile::isOpen(); + return StreamBaseFile::isOpen(); } protected: @@ -131,43 +142,43 @@ class fstream : public iostream, FatStreamBase { * \return */ int16_t getch() { - return FatStreamBase::getch(); + return StreamBaseClass::getch(); } /** Internal - do not use * \param[out] pos */ - void getpos(FatPos_t* pos) { - FatFile::getpos(pos); + void getpos(pos_t* pos) { + StreamBaseFile::fgetpos(pos); } /** Internal - do not use * \param[in] c */ void putch(char c) { - FatStreamBase::putch(c); + StreamBaseClass::putch(c); } /** Internal - do not use * \param[in] str */ void putstr(const char *str) { - FatStreamBase::putstr(str); + StreamBaseClass::putstr(str); } /** Internal - do not use * \param[in] pos */ bool seekoff(off_type off, seekdir way) { - return FatStreamBase::seekoff(off, way); + return StreamBaseClass::seekoff(off, way); } bool seekpos(pos_type pos) { - return FatStreamBase::seekpos(pos); + return StreamBaseClass::seekpos(pos); } - void setpos(FatPos_t* pos) { - FatFile::setpos(pos); + void setpos(pos_t* pos) { + StreamBaseFile::fsetpos(pos); } bool sync() { - return FatStreamBase::sync(); + return StreamBaseClass::sync(); } pos_type tellpos() { - return FatStreamBase::curPosition(); + return StreamBaseFile::curPosition(); } /// @endcond }; @@ -176,7 +187,7 @@ class fstream : public iostream, FatStreamBase { * \class ifstream * \brief file input stream. */ -class ifstream : public istream, FatStreamBase { +class ifstream : public istream, StreamBaseClass { public: using istream::peek; ifstream() {} @@ -194,11 +205,11 @@ class ifstream : public istream, FatStreamBase { * to be written to the storage device. */ void close() { - FatFile::close(); + StreamBaseClass::close(); } /** \return True if stream is open else false. */ bool is_open() { - return FatFile::isOpen(); + return StreamBaseFile::isOpen(); } /** Open an ifstream * \param[in] path file to open @@ -207,7 +218,7 @@ class ifstream : public istream, FatStreamBase { * \a mode See fstream::open() for valid modes. */ void open(const char* path, openmode mode = in) { - FatStreamBase::open(path, mode | in); + StreamBaseClass::open(path, mode | in); } protected: @@ -216,28 +227,28 @@ class ifstream : public istream, FatStreamBase { * \return */ int16_t getch() { - return FatStreamBase::getch(); + return StreamBaseClass::getch(); } /** Internal - do not use * \param[out] pos */ - void getpos(FatPos_t* pos) { - FatFile::getpos(pos); + void getpos(pos_t* pos) { + StreamBaseFile::fgetpos(pos); } /** Internal - do not use * \param[in] pos */ bool seekoff(off_type off, seekdir way) { - return FatStreamBase::seekoff(off, way); + return StreamBaseClass::seekoff(off, way); } bool seekpos(pos_type pos) { - return FatStreamBase::seekpos(pos); + return StreamBaseClass::seekpos(pos); } - void setpos(FatPos_t* pos) { - FatFile::setpos(pos); + void setpos(pos_t* pos) { + StreamBaseFile::fsetpos(pos); } pos_type tellpos() { - return FatStreamBase::curPosition(); + return StreamBaseFile::curPosition(); } /// @endcond }; @@ -246,14 +257,14 @@ class ifstream : public istream, FatStreamBase { * \class ofstream * \brief file output stream. */ -class ofstream : public ostream, FatStreamBase { +class ofstream : public ostream, StreamBaseClass { public: ofstream() {} /** Constructor with open * \param[in] path file to open * \param[in] mode open mode */ - explicit ofstream(const char* path, ios::openmode mode = out) { + explicit ofstream(const char* path, openmode mode = out) { open(path, mode); } #if DESTRUCTOR_CLOSES_FILE @@ -264,13 +275,13 @@ class ofstream : public ostream, FatStreamBase { */ void clear(iostate state = goodbit) { ios::clear(state); - FatFile::clearWriteError(); + StreamBaseClass::clearWriteError(); } /** Close a file and force cached data and directory information * to be written to the storage device. */ void close() { - FatFile::close(); + StreamBaseClass::close(); } /** Open an ofstream * \param[in] path file to open @@ -279,11 +290,11 @@ class ofstream : public ostream, FatStreamBase { * \a mode See fstream::open() for valid modes. */ void open(const char* path, openmode mode = out) { - FatStreamBase::open(path, mode | out); + StreamBaseClass::open(path, mode | out); } /** \return True if stream is open else false. */ bool is_open() { - return FatFile::isOpen(); + return StreamBaseFile::isOpen(); } protected: @@ -293,28 +304,27 @@ class ofstream : public ostream, FatStreamBase { * \param[in] c */ void putch(char c) { - FatStreamBase::putch(c); + StreamBaseClass::putch(c); } void putstr(const char* str) { - FatStreamBase::putstr(str); + StreamBaseClass::putstr(str); } bool seekoff(off_type off, seekdir way) { - return FatStreamBase::seekoff(off, way); + return StreamBaseClass::seekoff(off, way); } bool seekpos(pos_type pos) { - return FatStreamBase::seekpos(pos); + return StreamBaseClass::seekpos(pos); } /** * Internal do not use * \param[in] b */ bool sync() { - return FatStreamBase::sync(); + return StreamBaseClass::sync(); } pos_type tellpos() { - return FatStreamBase::curPosition(); + return StreamBaseFile::curPosition(); } /// @endcond }; -//------------------------------------------------------------------------------ #endif // fstream_h diff --git a/avr/cores/megacommand/SdFat/FatLib/ios.h b/avr/cores/megacommand/SdFat/iostream/ios.h similarity index 91% rename from avr/cores/megacommand/SdFat/FatLib/ios.h rename to avr/cores/megacommand/SdFat/iostream/ios.h index 14451d8ff..60254aa1c 100644 --- a/avr/cores/megacommand/SdFat/FatLib/ios.h +++ b/avr/cores/megacommand/SdFat/iostream/ios.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 20011-2017 Bill Greiman + * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -24,12 +24,25 @@ */ #ifndef ios_h #define ios_h -#include "FatFile.h" +#include "../FsLib/FsLib.h" /** * \file * \brief \ref ios_base and \ref ios classes */ //============================================================================== +/** For internal use in c++ streams */ +typedef fspos_t pos_t; +//============================================================================== +#if SDFAT_FILE_TYPE == 1 || defined(DOXYGEN) +/** Set File type for iostreams. */ +typedef FatFile StreamBaseFile; +#elif SDFAT_FILE_TYPE == 2 +typedef ExFatFile StreamBaseFile; +#elif SDFAT_FILE_TYPE == 3 +typedef FsBaseFile StreamBaseFile; +#else // SDFAT_FILE_TYPE +#error Invalid SDFAT_FILE_TYPE +#endif // SDFAT_FILE_TYPE /** * \class ios_base * \brief Base class for all streams @@ -47,6 +60,7 @@ class ios_base { static const iostate eofbit = 0x02; /** iostate fail bit for nonfatal error */ static const iostate failbit = 0X04; +#if SDFAT_FILE_TYPE == 1 /** * unsigned size that can represent maximum file size. * (violates spec - should be signed) @@ -56,7 +70,17 @@ class ios_base { typedef uint32_t pos_type; /** type for relative seek offset */ typedef int32_t off_type; - +#else // SDFAT_FILE_TYPE + /** + * unsigned size that can represent maximum file size. + * (violates spec - should be signed) + */ + typedef uint64_t streamsize; + /** type for absolute seek position */ + typedef uint64_t pos_type; + /** type for relative seek offset */ + typedef int64_t off_type; +#endif // SDFAT_FILE_TYPE /** enumerated type for the direction of relative seeks */ enum seekdir { /** seek relative to the beginning of the stream */ @@ -181,7 +205,6 @@ class ios_base { } /** clear format flags * \param[in] fl flags to be cleared - * \return old flags */ void unsetf(fmtflags fl) { m_fmtflags &= ~fl; @@ -366,16 +389,18 @@ inline ios_base& uppercase(ios_base& str) { class ios : public ios_base { public: /** Create ios with no error flags set */ - ios() : m_iostate(0) {} + ios() {} /** \return null pointer if fail() is true. */ operator const void*() const { - return !fail() ? reinterpret_cast(this) : 0; + return !fail() ? reinterpret_cast(this) : nullptr; } /** \return true if fail() else false. */ bool operator!() const { return fail(); } + /** \return false if fail() else true. */ + explicit operator bool() const {return !fail();} /** \return The iostate flags for this file. */ iostate rdstate() const { return m_iostate; @@ -418,6 +443,6 @@ class ios : public ios_base { } private: - iostate m_iostate; + iostate m_iostate = 0; }; #endif // ios_h diff --git a/avr/cores/megacommand/SdFat/FatLib/iostream.h b/avr/cores/megacommand/SdFat/iostream/iostream.h similarity index 99% rename from avr/cores/megacommand/SdFat/FatLib/iostream.h rename to avr/cores/megacommand/SdFat/iostream/iostream.h index e09b52c94..72ef4cbb0 100644 --- a/avr/cores/megacommand/SdFat/FatLib/iostream.h +++ b/avr/cores/megacommand/SdFat/iostream/iostream.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 20011-2017 Bill Greiman + * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License diff --git a/avr/cores/megacommand/SdFat/FatLib/istream.cpp b/avr/cores/megacommand/SdFat/iostream/istream.cpp similarity index 96% rename from avr/cores/megacommand/SdFat/FatLib/istream.cpp rename to avr/cores/megacommand/SdFat/iostream/istream.cpp index 8393ba054..930cf7767 100644 --- a/avr/cores/megacommand/SdFat/FatLib/istream.cpp +++ b/avr/cores/megacommand/SdFat/iostream/istream.cpp @@ -1,5 +1,5 @@ /** - * Copyright (c) 20011-2017 Bill Greiman + * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -22,7 +22,6 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ -// #include #include #include #include "istream.h" @@ -49,7 +48,7 @@ istream& istream::get(char& c) { //------------------------------------------------------------------------------ istream& istream::get(char *str, streamsize n, char delim) { int c; - FatPos_t pos; + pos_t pos; m_gcount = 0; while ((m_gcount + 1) < n) { c = getch(&pos); @@ -137,7 +136,7 @@ bool istream::getDouble(double* value) { int16_t exp = 0; int16_t fracExp = 0; uint32_t frac = 0; - FatPos_t endPos; + pos_t endPos; double pow10; double v; @@ -198,13 +197,13 @@ bool istream::getDouble(double* value) { if (exp & 1) { if (expNeg) { // check for underflow - if (v < FLT_MIN * pow10 && frac != 0) { + if (v < DBL_MIN * pow10 && frac != 0) { goto fail; } v /= pow10; } else { // check for overflow - if (v > FLT_MAX / pow10) { + if (v > DBL_MAX / pow10) { goto fail; } v *= pow10; @@ -217,7 +216,7 @@ bool istream::getDouble(double* value) { *value = neg ? -v : v; return true; -fail: + fail: // error restore position to last good place setpos(&endPos); setstate(failbit); @@ -226,7 +225,7 @@ bool istream::getDouble(double* value) { //------------------------------------------------------------------------------ istream& istream::getline(char *str, streamsize n, char delim) { - FatPos_t pos; + pos_t pos; int c; m_gcount = 0; if (n > 0) { @@ -263,7 +262,7 @@ bool istream::getNumber(uint32_t posMax, uint32_t negMax, uint32_t* num) { uint32_t val = 0; uint32_t cutoff; uint8_t cutlim; - FatPos_t endPos; + pos_t endPos; uint8_t f = flags() & basefield; uint8_t base = f == oct ? 8 : f != hex ? 10 : 16; getpos(&endPos); @@ -319,7 +318,7 @@ bool istream::getNumber(uint32_t posMax, uint32_t negMax, uint32_t* num) { } //------------------------------------------------------------------------------ void istream::getStr(char *str) { - FatPos_t pos; + pos_t pos; uint16_t i = 0; uint16_t m = width() ? width() - 1 : 0XFFFE; if (m != 0) { @@ -363,7 +362,7 @@ istream& istream::ignore(streamsize n, int delim) { //------------------------------------------------------------------------------ int istream::peek() { int16_t c; - FatPos_t pos; + pos_t pos; m_gcount = 0; getpos(&pos); c = getch(); @@ -388,7 +387,7 @@ int16_t istream::readSkip() { /** used to implement ws() */ void istream::skipWhite() { int c; - FatPos_t pos; + pos_t pos; do { c = getch(&pos); } while (isspace(c)); diff --git a/avr/cores/megacommand/SdFat/FatLib/istream.h b/avr/cores/megacommand/SdFat/iostream/istream.h similarity index 97% rename from avr/cores/megacommand/SdFat/FatLib/istream.h rename to avr/cores/megacommand/SdFat/iostream/istream.h index d0162f460..68c45da2d 100644 --- a/avr/cores/megacommand/SdFat/FatLib/istream.h +++ b/avr/cores/megacommand/SdFat/iostream/istream.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 20011-2017 Bill Greiman + * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -285,8 +285,8 @@ class istream : public virtual ios { * */ int peek(); -// istream& read(char *str, streamsize count); -// streamsize readsome(char *str, streamsize count); + // istream& read(char *str, streamsize count); + // streamsize readsome(char *str, streamsize count); /** * \return the stream position */ @@ -332,7 +332,7 @@ class istream : public virtual ios { * \param[out] pos * \return */ - int16_t getch(FatPos_t* pos) { + int16_t getch(pos_t* pos) { getpos(pos); return getch(); } @@ -340,14 +340,14 @@ class istream : public virtual ios { * Internal - do not use * \param[out] pos */ - virtual void getpos(FatPos_t* pos) = 0; + virtual void getpos(pos_t* pos) = 0; /** * Internal - do not use * \param[in] pos */ virtual bool seekoff(off_type off, seekdir way) = 0; virtual bool seekpos(pos_type pos) = 0; - virtual void setpos(FatPos_t* pos) = 0; + virtual void setpos(pos_t* pos) = 0; virtual pos_type tellpos() = 0; /// @endcond diff --git a/avr/cores/megacommand/SdFat/FatLib/ostream.cpp b/avr/cores/megacommand/SdFat/iostream/ostream.cpp similarity index 76% rename from avr/cores/megacommand/SdFat/FatLib/ostream.cpp rename to avr/cores/megacommand/SdFat/iostream/ostream.cpp index cac954885..75dc2900e 100644 --- a/avr/cores/megacommand/SdFat/FatLib/ostream.cpp +++ b/avr/cores/megacommand/SdFat/iostream/ostream.cpp @@ -1,5 +1,5 @@ /** - * Copyright (c) 20011-2017 Bill Greiman + * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -26,7 +26,7 @@ #include "ostream.h" #ifndef PSTR #define PSTR(x) x -#endif +#endif // PSTR //------------------------------------------------------------------------------ void ostream::do_fill(unsigned len) { for (; len < width(); len++) { @@ -41,17 +41,6 @@ void ostream::fill_not_left(unsigned len) { } } //------------------------------------------------------------------------------ -char* ostream::fmtNum(uint32_t n, char *ptr, uint8_t base) { - char a = flags() & uppercase ? 'A' - 10 : 'a' - 10; - do { - uint32_t m = n; - n /= base; - char c = m - base * n; - *--ptr = c < 10 ? c + '0' : c + a; - } while (n); - return ptr; -} -//------------------------------------------------------------------------------ void ostream::putBool(bool b) { if (flags() & boolalpha) { if (b) { @@ -75,10 +64,10 @@ void ostream::putDouble(double n) { double round = 0.5; char sign; char buf[13]; // room for sign, 10 digits, '.', and zero byte - char *end = buf + sizeof(buf) - 1; - char *str = end; + char *ptr = buf + sizeof(buf) - 1; + char *str = ptr; // terminate string - *end = '\0'; + *ptr = '\0'; // get sign and make nonnegative if (n < 0.0) { @@ -108,7 +97,7 @@ void ostream::putDouble(double n) { // calculate length for fill uint8_t len = sign ? 1 : 0; - len += nd + end - str; + len += nd + ptr - str; // extract adjust field fmtflags adj = flags() & adjustfield; @@ -118,7 +107,7 @@ void ostream::putDouble(double n) { } do_fill(len); } else { - // do fill for internal or right + // do fill for right fill_not_left(len); if (sign) { *--str = sign; @@ -138,44 +127,12 @@ void ostream::putDouble(double n) { //------------------------------------------------------------------------------ void ostream::putNum(int32_t n) { bool neg = n < 0 && flagsToBase() == 10; - if (neg) { - n = -n; - } - putNum(n, neg); + putNum((uint32_t)(neg ? -n : n), neg); } //------------------------------------------------------------------------------ -void ostream::putNum(uint32_t n, bool neg) { - char buf[13]; - char* end = buf + sizeof(buf) - 1; - char* num; - char* str; - uint8_t base = flagsToBase(); - *end = '\0'; - str = num = fmtNum(n, end, base); - if (base == 10) { - if (neg) { - *--str = '-'; - } else if (flags() & showpos) { - *--str = '+'; - } - } else if (flags() & showbase) { - if (flags() & hex) { - *--str = flags() & uppercase ? 'X' : 'x'; - } - *--str = '0'; - } - uint8_t len = end - str; - fmtflags adj = flags() & adjustfield; - if (adj == internal) { - while (str < num) { - putch(*str++); - } - } - if (adj != left) { - do_fill(len); - } - putstr(str); - do_fill(len); +void ostream::putNum(int64_t n) { + bool neg = n < 0 && flagsToBase() == 10; + putNum((uint64_t)(neg ? -n : n), neg); } //------------------------------------------------------------------------------ void ostream::putPgm(const char* str) { diff --git a/avr/cores/megacommand/SdFat/FatLib/ostream.h b/avr/cores/megacommand/SdFat/iostream/ostream.h similarity index 79% rename from avr/cores/megacommand/SdFat/FatLib/ostream.h rename to avr/cores/megacommand/SdFat/iostream/ostream.h index 84e1db55f..458215806 100644 --- a/avr/cores/megacommand/SdFat/FatLib/ostream.h +++ b/avr/cores/megacommand/SdFat/iostream/ostream.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 20011-2017 Bill Greiman + * Copyright (c) 2011-2022 Bill Greiman * This file is part of the SdFat library for SD memory cards. * * MIT License @@ -85,6 +85,16 @@ class ostream : public virtual ios { putStr((const char*)arg); return *this; } +#if ENABLE_ARDUINO_STRING + /** Output string + * \param[in] arg string to output + * \return the stream + */ + ostream &operator<< (const String& arg) { + putStr(arg.c_str()); + return *this; + } +#endif // ENABLE_ARDUINO_STRING /** Output character * \param[in] arg character to output * \return the stream @@ -173,6 +183,22 @@ class ostream : public virtual ios { putNum((uint32_t)arg); return *this; } + /** Output signed long long + * \param[in] arg value to output + * \return the stream + */ + ostream &operator<< (long long arg) { // NOLINT + putNum((int64_t)arg); + return *this; + } + /** Output unsigned long long + * \param[in] arg value to output + * \return the stream + */ + ostream &operator<< (unsigned long long arg) { // NOLINT + putNum((uint64_t)arg); + return *this; + } /** Output pointer * \param[in] arg value to output * \return the stream @@ -181,7 +207,6 @@ class ostream : public virtual ios { putNum(reinterpret_cast(arg)); return *this; } -#if (defined(ARDUINO) && ENABLE_ARDUINO_FEATURES) || defined(DOXYGEN) /** Output a string from flash using the Arduino F() macro. * \param[in] arg pointing to flash string * \return the stream @@ -190,7 +215,6 @@ class ostream : public virtual ios { putPgm(reinterpret_cast(arg)); return *this; } -#endif // (defined(ARDUINO) && ENABLE_ARDUINO_FEATURES) || defined(DOXYGEN) /** * Puts a character in a stream. * @@ -258,19 +282,67 @@ class ostream : public virtual ios { virtual bool seekoff(off_type pos, seekdir way) = 0; virtual bool seekpos(pos_type pos) = 0; virtual bool sync() = 0; - virtual pos_type tellpos() = 0; /// @endcond private: void do_fill(unsigned len); void fill_not_left(unsigned len); - char* fmtNum(uint32_t n, char *ptr, uint8_t base); void putBool(bool b); void putChar(char c); void putDouble(double n); - void putNum(uint32_t n, bool neg = false); void putNum(int32_t n); + void putNum(int64_t n); + void putNum(uint32_t n) {putNum(n, false);} + void putNum(uint64_t n) {putNum(n, false);} void putPgm(const char* str); void putStr(const char* str); + + template + char* fmtNum(T n, char *ptr, uint8_t base) { + char a = flags() & uppercase ? 'A' - 10 : 'a' - 10; + do { + T m = n; + n /= base; + char c = m - base * n; + *--ptr = c < 10 ? c + '0' : c + a; + } while (n); + return ptr; + } + + template + void putNum(T n, bool neg) { + char buf[(8*sizeof(T) + 2)/3 + 2]; + char* ptr = buf + sizeof(buf) - 1; + char* num; + char* str; + uint8_t base = flagsToBase(); + *ptr = '\0'; + str = num = fmtNum(n, ptr, base); + if (base == 10) { + if (neg) { + *--str = '-'; + } else if (flags() & showpos) { + *--str = '+'; + } + } else if (flags() & showbase) { + if (flags() & hex) { + *--str = flags() & uppercase ? 'X' : 'x'; + } + *--str = '0'; + } + uint8_t len = ptr - str; + fmtflags adj = flags() & adjustfield; + if (adj == internal) { + while (str < num) { + putch(*str++); + } + do_fill(len); + } else { + // do fill for right + fill_not_left(len); + } + putstr(str); + do_fill(len); + } }; #endif // ostream_h diff --git a/avr/cores/megacommand/SdFat/sdios.h b/avr/cores/megacommand/SdFat/sdios.h new file mode 100644 index 000000000..cf9da22cf --- /dev/null +++ b/avr/cores/megacommand/SdFat/sdios.h @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2011-2022 Bill Greiman + * This file is part of the SdFat library for SD memory cards. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef sdios_h +#define sdios_h +/** + * \file + * \brief C++ IO Streams features. + */ +#include "iostream/ArduinoStream.h" +#include "iostream/fstream.h" +#include "iostream/StdioStream.h" +#endif // sdios_h diff --git a/avr/cores/megacommand/main.cpp b/avr/cores/megacommand/main.cpp index 7a8d045f9..cc0a0e98a 100644 --- a/avr/cores/megacommand/main.cpp +++ b/avr/cores/megacommand/main.cpp @@ -252,6 +252,10 @@ void handleIncomingMidi() { void change_usb_mode(uint8_t mode) { uint8_t change_mode_msg[] = {0xF0, 0x7D, 0x4D, 0x43, 0x4C, 0x01, mode, 0xF7}; MidiUartUSB.m_putc(change_mode_msg, sizeof(change_mode_msg)); + delay(200); + if (mode == USB_SERIAL) { + MidiUartUSB.mode = UART_SERIAL; MidiUartUSB.set_speed(SERIAL_SPEED); + } } int main(void) {