diff options
author | David Crocker <dcrocker@eschertech.com> | 2018-03-24 18:26:14 +0300 |
---|---|---|
committer | David Crocker <dcrocker@eschertech.com> | 2018-03-24 18:26:14 +0300 |
commit | 8713f3b323ea9fece4bd58b3578c3ff66fb79888 (patch) | |
tree | 127f53b1638c093bc01a6aa9d009b4f715f49f92 /src/Storage | |
parent | 6e147c74951bacbce5ffb73d301a6beeed4a9596 (diff) | |
parent | a9767cc77a03a5564faf935f7ca6e6790a8aa744 (diff) |
Merged changes from dev branch as at version 1.21
Diffstat (limited to 'src/Storage')
-rw-r--r-- | src/Storage/CRC32.cpp | 74 | ||||
-rw-r--r-- | src/Storage/CRC32.h | 27 | ||||
-rw-r--r-- | src/Storage/FileData.h | 31 | ||||
-rw-r--r-- | src/Storage/FileStore.cpp | 208 | ||||
-rw-r--r-- | src/Storage/FileStore.h | 46 | ||||
-rw-r--r-- | src/Storage/FileWriteBuffer.h | 55 | ||||
-rw-r--r-- | src/Storage/MassStorage.h | 67 |
7 files changed, 406 insertions, 102 deletions
diff --git a/src/Storage/CRC32.cpp b/src/Storage/CRC32.cpp new file mode 100644 index 00000000..88142ac9 --- /dev/null +++ b/src/Storage/CRC32.cpp @@ -0,0 +1,74 @@ +#include "CRC32.h" + +const uint32_t CRC32::CRC_32_TAB[256] = { + /* + CRC polynomial 0xedb88320 + This table can also be generated in runtime + */ + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +CRC32::CRC32() +{ + Reset(); +} + +void CRC32::Update(char c) +{ + crc = (CRC_32_TAB[(crc ^ c) & 0xFF] ^ (crc >> 8)); +} + +void CRC32::Update(const char *c, size_t len) +{ + for (size_t i = 0; i < len; ++i) + { + Update(c[i]); + } +} + +void CRC32::Reset() +{ + crc = 0xffffffff; +}
\ No newline at end of file diff --git a/src/Storage/CRC32.h b/src/Storage/CRC32.h new file mode 100644 index 00000000..06bcfe13 --- /dev/null +++ b/src/Storage/CRC32.h @@ -0,0 +1,27 @@ +#ifndef CRC32_H +#define CRC32_H + +#include <cstdint> // for uint32_t +#include <cstddef> // for size_t + +class CRC32 +{ +private: + static const uint32_t CRC_32_TAB[]; + uint32_t crc; + +public: + CRC32(); + void Update(char c); + void Update(const char *c, size_t len); + void Reset(); + uint32_t Get() const; + +}; + +inline uint32_t CRC32::Get() const +{ + return ~crc; +} + +#endif
\ No newline at end of file diff --git a/src/Storage/FileData.h b/src/Storage/FileData.h index 00c7e47f..683b4165 100644 --- a/src/Storage/FileData.h +++ b/src/Storage/FileData.h @@ -19,7 +19,7 @@ class FileData public: friend class FileGCodeInput; - FileData() : f(NULL) {} + FileData() : f(nullptr) {} // Set this to refer to a newly-opened file void Set(FileStore* pfile) @@ -28,14 +28,14 @@ public: f = pfile; } - bool IsLive() const { return f != NULL; } + bool IsLive() const { return f != nullptr; } bool Close() { - if (f != NULL) + if (f != nullptr) { bool ok = f->Close(); - f = NULL; + f = nullptr; return ok; } return false; @@ -46,7 +46,7 @@ public: return f->Read(b); } - int Read(char* buf, size_t nBytes) + int Read(char *buf, size_t nBytes) { return f->Read(buf, nBytes); } @@ -56,7 +56,17 @@ public: return f->Write(b); } - bool Write(const char *s, unsigned int len) + bool Write(const char *s) + { + return f->Write(s, strlen(s)); + } + + bool Write(const char *s, size_t len) + { + return f->Write(s, len); + } + + bool Write(const uint8_t *s, size_t len) { return f->Write(s, len); } @@ -76,11 +86,6 @@ public: return f->Seek(position); } - float FractionRead() const - { - return (f == NULL ? -1.0 : f->FractionRead()); - } - FilePosition Length() const { return f->Length(); @@ -91,7 +96,7 @@ public: { Close(); f = other.f; - if (f != NULL) + if (f != nullptr) { f->Duplicate(); } @@ -110,7 +115,7 @@ private: void Init() { - f = NULL; + f = nullptr; } // Private assignment operator to prevent us assigning these objects diff --git a/src/Storage/FileStore.cpp b/src/Storage/FileStore.cpp index d650da2d..86c24893 100644 --- a/src/Storage/FileStore.cpp +++ b/src/Storage/FileStore.cpp @@ -8,8 +8,9 @@ uint32_t FileStore::longestWriteTime = 0; -FileStore::FileStore(Platform* p) : platform(p) +FileStore::FileStore() : writeBuffer(nullptr) { + Init(); } void FileStore::Init() @@ -21,13 +22,27 @@ void FileStore::Init() } // Invalidate the file if it uses the specified FATFS object -void FileStore::Invalidate(const FATFS *fs) +bool FileStore::Invalidate(const FATFS *fs, bool doClose) { if (file.fs == fs) { - Init(); - file.fs = nullptr; + if (doClose) + { + (void)ForceClose(); + } + else + { + file.fs = nullptr; + if (writeBuffer != nullptr) + { + reprap.GetPlatform().GetMassStorage()->ReleaseWriteBuffer(writeBuffer); + writeBuffer = nullptr; + } + Init(); + } + return true; } + return false; } // Return true if the file is open on the specified file system @@ -38,55 +53,62 @@ bool FileStore::IsOpenOn(const FATFS *fs) const // Open a local file (for example on an SD card). // This is protected - only Platform can access it. -bool FileStore::Open(const char* directory, const char* fileName, bool write) +bool FileStore::Open(const char* directory, const char* fileName, OpenMode mode) { - const char* location = (directory != nullptr) - ? platform->GetMassStorage()->CombineName(directory, fileName) - : fileName; - writing = write; + const char* const location = (directory != nullptr) + ? reprap.GetPlatform().GetMassStorage()->CombineName(directory, fileName) + : fileName; + writing = (mode == OpenMode::write || mode == OpenMode::append); - // Try to create the path of this file if we want to write to it if (writing) { - char filePathBuffer[FILENAME_LENGTH]; - StringRef filePath(filePathBuffer, FILENAME_LENGTH); + // Try to create the path of this file if we want to write to it + String<MaxFilenameLength> filePath; filePath.copy(location); - bool isVolume = isdigit(filePath[0]); - for(size_t i = 1; i < filePath.strlen(); i++) + size_t i = (isdigit(filePath[0]) && filePath[1] == ':') ? 2 : 0; + if (filePath[i] == '/') + { + ++i; + } + + while (i < filePath.strlen()) { if (filePath[i] == '/') { - if (isVolume) - { - isVolume = false; - continue; - } - filePath[i] = 0; - if (!platform->GetMassStorage()->DirectoryExists(filePath.Pointer()) && !platform->GetMassStorage()->MakeDirectory(filePath.Pointer())) + if (!reprap.GetPlatform().GetMassStorage()->DirectoryExists(filePath.Pointer()) && !reprap.GetPlatform().GetMassStorage()->MakeDirectory(filePath.Pointer())) { - platform->MessageF(GENERIC_MESSAGE, "Failed to create directory %s while trying to open file %s\n", - filePath.Pointer(), location); + reprap.GetPlatform().MessageF(ErrorMessage, "Failed to create directory %s while trying to open file %s\n", filePath.Pointer(), location); return false; } filePath[i] = '/'; } + ++i; } + + // Also try to allocate a write buffer so we can perform faster writes + // We only do this if the mode is write, not append, because we don't want to use up a large buffer to append messages to the log file, + // especially as we need to flush messages to SD card regularly. + // Currently, append mode is used only for the log file. + writeBuffer = (mode == OpenMode::write) ? reprap.GetPlatform().GetMassStorage()->AllocateWriteBuffer() : nullptr; } - FRESULT openReturn = f_open(&file, location, (writing) ? FA_CREATE_ALWAYS | FA_WRITE : FA_OPEN_EXISTING | FA_READ); + const FRESULT openReturn = f_open(&file, location, + (mode == OpenMode::write) ? FA_CREATE_ALWAYS | FA_WRITE + : (mode == OpenMode::append) ? FA_WRITE | FA_OPEN_ALWAYS + : FA_OPEN_EXISTING | FA_READ); if (openReturn != FR_OK) { // We no longer report an error if opening a file in read mode fails unless debugging is enabled, because sometimes that is quite normal. // It is up to the caller to report an error if necessary. if (reprap.Debug(modulePlatform)) { - platform->MessageF(GENERIC_MESSAGE, "Can't open %s to %s, error code %d\n", location, (writing) ? "write" : "read", openReturn); + reprap.GetPlatform().MessageF(ErrorMessage, "Can't open %s to %s, error code %d\n", location, (writing) ? "write" : "read", openReturn); } return false; } - + crc.Reset(); inUse = true; openCount = 1; return true; @@ -96,12 +118,14 @@ void FileStore::Duplicate() { if (!inUse) { - platform->Message(GENERIC_MESSAGE, "Error: Attempt to dup a non-open file.\n"); - return; + reprap.GetPlatform().Message(ErrorMessage, "Attempt to dup a non-open file.\n"); + } + else + { + irqflags_t flags = cpu_irq_save(); + ++openCount; + cpu_irq_restore(flags); } - irqflags_t flags = cpu_irq_save(); - ++openCount; - cpu_irq_restore(flags); } // This may be called from an ISR, in which case we need to defer the close @@ -114,7 +138,7 @@ bool FileStore::Close() return false; } - irqflags_t flags = cpu_irq_save(); + const irqflags_t flags = cpu_irq_save(); if (openCount > 1) { --openCount; @@ -129,11 +153,11 @@ bool FileStore::Close() if (!inUse) { - platform->Message(GENERIC_MESSAGE, "Error: Attempt to close a non-open file.\n"); + reprap.GetPlatform().Message(ErrorMessage, "Attempt to close a non-open file.\n"); return false; } - irqflags_t flags = cpu_irq_save(); + const irqflags_t flags = cpu_irq_save(); --openCount; bool leaveOpen = (openCount != 0); cpu_irq_restore(flags); @@ -143,16 +167,28 @@ bool FileStore::Close() return true; } + return ForceClose(); +} + +bool FileStore::ForceClose() +{ bool ok = true; if (writing) { ok = Flush(); } - FRESULT fr = f_close(&file); + if (writeBuffer != nullptr) + { + reprap.GetPlatform().GetMassStorage()->ReleaseWriteBuffer(writeBuffer); + writeBuffer = nullptr; + } + + const FRESULT fr = f_close(&file); inUse = false; writing = false; closeRequested = false; + openCount = 0; return ok && fr == FR_OK; } @@ -160,7 +196,7 @@ bool FileStore::Seek(FilePosition pos) { if (!inUse) { - platform->Message(GENERIC_MESSAGE, "Error: Attempt to seek on a non-open file.\n"); + reprap.GetPlatform().Message(ErrorMessage, "Attempt to seek on a non-open file.\n"); return false; } FRESULT fr = f_lseek(&file, pos); @@ -169,7 +205,12 @@ bool FileStore::Seek(FilePosition pos) FilePosition FileStore::Position() const { - return file.fptr; + return (inUse) ? file.fptr : 0; +} + +uint32_t FileStore::ClusterSize() const +{ + return (inUse) ? file.fs->csize * 512u : 1; // we divide by the cluster size so return 1 not 0 if there is an error } #if 0 // not currently used @@ -183,21 +224,11 @@ FilePosition FileStore::Length() const { if (!inUse) { - platform->Message(GENERIC_MESSAGE, "Error: Attempt to size non-open file.\n"); + reprap.GetPlatform().Message(ErrorMessage, "Attempt to size non-open file.\n"); return 0; } - return file.fsize; -} - -float FileStore::FractionRead() const -{ - FilePosition len = Length(); - if (len == 0) - { - return 0.0; - } - return (float)Position() / (float)len; + return (writeBuffer != nullptr) ? file.fsize + writeBuffer->BytesStored() : file.fsize; } // Single character read @@ -211,7 +242,7 @@ int FileStore::Read(char* extBuf, size_t nBytes) { if (!inUse) { - platform->Message(GENERIC_MESSAGE, "Error: Attempt to read from a non-open file.\n"); + reprap.GetPlatform().Message(ErrorMessage, "Attempt to read from a non-open file.\n"); return -1; } @@ -219,7 +250,7 @@ int FileStore::Read(char* extBuf, size_t nBytes) FRESULT readStatus = f_read(&file, extBuf, nBytes, &bytes_read); if (readStatus != FR_OK) { - platform->Message(GENERIC_MESSAGE, "Error: Cannot read file.\n"); + reprap.GetPlatform().Message(ErrorMessage, "Cannot read file.\n"); return -1; } return (int)bytes_read; @@ -259,6 +290,19 @@ int FileStore::ReadLine(char* buf, size_t nBytes) return i; } +FRESULT FileStore::Store(const char *s, size_t len, size_t *bytesWritten) +{ + uint32_t time = micros(); + crc.Update(s, len); + FRESULT writeStatus = f_write(&file, s, len, bytesWritten); + time = micros() - time; + if (time > longestWriteTime) + { + longestWriteTime = time; + } + return writeStatus; +} + bool FileStore::Write(char b) { return Write(&b, sizeof(char)); @@ -273,22 +317,42 @@ bool FileStore::Write(const char *s, size_t len) { if (!inUse) { - platform->Message(GENERIC_MESSAGE, "Error: Attempt to write block to a non-open file.\n"); + reprap.GetPlatform().Message(ErrorMessage, "Attempt to write block to a non-open file.\n"); return false; } - size_t bytesWritten; - uint32_t time = micros(); - - FRESULT writeStatus = f_write(&file, s, len, &bytesWritten); - time = micros() - time; - if (time > longestWriteTime) + size_t totalBytesWritten = 0; + FRESULT writeStatus = FR_OK; + if (writeBuffer == nullptr) { - longestWriteTime = time; + writeStatus = Store(s, len, &totalBytesWritten); } - if ((writeStatus != FR_OK) || (bytesWritten != len)) + else { - platform->Message(GENERIC_MESSAGE, "Error: Cannot write to file. Drive may be full.\n"); + do + { + size_t bytesStored = writeBuffer->Store(s + totalBytesWritten, len - totalBytesWritten); + if (writeBuffer->BytesLeft() == 0) + { + const size_t bytesToWrite = writeBuffer->BytesStored(); + size_t bytesWritten; + writeStatus = Store(writeBuffer->Data(), bytesToWrite, &bytesWritten); + writeBuffer->DataTaken(); + + if (bytesToWrite != bytesWritten) + { + // Something went wrong + break; + } + } + totalBytesWritten += bytesStored; + } + while (writeStatus == FR_OK && totalBytesWritten != len); + } + + if ((writeStatus != FR_OK) || (totalBytesWritten != len)) + { + reprap.GetPlatform().Message(ErrorMessage, "Failed to write to file. Drive may be full.\n"); return false; } return true; @@ -298,9 +362,27 @@ bool FileStore::Flush() { if (!inUse) { - platform->Message(GENERIC_MESSAGE, "Error: Attempt to flush a non-open file.\n"); + reprap.GetPlatform().Message(ErrorMessage, "Attempt to flush a non-open file.\n"); return false; } + + if (writeBuffer != nullptr) + { + const size_t bytesToWrite = writeBuffer->BytesStored(); + if (bytesToWrite != 0) + { + size_t bytesWritten; + const FRESULT writeStatus = Store(writeBuffer->Data(), bytesToWrite, &bytesWritten); + writeBuffer->DataTaken(); + + if ((writeStatus != FR_OK) || (bytesToWrite != bytesWritten)) + { + reprap.GetPlatform().Message(ErrorMessage, "Failed to write to file. Drive may be full.\n"); + return false; + } + } + } + return f_sync(&file) == FR_OK; } @@ -319,7 +401,7 @@ bool FileStore::SetClusterMap(uint32_t tbl[]) { if (!inUse) { - platform->Message(GENERIC_MESSAGE, "Error: Attempt to set cluster map for a non-open file.\n"); + platform->Message(ErrorMessage, "attempt to set cluster map for a non-open file.\n"); return false; } diff --git a/src/Storage/FileStore.h b/src/Storage/FileStore.h index 364bdc8f..5651b0bd 100644 --- a/src/Storage/FileStore.h +++ b/src/Storage/FileStore.h @@ -5,57 +5,75 @@ #include "Core.h" #include "Libraries/Fatfs/ff.h" - -const size_t FileBufLen = 256; // 512 would be more efficient, but need to free up some RAM first +#include "CRC32.h" class Platform; +class FileWriteBuffer; + +enum class OpenMode : uint8_t +{ + read, // open an existing file for reading + write, // write a file, replacing any existing file of the same name + append // append to an existing file, or create a new file if it is not found +}; class FileStore { public: + FileStore(); + + bool Open(const char* directory, const char* fileName, OpenMode mode); bool Read(char& b); // Read 1 byte int Read(char* buf, size_t nBytes); // Read a block of nBytes length int ReadLine(char* buf, size_t nBytes); // As Read but stop after '\n' or '\r\n' and null-terminate bool Write(char b); // Write 1 byte bool Write(const char *s, size_t len); // Write a block of len bytes + bool Write(const uint8_t *s, size_t len); // Write a block of len bytes bool Write(const char* s); // Write a string bool Close(); // Shut the file and tidy up + bool ForceClose(); bool Seek(FilePosition pos); // Jump to pos in the file FilePosition Position() const; // Return the current position in the file, assuming we are reading the file + uint32_t ClusterSize() const; // Cluster size in bytes + FilePosition Length() const; // File size in bytes #if 0 // not currently used bool GoToEnd(); // Position the file at the end (so you can write on the end). #endif - FilePosition Length() const; // File size in bytes - float FractionRead() const; // How far in we are + void Duplicate(); // Create a second reference to this file bool Flush(); // Write remaining buffer data - void Invalidate(const FATFS *fs); // Invalidate the file if it uses the specified FATFS object + bool Invalidate(const FATFS *fs, bool doClose); // Invalidate the file if it uses the specified FATFS object bool IsOpenOn(const FATFS *fs) const; // Return true if the file is open on the specified file system + uint32_t GetCRC32() const; #if 0 // not currently used bool SetClusterMap(uint32_t[]); // Provide a cluster map for fast seeking #endif static float GetAndClearLongestWriteTime(); // Return the longest time it took to write a block to a file, in milliseconds - friend class Platform; - -protected: - - FileStore(Platform* p); - void Init(); - bool Open(const char* directory, const char* fileName, bool write); + friend class MassStorage; private: - Platform* platform; + void Init(); + FRESULT Store(const char *s, size_t len, size_t *bytesWritten); // Write data to the non-volatile storage - FIL file; + FIL file; + FileWriteBuffer *writeBuffer; volatile unsigned int openCount; volatile bool closeRequested; bool inUse; bool writing; + CRC32 crc; static uint32_t longestWriteTime; }; +inline bool FileStore::Write(const uint8_t *s, size_t len) { return Write(reinterpret_cast<const char *>(s), len); } + +inline uint32_t FileStore::GetCRC32() const +{ + return crc.Get(); +} + #endif diff --git a/src/Storage/FileWriteBuffer.h b/src/Storage/FileWriteBuffer.h new file mode 100644 index 00000000..d5198e8c --- /dev/null +++ b/src/Storage/FileWriteBuffer.h @@ -0,0 +1,55 @@ +/* + * FileWriteBuffer.h + * + * Created on: 19 May 2017 + * Author: Christian + */ + +#ifndef SRC_STORAGE_FILEWRITEBUFFER_H_ +#define SRC_STORAGE_FILEWRITEBUFFER_H_ + +#include "RepRapFirmware.h" + +#if SAM4E || SAM4S || SAME70 +const size_t NumFileWriteBuffers = 2; // Number of write buffers +const size_t FileWriteBufLen = 8192; // Size of each write buffer +#else +const size_t NumFileWriteBuffers = 1; +const size_t FileWriteBufLen = 4096; +#endif + +// Class to cache data that is about to be written to the SD card. This is NOT a ring buffer, +// instead it just provides simple interfaces to cache a certain amount of data so that fewer +// f_write() calls are needed. This effectively improves upload speeds. +class FileWriteBuffer +{ +public: + FileWriteBuffer(FileWriteBuffer *n) : next(n), index(0) { } + + FileWriteBuffer *Next() const { return next; } + void SetNext(FileWriteBuffer *n) { next = n; } + + char *Data() { return reinterpret_cast<char *>(data32); } + const char *Data() const { return reinterpret_cast<const char *>(data32); } + const size_t BytesStored() const { return index; } + const size_t BytesLeft() const { return FileWriteBufLen - index; } + + size_t Store(const char *data, size_t length); // Stores some data and returns how much could be stored + void DataTaken() { index = 0; } // Called to indicate that the buffer has been written + +private: + FileWriteBuffer *next; + + size_t index; + uint32_t data32[FileWriteBufLen / sizeof(uint32_t)]; // 32-bit aligned buffer for better HSMCI performance +}; + +inline size_t FileWriteBuffer::Store(const char *data, size_t length) +{ + size_t bytesToStore = min<size_t>(BytesLeft(), length); + memcpy(Data() + index, data, bytesToStore); + index += bytesToStore; + return bytesToStore; +} + +#endif diff --git a/src/Storage/MassStorage.h b/src/Storage/MassStorage.h index 25ed0aee..42302f74 100644 --- a/src/Storage/MassStorage.h +++ b/src/Storage/MassStorage.h @@ -3,27 +3,30 @@ #include "RepRapFirmware.h" #include "Pins.h" +#include "FileWriteBuffer.h" #include "Libraries/Fatfs/ff.h" +#include "GCodes/GCodeResult.h" +#include "FileStore.h" #include <ctime> // Info returned by FindFirst/FindNext calls struct FileInfo { bool isDirectory; - char fileName[FILENAME_LENGTH]; - unsigned long size; + char fileName[MaxFilenameLength]; + uint32_t size; time_t lastModified; }; class MassStorage { public: - + FileStore* OpenFile(const char* directory, const char* fileName, OpenMode mode); bool FindFirst(const char *directory, FileInfo &file_info); bool FindNext(FileInfo &file_info); const char* GetMonthName(const uint8_t month); const char* CombineName(const char* directory, const char* fileName); - bool Delete(const char* directory, const char* fileName); + bool Delete(const char* directory, const char* fileName, bool silent = false); bool MakeDirectory(const char *parentDir, const char *dirName); bool MakeDirectory(const char *directory); bool Rename(const char *oldFilename, const char *newFilename); @@ -33,26 +36,66 @@ public: bool DirectoryExists(const char* directory, const char* subDirectory); time_t GetLastModifiedTime(const char* directory, const char *fileName) const; bool SetLastModifiedTime(const char* directory, const char *file, time_t time); - bool Mount(size_t card, StringRef& reply, bool reportSuccess); - bool Unmount(size_t card, StringRef& reply); - bool IsDriveMounted(size_t drive) const { return drive < NumSdCards && isMounted[drive]; } + GCodeResult Mount(size_t card, const StringRef& reply, bool reportSuccess); + GCodeResult Unmount(size_t card, const StringRef& reply); + bool IsDriveMounted(size_t drive) const { return drive < NumSdCards && info[drive].isMounted; } bool CheckDriveMounted(const char* path); + bool IsCardDetected(size_t card) const; + unsigned int InvalidateFiles(const FATFS *fs, bool doClose); // Invalidate all open files on the specified file system, returning the number of files invalidated + bool AnyFileOpen(const FATFS *fs) const; // Return true if any files are open on the file system + void CloseAllFiles(); + unsigned int GetNumFreeFiles() const; + void Spin(); + + enum class InfoResult : uint8_t + { + badSlot = 0, + noCard = 1, + ok = 2 + }; + + InfoResult GetCardInfo(size_t slot, uint64_t& capacity, uint64_t& freeSpace, uint32_t& speed, uint32_t& clSize); friend class Platform; +friend class FileStore; protected: - MassStorage(Platform* p); void Init(); + FileWriteBuffer *AllocateWriteBuffer(); + void ReleaseWriteBuffer(FileWriteBuffer *buffer); + private: + enum class CardDetectState : uint8_t + { + notPresent = 0, + inserting, + present, + removing + }; + + struct SdCardInfo + { + FATFS fileSystem; + uint32_t cdChangedTime; + uint32_t mountStartTime; + Pin cdPin; + bool mounting; + bool isMounted; + CardDetectState cardState; + }; + + bool InternalUnmount(size_t card, bool doClose); static time_t ConvertTimeStamp(uint16_t fdate, uint16_t ftime); - Platform* platform; - FATFS fileSystems[NumSdCards]; + SdCardInfo info[NumSdCards]; + DIR findDir; - bool isMounted[NumSdCards]; - char combinedName[FILENAME_LENGTH + 1]; + char combinedName[MaxFilenameLength + 1]; + FileWriteBuffer *freeWriteBuffers; + + FileStore files[MAX_FILES]; }; #endif |