From 4ac72c678f9218bda3726cd844efe63c30cdc050 Mon Sep 17 00:00:00 2001 From: kazu Date: Sun, 21 Feb 2021 09:33:08 +0100 Subject: [PATCH] worked on FAT32 stuff --- Debug.h | 57 +++++---- ext/sd/AccessHelper.h | 22 ++-- ext/sd/CMakeLists.txt | 2 + ext/sd/CRC16.h | 76 ++++++++++++ ext/sd/SDCard.h | 238 +++++++++++++++++++++++++++++------- ext/sd/fat32/DirIterator.h | 12 +- ext/sd/fat32/FS.h | 72 ++++++----- ext/sd/fat32/File2.h | 23 ++-- ext/sd/fat32/Structs.h | 2 +- ext/sd/main.cpp | 24 ++-- ext/sd/tests/Helper.h | 6 +- ext/sd/tests/TestCreate.cpp | 95 ++++++++++++-- io/SoftSPI.h | 13 +- 13 files changed, 494 insertions(+), 148 deletions(-) create mode 100644 ext/sd/CRC16.h diff --git a/Debug.h b/Debug.h index ad957fc..3ca3199 100644 --- a/Debug.h +++ b/Debug.h @@ -31,41 +31,54 @@ extern "C" { class Log { + + public: - template static void addInfo(const char* module, const char* fmt, Args... args) { + template static inline void addInfo(const char* module, const char* fmt, Args... args) { add('i', module, fmt, args...); } - template static void addError(const char* module, const char* fmt, Args... args) { + template static inline void addError(const char* module, const char* fmt, Args... args) { add('e', module, fmt, args...); - while(true) {} + //while(true) {} } private: - template static void add(char level, const char* module, const char* fmt, Args... args) { - - char buf[4096]; - char* dst = buf; + #if defined(WITH_LOG) - dst = dst + sprintf(dst, "%c[%-10s] ", level, module); - dst = dst + sprintf(dst, fmt, args...); - dst = dst + sprintf(dst, "\n"); + template static void add(char level, const char* module, const char* fmt, Args... args) { - #ifdef IS_DESKTOP - printf(buf); - #elif TEENSY - Serial.print(buf); - #elif ESP8266 - os_printf(buf); - #elif ESP32 - printf(buf); - #else - #error "unsupported platform" - #endif + // temporal buffer (NOTE! MUST NOT BE TOO LARGE OR IT CAN KILL THE STACK!) + char buf[512]; - } + char* dst = buf; + char* end = buf + sizeof(buf); + + dst = dst + snprintf(dst, (end-dst), "%c[%-10s] ", level, module); + dst = dst + snprintf(dst, (end-dst)-2, fmt, args...); + dst = dst + snprintf(dst, (end-dst), "\n"); + + #if IS_DESKTOP + printf(buf); + #elif TEENSY + Serial.print(buf); + #elif ESP8266 + os_printf(buf); + #elif ESP32 + printf(buf); + #else + #error "unsupported platform" + #endif + + } + + #else + + template static void add(char , const char* , const char* , Args... ) {} + + #endif }; diff --git a/ext/sd/AccessHelper.h b/ext/sd/AccessHelper.h index 416cdb8..cd6f9a4 100644 --- a/ext/sd/AccessHelper.h +++ b/ext/sd/AccessHelper.h @@ -38,7 +38,7 @@ public: if (offset || size < SEC_SIZE) { // non-aligned / non-full-block write // read the whole sector - if (!readSingleBlock(addrLBA, buf)) {return written;} + if (!readBlock(addrLBA, buf)) {return written;} // merge in the new data const uint32_t toModify = min(SEC_SIZE-offset, size); @@ -46,7 +46,7 @@ public: offset = 0; // write back the modified sector - if (!writeSingleBlock(addrLBA, buf)) {return written;} + if (!writeBlock(addrLBA, buf)) {return written;} ++addrLBA; size -= toModify; @@ -55,7 +55,7 @@ public: } else { // write a full block - if (!writeSingleBlock(addrLBA, &src[written])) {return written;} + if (!writeBlock(addrLBA, &src[written])) {return written;} ++addrLBA; size -= SEC_SIZE; @@ -83,7 +83,7 @@ public: if (offset || size < SEC_SIZE) { // non-aligned read / non-full-block read const uint32_t toRead = min(SEC_SIZE-offset, size); - if (!readSingleBlock(addrLBA, &dst[read], offset, toRead)) {return read;} + if (!readBlock(addrLBA, &dst[read], offset, toRead)) {return read;} offset = 0; // all following reads are aligned ++addrLBA; @@ -92,7 +92,7 @@ public: } else { // full block read - if (!readSingleBlock(addrLBA, &dst[read])) {return read;} + if (!readBlock(addrLBA, &dst[read])) {return read;} ++addrLBA; size -= SEC_SIZE; read += SEC_SIZE; @@ -106,14 +106,14 @@ public: } /** read a single block of SEC_SIZE bytes. addr = byteAddr/512 */ - bool readSingleBlock(LBA512 addr, uint8_t* dst) { - return dev.readSingleBlock(addr, dst); + bool readBlock(LBA512 addr, uint8_t* dst) { + return dev.readBlock(addr, dst); } /** read a single block of SEC_SIZE bytes. addr = byteAddr/512, write only a fraction of the 512 bytes into dst (skip+len) */ - bool readSingleBlock(LBA512 addr, uint8_t* dst, uint16_t skip, uint16_t len) { + bool readBlock(LBA512 addr, uint8_t* dst, uint16_t skip, uint16_t len) { uint8_t buf[SEC_SIZE]; - if (!dev.readSingleBlock(addr, buf)) {return false;} + if (!dev.readBlock(addr, buf)) {return false;} for (int i = 0; i < len; ++i) { *dst = buf[i+skip]; ++dst; @@ -122,8 +122,8 @@ public: } /** write a single block of 512 bytes. addr = byteAddr/512 */ - bool writeSingleBlock(LBA512 addr, const uint8_t* src) { - return dev.writeSingleBlock(addr, src); + bool writeBlock(LBA512 addr, const uint8_t* src) { + return dev.writeBlock(addr, src); } diff --git a/ext/sd/CMakeLists.txt b/ext/sd/CMakeLists.txt index 9cdb58e..cfe3668 100755 --- a/ext/sd/CMakeLists.txt +++ b/ext/sd/CMakeLists.txt @@ -38,6 +38,7 @@ ADD_DEFINITIONS( -Warray-bounds -fstack-protector-all + -DWITH_LOG -g -O0 @@ -65,6 +66,7 @@ ADD_EXECUTABLE( TARGET_LINK_LIBRARIES( ${PROJECT_NAME} gtest + fmt # pthread ${EXTRA_LIBS} ) diff --git a/ext/sd/CRC16.h b/ext/sd/CRC16.h new file mode 100644 index 0000000..4a66cde --- /dev/null +++ b/ext/sd/CRC16.h @@ -0,0 +1,76 @@ +#pragma once + +static constexpr const uint16_t tblCRC16[256] = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, + 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, + 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, + 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, + 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, + 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, + 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, + 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, + 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, + 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, + 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, + 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, + 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, + 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, + 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, + 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, + 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, + 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, + 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, + 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, + 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, + 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, + 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 +}; + +class CRC16 { + + + +public: + + static uint16_t get(const uint8_t* pData, size_t length) { + + const uint32_t* p = (const uint32_t*)pData; + + //assert ( (length & 3) == 0 ); + + // Calculate the CRC16 checksum for the specified data block. + // Unrolled loop which processes 4-bytes per iteration. + uint16_t crc = 0; + + while (length) { + + uint32_t data = *p++; + + data = ((data >> 8) & 0x00FF00FF) | ((data << 8) & 0xFF00FF00); + + crc ^= data; + crc = (crc << 8) ^ tblCRC16[(crc >> 8) & 0x00FF]; + crc = (crc << 8) ^ tblCRC16[(crc >> 8) & 0x00FF]; + + crc ^= data >> 16; + crc = (crc << 8) ^ tblCRC16[(crc >> 8) & 0x00FF]; + crc = (crc << 8) ^ tblCRC16[(crc >> 8) & 0x00FF]; + + length -= 4; + } + + // Return the calculated checksum + return crc; + + } + +}; diff --git a/ext/sd/SDCard.h b/ext/sd/SDCard.h index d781855..ca218e2 100644 --- a/ext/sd/SDCard.h +++ b/ext/sd/SDCard.h @@ -2,6 +2,7 @@ #include "../../io/GPIO.h" #include "../../Debug.h" +#include "CRC16.h" #define TEENSY_SD_PIN_CS 46 #define TEENSY_SD_PIN_MOSI 45 @@ -17,7 +18,7 @@ template class SDCard { SPI& spi; - + union R1 { // SanDisk Manual Page 5-13 uint8_t raw; struct { @@ -60,7 +61,7 @@ template class SDCard { public: SDCard(SPI& spi) : spi(spi) { - + } bool init() { @@ -117,39 +118,111 @@ public: } +private: + + //LBA512 lastReadBlock = 0xFFFFFFFF; + //LBA512 lastWriteBlock = 0xFFFFFFFF; + +public: + /** read a single block of 512 bytes, addr = byteAddr/512 */ - bool readSingleBlock(LBA512 addr, uint8_t* dst) { - - sendCMD(17, addr>>24, addr>>16, addr>>8, addr>>0, 0xFF); - R1 res; readResponse(&res.raw, 1); + bool readBlock(LBA512 addr, uint8_t* dst) { - Log::addInfo(NAME, "readBlock(%d*512): %02x", addr, res.raw); + /* + if (lastReadBlock == 0xFFFFFFFF) { + if (!startMultiRead(addr)) {endCMD(); return false;} + if (!readMultiBlock(dst)) {endCMD(); return false;} - // read command OK? - if (res.raw != 0) {Log::addError(NAME, "failed"); endCMD(); return false;} - - // wait for data to become available - for (uint16_t i = 0; ; ++i) { - uint8_t res = spi.readWriteByte(0xFF); - if (res == 0xFE) {break;} // available! - if (res != 0xFF) {Log::addError(NAME, "invalid"); endCMD(); return false;} // invalid response - if (i > 1024) {Log::addError(NAME, "timeout"); endCMD(); return false;} // timeout + } else if ((lastReadBlock + 1) == addr) { + if (!readMultiBlock(dst)) {endCMD(); return false;} + + } else { + if (!stopMultiRead()) {endCMD(); return false;} + if (!startMultiRead(addr)) {endCMD(); return false;} + if (!readMultiBlock(dst)) {endCMD(); return false;} } - - // read data - for (uint16_t i = 0; i < 512; ++i) { - dst[i] = spi.readWriteByte(0xFF); - } - - // done - endCMD(); + + lastReadBlock = addr; return true; - + */ + + // bulletproof, but slower + return readSingleBlock(addr, dst); + + } + + /** write a single block of 512 bytes, addr = byteAddr/512 */ + bool writeBlock(LBA512 addr, const uint8_t* src) { + + // bulletproof, but slow + return writeSingleBlock(addr, src); + } private: - + /* + bool startMultiRead(LBA512 addr) { + + sendCMD(18, addr>>24, addr>>16, addr>>8, addr>>0, 0xFF); + R1 res; readResponse(&res.raw, 1); + + Log::addInfo(NAME, "startMultiRead(%d*512): %02x", addr, res.raw); + + if (res.raw != 0) {Log::addError(NAME, "failed"); endCMD(); return false;} + + // wait for data to become available + for (uint16_t i = 0; ; ++i) { + uint8_t res = spi.readWriteByte(0xFF); + if (res == 0xFE) {break;} // available! + if (res != 0xFF) {Log::addError(NAME, "invalid"); endCMD(); return false;} // invalid response + if (i > 1024) {Log::addError(NAME, "timeout"); endCMD(); return false;} // timeout + } + + return true; + + } + + bool readMultiBlock(uint8_t* dst) { + + Log::addInfo(NAME, "readMultiBlock()"); + + for (uint16_t i = 0; i < 512; ++i) { + dst[i] = spi.readWriteByte(0xFF); + } + + return true; + + } + + bool stopMultiRead() { + + sendCMD(12, 0,0,0,0, 0xFF); + R1 res; readResponse(&res.raw, 1); + + Log::addInfo(NAME, "stopMultiRead(): %02x", res.raw); + + endCMD(); + lastReadBlock = 0xFFFFFFFF; + + return true;//(res.raw == 0); + + } + */ + + /** select the card (CS = low) */ + void select() { + spi.writeByte(0xFF); + MyGPIO::clear(PIN_CS); + spi.writeByte(0xFF); + } + + /** deselect the card (CS = high) */ + void deselect() { + spi.writeByte(0xFF); + MyGPIO::set(PIN_CS); + spi.writeByte(0xFF); + } /** send a new command */ void sendCMD(uint8_t cmd, uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t arg3, uint8_t crc) { @@ -226,11 +299,8 @@ private: } - - - - - + + /** read the card's response to the last command */ void readResponse(uint8_t* dst, uint8_t len) { // the SD card takes some time to answer, but must receive clock signals meanwhile! @@ -253,18 +323,102 @@ private: - /** select the card (CS = low) */ - void select() { - spi.writeByte(0xFF); - MyGPIO::clear(PIN_CS); - spi.writeByte(0xFF); +private: + + /** most simple read operation: read a single block of 512 bytes, addr = byteAddr/512 */ + bool readSingleBlock(LBA512 addr, uint8_t* dst) { + + sendCMD(17, addr>>24, addr>>16, addr>>8, addr>>0, 0xFF); + R1 res; readResponse(&res.raw, 1); + + Log::addInfo(NAME, "readSingleBlock(%d*512): %02x", addr, res.raw); + + // read command OK? + if (res.raw != 0) {Log::addError(NAME, "failed"); endCMD(); return false;} + + // wait for data to become available + for (uint16_t i = 0; ; ++i) { + uint8_t res = spi.readWriteByte(0xFF); + if (res == 0xFE) {break;} // available! + if (res != 0xFF) {Log::addError(NAME, "invalid"); endCMD(); return false;} // invalid response + if (i > 1024) {Log::addError(NAME, "timeout"); endCMD(); return false;} // timeout + } + + // read data + for (uint16_t i = 0; i < 512; ++i) { + dst[i] = spi.readWriteByte(0xFF); + } + + // done + endCMD(); + return true; + } - - /** deselect the card (CS = high) */ - void deselect() { - spi.writeByte(0xFF); - MyGPIO::set(PIN_CS); - spi.writeByte(0xFF); + + bool waitWhileBusy() { + + Log::addInfo(NAME, "waitWhileBusy()"); + + uint8_t res = 0; + for (uint16_t i = 0; ; ++i) { + res = spi.readWriteByte(0xFF); + if (res == 0xFF) {return true;} + if (i >= 1024) {return false;} // timeout + } + } + + /** most simple write operation: write a single block of 512 bytes, addr = byteAddr/512 */ + bool writeSingleBlock(LBA512 addr, const uint8_t* src) { + + sendCMD(24, addr>>24, addr>>16, addr>>8, addr>>0, 0xFF); + R1 res; readResponse(&res.raw, 1); + + Log::addInfo(NAME, "writeSingleBlock(%d*512): %02x", addr, res.raw); + + // write command OK? + if (res.raw != 0) {Log::addError(NAME, "failed"); endCMD(); return false;} + + // wait while the card is busy + if (!waitWhileBusy()) {Log::addError(NAME, "busy timeout"); endCMD(); return false;} + + // send BLOCK_START + spi.writeByte(0xFE); + + // send data + for (uint16_t i = 0; i < 512; ++i) { + spi.writeByte(src[i]); + } + + // send CRC16 + // NOTE: it seems that it doesnt matter what we send here + // the card is always fine with it?! + //const uint16_t crc = CRC16::get(src, 512); + //spi.writeByte(crc >> 8); + //spi.writeByte(crc); + spi.writeByte(0xFF); + spi.writeByte(0xFF); + + //const uint8_t DATA_RESPONSE_MASK = 0x1F; + //const uint8_t DATA_RESPONSE_DATA_ACCEPTED = ((2 << 1) | 1); + + //uint8_t crcRes = spi.readWriteByte(0xFF); + const uint8_t crcRes = spi.readWriteByte(0xFF); // response to CRC + //R1 res2; readResponse(&res2.raw, 1); + //Log::addInfo(NAME, "writeSingleBlockCRC(): %02x %02x", tmp, res2.raw); + + // wait for card to finish writing + // should be a series of 0x00 followed by something != 0x00 and then a series of 0xFF + // TODO: check the non 0x00 value? + while(true) { + if (spi.readWriteByte(0xFF) == 0xFF) {break;} + } + + endCMD(); + return true; + //return (crcRes & DATA_RESPONSE_MASK) == DATA_RESPONSE_DATA_ACCEPTED; + + } + }; diff --git a/ext/sd/fat32/DirIterator.h b/ext/sd/fat32/DirIterator.h index 83e9127..d8f90c7 100644 --- a/ext/sd/fat32/DirIterator.h +++ b/ext/sd/fat32/DirIterator.h @@ -20,11 +20,7 @@ public: } - DirEntryAt cur() { - AbsPos pos = fs.clusterToAbsPos(curCluster) + (curSectorInCluster * fs.desc.bytesPerSector) + (curEntryInSector * sizeof(DirEntry)); - DirEntry* dirEntry = reinterpret_cast(buf + (curEntryInSector * sizeof(DirEntry))); - return DirEntryAt(pos, dirEntry); - } + /** get the next usable entry within the current directory */ DirEntryAt nextUsable() { @@ -86,6 +82,12 @@ public: private: + DirEntryAt cur() { + AbsPos pos = fs.clusterToAbsPos(curCluster) + (curSectorInCluster * fs.desc.bytesPerSector) + (curEntryInSector * sizeof(DirEntry)); + DirEntry* dirEntry = reinterpret_cast(buf + (curEntryInSector * sizeof(DirEntry))); + return DirEntryAt(pos, dirEntry); + } + DirEntryAt next(bool allocIfNeeded) { while(true) { diff --git a/ext/sd/fat32/FS.h b/ext/sd/fat32/FS.h index 150a6a3..9684884 100644 --- a/ext/sd/fat32/FS.h +++ b/ext/sd/fat32/FS.h @@ -2,6 +2,7 @@ #include #include +#include #include "Structs.h" @@ -75,33 +76,24 @@ namespace FAT32 { return File(*this, dea.getSize(), dea.getFirstCluster()); } - /** open the given file for reading*/ + /** open the given file for reading */ File2 open2(const DirEntryAt& dea) { return File2(*this, dea); } - /** create a new file for writing, in the given directory */ - File2 create2(const char* name) { + /** get or create a file with the given name */ + File2 getOrCreateFile(const char* name) { - ClusterNr dirStartCluster = 2; // TODO + DirEntryAt dea = getDirEntry(name, true); - //if (!dea.isValid()) {Log::addError(NAME, "create2(): not a valid DirEntry");} - //if (!dea.isDirectory()) {Log::addError(NAME, "create2(): not a directory");} - DirIterator di(*this, dirStartCluster); + // new file -> allocate the first cluster + if (dea.getFirstCluster() == 0) { + ClusterNr firstCluster = allocFreeCluster(0); + dea.setFirstCluster(firstCluster); + write(dea); + } - // allocate a new directory entry for the file - DirEntryAt dea2 = di.nextFree(); - dea2.setName(name); - dea2.setSize(0); - - // allocate the file's first cluster - ClusterNr firstCluster = allocFreeCluster(0); - dea2.setFirstCluster(firstCluster); - - // write the file description - write(dea2); - - return File2(*this, dea2); + return File2(*this, dea); } @@ -132,18 +124,36 @@ namespace FAT32 { Log::addInfo(NAME, "Bytes/Sector: %d, Sector/Cluster: %d, FATs: %d, RootDir: %d", desc.bytesPerSector, desc.sectorsPerCluster, desc.numberOfFATs, desc.rootDirFirstCluster); - /* - std::cout << (int)desc.bytesPerSector << std::endl; - std::cout << (int)desc.sectorsPerCluster << std::endl; - std::cout << (int)desc.numReservedSectors << std::endl; - std::cout << (int)desc.numberOfFATs << std::endl; - std::cout << (int)desc.sectorsPerFAT << std::endl; - std::cout << (int)desc.rootDirFirstCluster << std::endl; + } - std::cout << tmp.startOfFAT << std::endl; - std::cout << tmp.startOfFirstDataCluster << std::endl; - std::cout << tmp.startOfFirstRootDirCluster << std::endl; - */ + /** get (or create, if needed) a DirEntry with the given name */ + DirEntryAt getDirEntry(const char* name, bool createIfNeeded) { + + // TODO: support for sub folders) + + // start at the root folder + ClusterNr dirStartCluster = 2; + + { // search for a matching existing entry + DirIterator di(*this, dirStartCluster); + while(true) { + DirEntryAt dea = di.nextUsable(); + if (!dea.isValid()) {break;} + if (dea.getName() == name) {return dea;} + } + } + + // no matching entry found + if (!createIfNeeded) {return DirEntryAt::invalid();} + + { // allocate a new DirEntry for the file + DirIterator di(*this, dirStartCluster); + DirEntryAt dea = di.nextFree(); + dea.setName(name); + dea.setSize(0); + dea.setFirstCluster(0); + return dea; + } } diff --git a/ext/sd/fat32/File2.h b/ext/sd/fat32/File2.h index 9d926f0..b5b69f4 100644 --- a/ext/sd/fat32/File2.h +++ b/ext/sd/fat32/File2.h @@ -29,11 +29,11 @@ public: ClusterNr getFirstCluster() const {return dea.getFirstCluster();} /** get the file's name */ - const std::string& getName() const {return dea.getName();} + std::string getName() const {return dea.getName();} /** read x bytes from the file */ - uint32_t read(uint32_t size, uint8_t* dst, std::function callback) { + uint32_t read(uint32_t size, uint8_t* dst) { Log::addInfo(NAME, "read %d bytes", size); @@ -48,14 +48,14 @@ public: remaining -= read; dst += read; totalRead += read; - callback(totalRead*100/size); } return totalRead; } - uint32_t write(uint32_t size, const uint8_t* src, std::function callback) { + /* write the given data into the file */ + uint32_t write(uint32_t size, const uint8_t* src) { Log::addInfo(NAME, "write %d bytes", size); @@ -67,10 +67,10 @@ public: remaining -= written; src += written; totalWritten += written; - callback(totalWritten*100/size); + //callback(totalWritten*100/size); } - // update the file header (size might have changed) + // update the file header (filesize might have changed) fs.write(dea); return totalWritten; @@ -98,10 +98,10 @@ private: } // debug - char buf[1024]; - char* dst = buf; dst += sprintf(dst, "clusters: "); - for (ClusterNr nr : clusters) {dst += sprintf(dst, "%d ", nr);} - Log::addInfo(NAME, buf); + //char buf[1024]; + //char* dst = buf; dst += sprintf(dst, "clusters: "); + //for (ClusterNr nr : clusters) {dst += sprintf(dst, "%d ", nr);} + //Log::addInfo(NAME, buf); } @@ -136,6 +136,7 @@ private: int32_t _write(uint32_t writeSize, const uint8_t* src) { + // do we need to append more free sectors to the end of the file? if (curAbsPos >= getAllocatedSize()) { if (clusters.empty()) { const ClusterNr newCluster = fs.allocFreeCluster(0); @@ -147,8 +148,6 @@ private: clusters.push_back(newCluster); Log::addInfo(NAME, "allocNextCluster(%d): %d", prevCluster, newCluster); } - dea.setSize(dea.getSize() + fs.tmp.bytesPerCluster); - fs.write(dea); } // end of current cluster reached? -> switch to the next one diff --git a/ext/sd/fat32/Structs.h b/ext/sd/fat32/Structs.h index 3f066ce..10e85a0 100644 --- a/ext/sd/fat32/Structs.h +++ b/ext/sd/fat32/Structs.h @@ -2,7 +2,7 @@ #include #include -#include "Types.h" +#include "../Types.h" namespace FAT32 { diff --git a/ext/sd/main.cpp b/ext/sd/main.cpp index ab119a6..7849480 100644 --- a/ext/sd/main.cpp +++ b/ext/sd/main.cpp @@ -17,6 +17,8 @@ #include +#include + //class Simu { // FILE* f; @@ -60,13 +62,13 @@ public: fclose(f); } - uint32_t readSingleBlock(LBA512 addr, uint8_t* dst) { + uint32_t readBlock(LBA512 addr, uint8_t* dst) { Log::addInfo("SD", "read512(%d*512)", addr); memcpy(dst, data+addr*512, 512); return 512; } - uint32_t writeSingleBlock(LBA512 addr, const uint8_t* src) { + uint32_t writeBlock(LBA512 addr, const uint8_t* src) { Log::addInfo("SD", "write512(%d*512)", addr); memcpy(data+addr*512, src, 512); return 512; @@ -76,8 +78,16 @@ public: #include + +//#define logInfo(fmt, ...) std::string s = fmt::format(FMT_STRING(fmt), __VA_ARGS__); + int main(int argc, char** argv) { + + //logInfo("{:s}", "I am not a number") + //std::string s = fmt::format(FMT_STRING("{:s}"), "I am not a number"); + //std::string s = fmt::format(FMT_STRING("{:s}"), "I am not a number"); + ::testing::InitGoogleTest(&argc, argv); ::testing::GTEST_FLAG(filter) = "*TestCreate*"; return RUN_ALL_TESTS(); @@ -99,10 +109,6 @@ int main(int argc, char** argv) { FAT32FS fat(ah, mbr.getPartition(0).getFirstSector() * 512); - auto callback = [] (const int percent) { - std::cout << percent << std::endl; - }; - FAT32FS::DirIterator dir = fat.getRoot(); while(false) { @@ -114,7 +120,7 @@ int main(int argc, char** argv) { if (1==0) { uint8_t* bufff = (uint8_t*) malloc(1024*1024); - uint32_t read = f.read(f.getSize(), bufff, callback); + uint32_t read = f.read(f.getSize(), bufff); std::string name = f.getName(); std::ofstream out("/tmp/ram/" + name); @@ -132,10 +138,10 @@ int main(int argc, char** argv) { //FAT32::DirEntryAt root = fat.getRoot().cur(); - FAT32FS::File2 file = fat.create2("tmp1.txt"); + FAT32FS::File2 file = fat.getOrCreateFile("tmp1.txt"); uint8_t src[128]; - file.write(128, src, [] (int percent) {} ); + file.write(128, src); // diff /tmp/ram/TETRIS.GB /apps/workspace/gbemu/tests/tetris.gb // diff /tmp/ram/KIRBY1.GB /apps/workspace/gbemu/tests/Kirby\'s\ Dream\ Land\ \(USA\,\ Europe\).gb diff --git a/ext/sd/tests/Helper.h b/ext/sd/tests/Helper.h index c3a453e..7fb47e3 100644 --- a/ext/sd/tests/Helper.h +++ b/ext/sd/tests/Helper.h @@ -25,18 +25,18 @@ struct TestDevice { } /** read a 512 byte block into dst */ - bool readSingleBlock(LBA512 addr, uint8_t* dst) { + bool readBlock(LBA512 addr, uint8_t* dst) { memcpy(dst, buf+addr*512, 512); return true; } - bool writeSingleBlock(LBA512 addr, const uint8_t* src) { + bool writeBlock(LBA512 addr, const uint8_t* src) { memcpy(buf+addr*512, src, 512); return true; } void reset(uint8_t val) { - for (int i = 0; i < sizeof(buf); ++i) {buf[i] = val;} + for (uint32_t i = 0; i < sizeof(buf); ++i) {buf[i] = val;} } }; diff --git a/ext/sd/tests/TestCreate.cpp b/ext/sd/tests/TestCreate.cpp index aadd09f..4a552ec 100644 --- a/ext/sd/tests/TestCreate.cpp +++ b/ext/sd/tests/TestCreate.cpp @@ -11,7 +11,7 @@ -TEST(TestCreate, structure) { +TEST(TestCreat, structure) { FAT32::FSHeader header; @@ -26,25 +26,102 @@ TEST(TestCreate, structure) { } -TEST (TestCreate, write) { +TEST (TestCreat, writeRead) { using BlockDev = AccessHelper; + using FS = FAT32::FS; size_t size = 32*1024*1024; TestDevice dev(size); BlockDev bDev(dev); - FAT32::FS fs(bDev, 0); + FS fs(bDev, 0); fs.setup(size); - const char* test = "This is a test"; - FAT32::FS::File2 f1 = fs.create2("test1.txt"); - f1.write(strlen(test), (const uint8_t*)test, [](int percent) {}); + for (int i = 0; i < 64; ++i) { - FAT32::FS::File2 f2 = fs.create2("test2.txt"); - f2.write(strlen(test), (const uint8_t*)test, [](int percent) {}); + char name[64]; + sprintf(name, "test%03d.txt", i); + const size_t size = 128 + i * 512; + const size_t sizeA = (size/4096+1) * 4096; + std::cout << name << " - " << size << std::endl; + + FAT32::FS::File2 f = fs.getOrCreateFile(name); + uint8_t* data = (uint8_t*)malloc(128 + i * 512); + for (uint32_t j = 0; j < size; ++j) {data[j] = j;} + uint32_t written = f.write(size, data); + free(data); + + ASSERT_EQ(size, written); + ASSERT_EQ(size, f.getSize()); + ASSERT_EQ(sizeA, f.getAllocatedSize()); + + } dev.toFile("/tmp/ram/1.fat32"); - // mount -t vfat 1.fat32 /mnt/fat/ && ls -l /mnt/fat/ && cat /mnt/fat/test1.txt && umount /mnt/fat + // mount -t vfat 1.fat32 /mnt/fat/ && ls -l /mnt/fat/ && cat /mnt/fat/test001.txt && umount /mnt/fat + + // READ AGAIN + + FS fs2(bDev, 0); + FS::DirIterator di = fs2.getRoot(); + + for (int i = 0; i < 64; ++i) { + + char name[64]; + sprintf(name, "test%03d.txt", i); + const size_t size = 128 + i * 512; + const size_t sizeA = (size/4096+1) * 4096; + + FAT32::DirEntryAt dea = di.nextUsable(); + FS::File2 f = fs.open2(dea); + + ASSERT_EQ(name, dea.getName()); + ASSERT_EQ(name, f.getName()); + ASSERT_EQ(size, f.getSize()); + ASSERT_EQ(sizeA, f.getAllocatedSize()); + + uint8_t* data = (uint8_t*)malloc(128 + i * 512); + uint32_t read = f.read(size, data); + + ASSERT_EQ(size, read); + + for (uint32_t j = 0; j < size; ++j) { + ASSERT_EQ((uint8_t)j, data[j]); + } + + free(data); + + } + +} + +TEST (TestCreate, getOrCreateFile) { + + using BlockDev = AccessHelper; + using FS = FAT32::FS; + + size_t size = 32*1024*1024; + TestDevice dev(size); + BlockDev bDev(dev); + FS fs(bDev, 0); + fs.setup(size); + + FS::File2 f1 = fs.getOrCreateFile("test.txt"); + ASSERT_EQ(0, f1.getSize()); + ASSERT_EQ(4096, f1.getAllocatedSize()); + ASSERT_EQ("test.txt", f1.getName()); + + uint8_t d1[128]; + uint8_t d2[128]; + + for (int i = 0; i < 128; ++i) {d1[i] = 1;} + ASSERT_EQ(128, f1.write(128, d1)); + + FS::File2 f2 = fs.getOrCreateFile("test.txt"); + + ASSERT_EQ(128, f2.getSize()); + ASSERT_EQ(128, f2.read(128, d2)); + for (uint32_t i = 0; i < 128; ++i) {ASSERT_EQ(d1[i], d2[i]);} } diff --git a/io/SoftSPI.h b/io/SoftSPI.h index c6b0ea1..6346c5a 100644 --- a/io/SoftSPI.h +++ b/io/SoftSPI.h @@ -10,9 +10,10 @@ // NOTE: the last template argument has changed from "bool fast" to "int slowdown" where 0 is now fastest! -template class SoftSPI { +template class SoftSPI { static constexpr const char* NAME = "softSPI"; + uint8_t SLOWDOWN = 128; #ifndef BIT #define BIT(x) (1<SLOWDOWN = sd; + } + + private: void init() { @@ -34,10 +40,11 @@ private: MyGPIO::setOutput(PIN_CLK); } + private: - static inline void wait() { + inline void wait() { for (int i = 0; i < SLOWDOWN; ++i) { __asm__ __volatile__("nop"); } @@ -67,7 +74,7 @@ private: return (val) ? 1 : 0; } - static inline uint8_t readWriteBit(const bool out) { + inline uint8_t readWriteBit(const bool out) { if(out) {MyGPIO::set(PIN_MOSI);} else {MyGPIO::clear(PIN_MOSI);} wait(); clkHi();