#define HAL_STORAGE_IMPL #include "HalStorage.h" #include // need to be included before SdFat.h for compatibility with FS.h's File class #include #include #include #define SDCard SDCardManager::getInstance() HalStorage HalStorage::instance; HalStorage::HalStorage() { storageMutex = xSemaphoreCreateMutex(); assert(storageMutex != nullptr); } // begin() and ready() are only called from setup, no need to acquire mutex for them bool HalStorage::begin() { return SDCard.begin(); } bool HalStorage::ready() const { return SDCard.ready(); } // For the rest of the methods, we acquire the mutex to ensure thread safety class HalStorage::StorageLock { public: StorageLock() { xSemaphoreTake(HalStorage::getInstance().storageMutex, portMAX_DELAY); } ~StorageLock() { xSemaphoreGive(HalStorage::getInstance().storageMutex); } }; #define HAL_STORAGE_WRAPPED_CALL(method, ...) \ HalStorage::StorageLock lock; \ return SDCard.method(__VA_ARGS__); std::vector HalStorage::listFiles(const char* path, int maxFiles) { HAL_STORAGE_WRAPPED_CALL(listFiles, path, maxFiles); } String HalStorage::readFile(const char* path) { HAL_STORAGE_WRAPPED_CALL(readFile, path); } bool HalStorage::readFileToStream(const char* path, Print& out, size_t chunkSize) { HAL_STORAGE_WRAPPED_CALL(readFileToStream, path, out, chunkSize); } size_t HalStorage::readFileToBuffer(const char* path, char* buffer, size_t bufferSize, size_t maxBytes) { HAL_STORAGE_WRAPPED_CALL(readFileToBuffer, path, buffer, bufferSize, maxBytes); } bool HalStorage::writeFile(const char* path, const String& content) { HAL_STORAGE_WRAPPED_CALL(writeFile, path, content); } bool HalStorage::ensureDirectoryExists(const char* path) { HAL_STORAGE_WRAPPED_CALL(ensureDirectoryExists, path); } class HalFile::Impl { public: Impl(FsFile&& fsFile) : file(std::move(fsFile)) {} FsFile file; }; HalFile::HalFile() = default; HalFile::HalFile(std::unique_ptr impl) : impl(std::move(impl)) {} HalFile::~HalFile() = default; HalFile::HalFile(HalFile&&) = default; HalFile& HalFile::operator=(HalFile&&) = default; HalFile HalStorage::open(const char* path, const oflag_t oflag) { StorageLock lock; // ensure thread safety for the duration of this function return HalFile(std::make_unique(SDCard.open(path, oflag))); } bool HalStorage::mkdir(const char* path, const bool pFlag) { HAL_STORAGE_WRAPPED_CALL(mkdir, path, pFlag); } bool HalStorage::exists(const char* path) { HAL_STORAGE_WRAPPED_CALL(exists, path); } bool HalStorage::remove(const char* path) { HAL_STORAGE_WRAPPED_CALL(remove, path); } bool HalStorage::rename(const char* oldPath, const char* newPath) { HAL_STORAGE_WRAPPED_CALL(rename, oldPath, newPath); } bool HalStorage::rmdir(const char* path) { HAL_STORAGE_WRAPPED_CALL(rmdir, path); } bool HalStorage::openFileForRead(const char* moduleName, const char* path, HalFile& file) { StorageLock lock; // ensure thread safety for the duration of this function FsFile fsFile; bool ok = SDCard.openFileForRead(moduleName, path, fsFile); file = HalFile(std::make_unique(std::move(fsFile))); return ok; } bool HalStorage::openFileForRead(const char* moduleName, const std::string& path, HalFile& file) { return openFileForRead(moduleName, path.c_str(), file); } bool HalStorage::openFileForRead(const char* moduleName, const String& path, HalFile& file) { return openFileForRead(moduleName, path.c_str(), file); } bool HalStorage::openFileForWrite(const char* moduleName, const char* path, HalFile& file) { StorageLock lock; // ensure thread safety for the duration of this function FsFile fsFile; bool ok = SDCard.openFileForWrite(moduleName, path, fsFile); file = HalFile(std::make_unique(std::move(fsFile))); return ok; } bool HalStorage::openFileForWrite(const char* moduleName, const std::string& path, HalFile& file) { return openFileForWrite(moduleName, path.c_str(), file); } bool HalStorage::openFileForWrite(const char* moduleName, const String& path, HalFile& file) { return openFileForWrite(moduleName, path.c_str(), file); } bool HalStorage::removeDir(const char* path) { HAL_STORAGE_WRAPPED_CALL(removeDir, path); } // HalFile implementation // Allow doing file operations while ensuring thread safety via HalStorage's mutex. // Please keep the list below in sync with the HalFile.h header #define HAL_FILE_WRAPPED_CALL(method, ...) \ HalStorage::StorageLock lock; \ assert(impl != nullptr); \ return impl->file.method(__VA_ARGS__); #define HAL_FILE_FORWARD_CALL(method, ...) \ assert(impl != nullptr); \ return impl->file.method(__VA_ARGS__); void HalFile::flush() { HAL_FILE_WRAPPED_CALL(flush, ); } size_t HalFile::getName(char* name, size_t len) { HAL_FILE_WRAPPED_CALL(getName, name, len); } size_t HalFile::size() { HAL_FILE_FORWARD_CALL(size, ); } // already thread-safe, no need to wrap size_t HalFile::fileSize() { HAL_FILE_FORWARD_CALL(fileSize, ); } // already thread-safe, no need to wrap bool HalFile::getModifyDateTime(uint16_t* pdate, uint16_t* ptime) { HAL_FILE_WRAPPED_CALL(getModifyDateTime, pdate, ptime); } bool HalFile::seek(size_t pos) { HAL_FILE_WRAPPED_CALL(seekSet, pos); } bool HalFile::seekCur(int64_t offset) { HAL_FILE_WRAPPED_CALL(seekCur, offset); } bool HalFile::seekSet(size_t offset) { HAL_FILE_WRAPPED_CALL(seekSet, offset); } int HalFile::available() const { HAL_FILE_WRAPPED_CALL(available, ); } size_t HalFile::position() const { HAL_FILE_WRAPPED_CALL(position, ); } int HalFile::read(void* buf, size_t count) { HAL_FILE_WRAPPED_CALL(read, buf, count); } int HalFile::read() { HAL_FILE_WRAPPED_CALL(read, ); } size_t HalFile::write(const void* buf, size_t count) { HAL_FILE_WRAPPED_CALL(write, buf, count); } size_t HalFile::write(uint8_t b) { HAL_FILE_WRAPPED_CALL(write, b); } bool HalFile::rename(const char* newPath) { HAL_FILE_WRAPPED_CALL(rename, newPath); } bool HalFile::isDirectory() const { HAL_FILE_FORWARD_CALL(isDirectory, ); } // already thread-safe, no need to wrap void HalFile::rewindDirectory() { HAL_FILE_WRAPPED_CALL(rewindDirectory, ); } bool HalFile::close() { HAL_FILE_WRAPPED_CALL(close, ); } HalFile HalFile::openNextFile() { HalStorage::StorageLock lock; assert(impl != nullptr); return HalFile(std::make_unique(impl->file.openNextFile())); } bool HalFile::isOpen() const { return impl != nullptr && impl->file.isOpen(); } // already thread-safe, no need to wrap HalFile::operator bool() const { return isOpen(); }