Files
OpenEPaperLink/ESP32_AP-Flasher/src/storage.cpp
Mimoja 88c5ee76be Report SDCard status to frontend
Signed-off-by: Mimoja <git@mimoja.de>
2023-07-07 14:13:44 +02:00

340 lines
8.9 KiB
C++

#include "storage.h"
#ifdef HAS_SDCARD
#include "FS.h"
#include "SD.h"
#include "SPI.h"
#endif
#include "LittleFS.h"
DynStorage::DynStorage() : isInited(0) {}
static void initLittleFS() {
LittleFS.begin();
contentFS = &LittleFS;
}
#ifdef HAS_SDCARD
static SPIClass* spi;
static void initSDCard() {
uint8_t spi_bus = VSPI;
// SD.begin and spi.begin are allocating memory so we dont want to do that
if(!spi) {
spi = new SPIClass(spi_bus);
spi->begin(SD_CARD_CLK, SD_CARD_MISO, SD_CARD_MOSI, SD_CARD_SS);
bool res = SD.begin(SD_CARD_SS, *spi, 40000000);
if (!res) {
Serial.println("Card Mount Failed");
return;
}
}
uint8_t cardType = SD.cardType();
if (cardType == CARD_NONE) {
Serial.println("No SD card attached");
return;
}
contentFS = &SD;
}
uint8_t DynStorage::cardType() {
return SD.cardType();
}
#endif
size_t DynStorage::freeSpace(){
this->begin();
#ifdef HAS_SDCARD
return SD.totalBytes() - SD.usedBytes();
#endif
return LittleFS.totalBytes() - LittleFS.usedBytes();
}
void copyFile(File in, File out) {
Serial.print("Copying ");
Serial.print(in.path());
Serial.print(" to ");
Serial.println(out.path());
size_t n;
uint8_t buf[64];
while ((n = in.read(buf, sizeof(buf))) > 0) {
out.write(buf, n);
}
}
void copyBetweenFS(FS& sourceFS, const char* source_path, FS& targetFS) {
File root = sourceFS.open(source_path);
char next_path[128];
if (root.isDirectory()) {
if (!contentFS->exists(root.path())) {
if (!contentFS->mkdir(root.path())) {
Serial.print("Failed to create directory ");
Serial.println(root.path());
return;
}
}
File file = root.openNextFile();
while (file) {
if (file.isDirectory()) {
sprintf(next_path, "%s/%s\0", root.path(), file.path());
copyBetweenFS(sourceFS, file.path(), targetFS);
} else {
File target = contentFS->open(file.path(), "w");
if (target) {
copyFile(file, target);
target.close();
file.close();
} else {
Serial.print("Couldn't create high target file");
Serial.println(file.path());
return;
}
}
file = root.openNextFile();
}
} else {
File target = contentFS->open(root.path(), "w");
if (target) {
copyFile(root, target);
} else {
Serial.print("Couldn't create target file ");
Serial.println(root.path());
return;
}
}
}
#ifdef HAS_SDCARD
void copyIfNeeded(const char* path) {
if (!contentFS->exists(path) && LittleFS.exists(path)) {
Serial.printf("SDCard does not contain %s, littleFS does, copying\n", path);
copyBetweenFS(LittleFS, path, *contentFS);
}
}
#endif
String md5Filepath = "/ota_md5.txt";
String otaFilepath = "/ota.bin";
SemaphoreHandle_t otaMutex;
static void fileSystemFirmwareUpdateTask(void* parameter) {
const char* md5 = nullptr;
String md5Str = "";
xSemaphoreTake(otaMutex, portMAX_DELAY);
File updateBin = contentFS->open(otaFilepath);
if (!updateBin) {
Serial.println("Failed to load " + otaFilepath);
wsErr("Failed to load " + otaFilepath);
xSemaphoreGive(otaMutex);
return;
}
if(updateBin.isDirectory()){
Serial.println("Error " + otaFilepath + " is not a file");
wsErr("Error " + otaFilepath + " is not a file");
updateBin.close();
xSemaphoreGive(otaMutex);
return;
}
// MD5 check
if (contentFS->exists(md5Filepath)) {
Serial.println("Reading MD5 file");
File md5File = contentFS->open(md5Filepath);
if (!md5File ) {
Serial.println("Failed to load ota md5 even tho an md5 file exists");
wsErr("Failed to load ota md5 even tho an md5 file exists");
contentFS->rename(md5Filepath, md5Filepath + "_failure.txt");
xSemaphoreGive(otaMutex);
return;
}
if(md5File.size() < 32){
Serial.println("md5 sum too short");
wsErr("md5 sum too short");
md5File.close();
contentFS->rename(md5Filepath, md5Filepath + "_failure.txt");
xSemaphoreGive(otaMutex);
return;
}
// Only take the md5 from the output of md5sum
// "471a53ab5e35fa9d3e642a82fa95f3ce .pio/build/Esp32-POE-ISO/firmware.bin"
md5Str = md5File.readStringUntil(' ');
md5Str.trim();
md5Str.toLowerCase();
Serial.println(String("ota.bin md5:") + md5Str);
wsLog(String("ota.bin md5:") + md5Str);
md5 = md5Str.c_str();
md5File.close();
}
bool success = executeUpdate(md5, updateBin.size(), updateBin, updateBin.size());
if (!success) {
Serial.println("Update failed.");
}
updateBin.close();
contentFS->remove(otaFilepath);
if (contentFS->exists(md5Filepath))
contentFS->remove(md5Filepath);
if (success) {
Serial.println("Rebooting now");
wsLog("Rebooting now!");
ESP.restart();
}
xSemaphoreGive(otaMutex);
}
void DynStorage::checkForUpdate() {
Storage.begin();
if (!contentFS->exists(otaFilepath)) {
return;
}
if(!otaMutex)
otaMutex = xSemaphoreCreateMutex();
// An update process is already running
if (xQueuePeek((xQueueHandle)otaMutex, (void*)NULL, (portTickType)NULL) != pdTRUE)
return;
File updateBin = contentFS->open(otaFilepath);
if (!updateBin) {
Serial.println("Failed to load " + otaFilepath);
wsErr("Failed to load " + otaFilepath);
}
size_t updateSize = updateBin.size();
if (updateSize <= 0) {
return;
}
updateBin.close();
Serial.println("Found OTA file on contentFS, updating async");
wsLog("Found OTA file on contentFS, updating async");
xTaskCreate(fileSystemFirmwareUpdateTask, "FSUpdateTask", 6144, NULL, 10, NULL);
}
void DynStorage::begin() {
initLittleFS();
#ifdef HAS_SDCARD
bool inited = initSDCard();
if(inited && inited != this->isInited) {
Serial.println("SDCard mounted");
wsLog("SDcard mounted");
copyIfNeeded("/index.html");
copyIfNeeded("/fonts");
copyIfNeeded("/www");
copyIfNeeded("/AP_FW_Pack.bin");
copyIfNeeded("/tag_md5_db.json");
copyIfNeeded("/update_actions.json");
copyIfNeeded("/content_template.json");
}
if (this->isInited && !inited) {
Serial.println("Lost connection to the SDCard");
wsErr("Lost connection to the SDCard");
}
this->isInited = inited;
#endif
if (!contentFS->exists("/current")) {
contentFS->mkdir("/current");
}
if (!contentFS->exists("/temp")) {
contentFS->mkdir("/temp");
}
}
void DynStorage::end() {
#ifdef HAS_SDCARD
initLittleFS();
if (SD_CARD_CLK == FLASHER_AP_CLK ||
SD_CARD_MISO == FLASHER_AP_MISO ||
SD_CARD_MOSI == FLASHER_AP_MOSI) {
Serial.println("Tearing down SD card connection");
copyBetweenFS(*contentFS, "/tag_md5_db.json", LittleFS);
copyBetweenFS(*contentFS, "/AP_FW_Pack.bin", LittleFS);
if (contentFS->exists("/AP_force_flash.bin")) {
copyBetweenFS(*contentFS, "/AP_force_flash.bin", LittleFS);
contentFS->remove("/AP_force_flash.bin");
}
Serial.println("Swapping to LittleFS");
contentFS = &LittleFS;
}
#endif
}
void listDir(fs::FS& fs, const char* dirname, uint8_t levels) {
Storage.begin();
// Print blank line on screen
Serial.printf(" \n ");
Serial.printf("Listing directory: %s\n", dirname);
File root = fs.open(dirname);
if (!root) {
Serial.println("Failed to open directory");
return;
}
if (!root.isDirectory()) {
Serial.println("Not a directory");
return;
}
File file = root.openNextFile();
while (file) {
if (!strcmp("System Volume Information", file.name())) {
file = root.openNextFile();
continue;
}
if (file.isDirectory()) {
Serial.print(" DIR : ");
Serial.println(file.name());
if (levels) {
listDir(fs, file.path(), levels - 1);
}
Serial.println();
} else {
Serial.print(" FILE: ");
Serial.print(file.name());
Serial.print(" SIZE: ");
Serial.println(file.size());
}
file = root.openNextFile();
}
}
void DynStorage::listFiles() {
listDir(LittleFS, "/", 1);
#ifdef HAS_SDCARD
listDir(*contentFS, "/", 1);
#endif
}
fs::FS* contentFS;
DynStorage Storage;