worked on FAT32 stuff
This commit is contained in:
29
Debug.h
29
Debug.h
@@ -31,29 +31,36 @@ extern "C" {
|
||||
|
||||
class Log {
|
||||
|
||||
|
||||
|
||||
public:
|
||||
|
||||
template<typename... Args> static void addInfo(const char* module, const char* fmt, Args... args) {
|
||||
template<typename... Args> static inline void addInfo(const char* module, const char* fmt, Args... args) {
|
||||
add('i', module, fmt, args...);
|
||||
}
|
||||
|
||||
template<typename... Args> static void addError(const char* module, const char* fmt, Args... args) {
|
||||
template<typename... Args> static inline void addError(const char* module, const char* fmt, Args... args) {
|
||||
add('e', module, fmt, args...);
|
||||
while(true) {}
|
||||
//while(true) {}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
#if defined(WITH_LOG)
|
||||
|
||||
template<typename... Args> static void add(char level, const char* module, const char* fmt, Args... args) {
|
||||
|
||||
char buf[4096];
|
||||
// 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 + sprintf(dst, "%c[%-10s] ", level, module);
|
||||
dst = dst + sprintf(dst, fmt, args...);
|
||||
dst = dst + sprintf(dst, "\n");
|
||||
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");
|
||||
|
||||
#ifdef IS_DESKTOP
|
||||
#if IS_DESKTOP
|
||||
printf(buf);
|
||||
#elif TEENSY
|
||||
Serial.print(buf);
|
||||
@@ -67,6 +74,12 @@ private:
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
template<typename... Args> static void add(char , const char* , const char* , Args... ) {}
|
||||
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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}
|
||||
)
|
||||
|
||||
76
ext/sd/CRC16.h
Normal file
76
ext/sd/CRC16.h
Normal file
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
208
ext/sd/SDCard.h
208
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
|
||||
@@ -117,15 +118,57 @@ public:
|
||||
}
|
||||
|
||||
|
||||
/** read a single block of 512 bytes, addr = byteAddr/512 */
|
||||
bool readSingleBlock(LBA512 addr, uint8_t* dst) {
|
||||
private:
|
||||
|
||||
sendCMD(17, addr>>24, addr>>16, addr>>8, addr>>0, 0xFF);
|
||||
//LBA512 lastReadBlock = 0xFFFFFFFF;
|
||||
//LBA512 lastWriteBlock = 0xFFFFFFFF;
|
||||
|
||||
public:
|
||||
|
||||
/** read a single block of 512 bytes, addr = byteAddr/512 */
|
||||
bool readBlock(LBA512 addr, uint8_t* dst) {
|
||||
|
||||
/*
|
||||
if (lastReadBlock == 0xFFFFFFFF) {
|
||||
if (!startMultiRead(addr)) {endCMD(); return false;}
|
||||
if (!readMultiBlock(dst)) {endCMD(); return false;}
|
||||
|
||||
} 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;}
|
||||
}
|
||||
|
||||
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, "readBlock(%d*512): %02x", addr, res.raw);
|
||||
Log::addInfo(NAME, "startMultiRead(%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
|
||||
@@ -136,20 +179,50 @@ public:
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
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) {
|
||||
@@ -227,10 +300,7 @@ 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
|
||||
}
|
||||
|
||||
/** deselect the card (CS = high) */
|
||||
void deselect() {
|
||||
spi.writeByte(0xFF);
|
||||
MyGPIO::set(PIN_CS);
|
||||
spi.writeByte(0xFF);
|
||||
// read data
|
||||
for (uint16_t i = 0; i < 512; ++i) {
|
||||
dst[i] = spi.readWriteByte(0xFF);
|
||||
}
|
||||
|
||||
// done
|
||||
endCMD();
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
@@ -20,11 +20,7 @@ public:
|
||||
|
||||
}
|
||||
|
||||
DirEntryAt cur() {
|
||||
AbsPos pos = fs.clusterToAbsPos(curCluster) + (curSectorInCluster * fs.desc.bytesPerSector) + (curEntryInSector * sizeof(DirEntry));
|
||||
DirEntry* dirEntry = reinterpret_cast<DirEntry*>(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<DirEntry*>(buf + (curEntryInSector * sizeof(DirEntry)));
|
||||
return DirEntryAt(pos, dirEntry);
|
||||
}
|
||||
|
||||
DirEntryAt next(bool allocIfNeeded) {
|
||||
|
||||
while(true) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include "Structs.h"
|
||||
|
||||
@@ -80,28 +81,19 @@ namespace FAT32 {
|
||||
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);
|
||||
|
||||
// allocate a new directory entry for the file
|
||||
DirEntryAt dea2 = di.nextFree();
|
||||
dea2.setName(name);
|
||||
dea2.setSize(0);
|
||||
|
||||
// allocate the file's first cluster
|
||||
// new file -> allocate the first cluster
|
||||
if (dea.getFirstCluster() == 0) {
|
||||
ClusterNr firstCluster = allocFreeCluster(0);
|
||||
dea2.setFirstCluster(firstCluster);
|
||||
dea.setFirstCluster(firstCluster);
|
||||
write(dea);
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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<void(int)> 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<void(int)> 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
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include "Types.h"
|
||||
#include "../Types.h"
|
||||
|
||||
namespace FAT32 {
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
//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 <gtest/gtest.h>
|
||||
|
||||
|
||||
//#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
|
||||
|
||||
@@ -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;}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -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<TestDevice>;
|
||||
using FS = FAT32::FS<BlockDev>;
|
||||
|
||||
size_t size = 32*1024*1024;
|
||||
TestDevice dev(size);
|
||||
BlockDev bDev(dev);
|
||||
FAT32::FS<BlockDev> fs(bDev, 0);
|
||||
FS fs(bDev, 0);
|
||||
fs.setup(size);
|
||||
|
||||
const char* test = "This is a test";
|
||||
FAT32::FS<BlockDev>::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<BlockDev>::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<BlockDev>::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<TestDevice>;
|
||||
using FS = FAT32::FS<BlockDev>;
|
||||
|
||||
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]);}
|
||||
|
||||
}
|
||||
|
||||
13
io/SoftSPI.h
13
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 <int PIN_MISO, int PIN_MOSI, int PIN_CLK, int SLOWDOWN> class SoftSPI {
|
||||
template <int PIN_MISO, int PIN_MOSI, int PIN_CLK> class SoftSPI {
|
||||
|
||||
static constexpr const char* NAME = "softSPI";
|
||||
uint8_t SLOWDOWN = 128;
|
||||
|
||||
#ifndef BIT
|
||||
#define BIT(x) (1<<x)
|
||||
@@ -25,6 +26,11 @@ public:
|
||||
init();
|
||||
}
|
||||
|
||||
void setSlowdown(uint8_t sd) {
|
||||
this->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();
|
||||
|
||||
Reference in New Issue
Block a user