#include #include #include #include #include "fs_mgmt.h" LOG_MODULE_REGISTER(fs_mgmt, CONFIG_FS_MGMT_LOG_LEVEL); #define FS_PARTITION_ID FLASH_AREA_ID(littlefs_storage) FS_LITTLEFS_DECLARE_DEFAULT_CONFIG(fs_storage_data); #define QSPI_FLASH_NODE DT_ALIAS(qspi_flash) static const struct device *flash_dev = DEVICE_DT_GET(QSPI_FLASH_NODE); static struct fs_mount_t fs_storage_mnt = { .type = FS_LITTLEFS, .fs_data = &fs_storage_data, .storage_dev = (void *)FS_PARTITION_ID, .mnt_point = CONFIG_FS_MGMT_MOUNT_POINT, }; static int open_count = 0; static struct k_mutex flash_pm_lock; /** * @brief Puts the QSPI flash into deep sleep mode to save power * Decrements the open count and suspends the flash if no more users are active * @return 0 on success, negative error code on failure */ static int fs_mgmt_pm_flash_suspend(void) { #if IS_ENABLED(CONFIG_PM_DEVICE) if (!device_is_ready(flash_dev)) { return -ENODEV; } k_mutex_lock(&flash_pm_lock, K_FOREVER); if (open_count > 0) { open_count--; if (open_count == 0) { int rc = pm_device_action_run(flash_dev, PM_DEVICE_ACTION_SUSPEND); if (rc < 0) { LOG_WRN("Could not suspend flash: %d", rc); } else { LOG_DBG("Flash entered deep power-down"); } } } k_mutex_unlock(&flash_pm_lock); #endif /* CONFIG_PM_DEVICE */ return 0; } /** * @brief Resumes the QSPI flash from deep sleep mode * Increments the open count and resumes the flash if it was previously suspended * @return 0 on success, negative error code on failure */ static int fs_mgmt_pm_flash_resume(void) { #if IS_ENABLED(CONFIG_PM_DEVICE) if (!device_is_ready(flash_dev)) return -ENODEV; k_mutex_lock(&flash_pm_lock, K_FOREVER); if (open_count == 0) { int rc = pm_device_action_run(flash_dev, PM_DEVICE_ACTION_RESUME); if (rc == 0) { k_busy_wait(50); // t-exit-dpd LOG_DBG("Flash resumed"); } } open_count++; k_mutex_unlock(&flash_pm_lock); #endif /* CONFIG_PM_DEVICE */ return 0; } int fs_mgmt_pm_open(struct fs_file_t *file, const char *path, fs_mode_t mode) { LOG_DBG("PM Opening file '%s' with mode 0x%02x", path, mode); fs_mgmt_pm_flash_resume(); int rc = fs_open(file, path, mode); if (rc < 0) { fs_mgmt_pm_flash_suspend(); } return rc; } int fs_mgmt_pm_close(struct fs_file_t *file) { LOG_DBG("PM Closing file"); int rc = fs_close(file); fs_mgmt_pm_flash_suspend(); return rc; } int fs_mgmt_pm_opendir(struct fs_dir_t *dirp, const char *path) { LOG_DBG("PM Opening directory '%s'", path); fs_mgmt_pm_flash_resume(); int rc = fs_opendir(dirp, path); if (rc < 0) { fs_mgmt_pm_flash_suspend(); } return rc; } int fs_mgmt_pm_closedir(struct fs_dir_t *dirp) { LOG_DBG("PM Closing directory"); int rc = fs_closedir(dirp); fs_mgmt_pm_flash_suspend(); return rc; } int fs_mgmt_pm_unlink(const char *path) { LOG_DBG("PM Unlinking file '%s'", path); fs_mgmt_pm_flash_resume(); int rc = fs_unlink(path); fs_mgmt_pm_flash_suspend(); return rc; } int fs_mgmt_pm_statvfs(const char *path, struct fs_statvfs *stat) { LOG_DBG("PM Getting filesystem stats for '%s'", path); fs_mgmt_pm_flash_resume(); int rc = fs_statvfs(path, stat); fs_mgmt_pm_flash_suspend(); return rc; } int fs_mgmt_pm_stat(const char *path, struct fs_dirent *entry) { LOG_DBG("PM Getting stat for '%s'", path); fs_mgmt_pm_flash_resume(); int rc = fs_stat(path, entry); fs_mgmt_pm_flash_suspend(); return rc; } int fs_mgmt_pm_mkdir(const char *path) { LOG_DBG("PM Creating directory '%s'", path); fs_mgmt_pm_flash_resume(); int rc = fs_mkdir(path); fs_mgmt_pm_flash_suspend(); return rc; } int fs_mgmt_pm_rename(const char *old_path, const char *new_path) { LOG_DBG("PM Renaming '%s' to '%s'", old_path, new_path); fs_mgmt_pm_flash_resume(); int rc = fs_rename(old_path, new_path); fs_mgmt_pm_flash_suspend(); return rc; } int fs_mgmt_pm_rm_recursive(char *path_buf, size_t max_len) { struct fs_dirent entry; struct fs_dir_t dir; int rc; fs_mgmt_pm_flash_resume(); /* 1. Stat prüfen: Ist es eine Datei? */ rc = fs_stat(path_buf, &entry); if (rc != 0) { fs_mgmt_pm_flash_suspend(); return rc; } /* Wenn es eine Datei ist, direkt löschen und beenden */ if (entry.type == FS_DIR_ENTRY_FILE) { rc = fs_unlink(path_buf); fs_mgmt_pm_flash_suspend(); return rc; } /* 2. Es ist ein Verzeichnis. Schleife bis es leer ist. */ size_t orig_len = strlen(path_buf); while (1) { fs_dir_t_init(&dir); rc = fs_opendir(&dir, path_buf); if (rc != 0) { break; } bool found_something = false; /* Genau EINEN löschbaren Eintrag suchen */ while (1) { rc = fs_readdir(&dir, &entry); if (rc != 0 || entry.name[0] == '\0') { break; /* Ende oder Fehler */ } if (strcmp(entry.name, ".") == 0 || strcmp(entry.name, "..") == 0) { continue; /* Ignorieren */ } found_something = true; break; /* Treffer! Schleife abbrechen. */ } /* WICHTIG: Das Verzeichnis SOFORT schließen, BEVOR wir rekurieren! * Damit geben wir das File-Handle (NUM_DIRS) an Zephyr zurück. */ fs_closedir(&dir); if (!found_something || rc != 0) { break; /* Verzeichnis ist nun restlos leer */ } size_t name_len = strlen(entry.name); if (orig_len + 1 + name_len >= max_len) { rc = -ENAMETOOLONG; break; } /* Pfad für das gefundene Kindelement bauen */ path_buf[orig_len] = '/'; strcpy(&path_buf[orig_len + 1], entry.name); /* Rekursiver Aufruf für das Kind */ rc = fs_mgmt_pm_rm_recursive(path_buf, max_len); /* Puffer sofort wieder auf unser Verzeichnis zurückschneiden */ path_buf[orig_len] = '\0'; if (rc != 0) { break; /* Abbruch, falls beim Löschen des Kindes ein Fehler auftrat */ } } /* 3. Das nun restlos leere Verzeichnis selbst löschen */ if (rc == 0) { rc = fs_unlink(path_buf); } fs_mgmt_pm_flash_suspend(); return rc; } int fs_mgmt_pm_mkdir_recursive(char *path) { int rc = 0; struct fs_dirent entry; char *p = path; /* Führenden Slash überspringen, falls vorhanden (z. B. bei "/lfs") */ if (*p == '/') { p++; } /* Flash für den gesamten Durchlauf aktivieren */ fs_mgmt_pm_flash_resume(); while (*p != '\0') { if (*p == '/') { *p = '\0'; /* String temporär am aktuellen Slash terminieren */ /* Prüfen, ob dieser Pfadabschnitt bereits existiert */ rc = fs_stat(path, &entry); if (rc == -ENOENT) { /* Existiert nicht -> anlegen */ rc = fs_mkdir(path); if (rc != 0) { *p = '/'; /* Bei Fehler Slash wiederherstellen und abbrechen */ break; } } else if (rc == 0) { /* Existiert -> prüfen, ob es ein Verzeichnis ist */ if (entry.type != FS_DIR_ENTRY_DIR) { rc = -ENOTDIR; *p = '/'; break; } } else { /* Anderer Dateisystemfehler */ *p = '/'; break; } *p = '/'; /* Slash für den nächsten Schleifendurchlauf wiederherstellen */ } p++; } /* Letztes Element verarbeiten, falls der Pfad nicht mit '/' endet */ if (rc == 0 && p > path && *(p - 1) != '/') { rc = fs_stat(path, &entry); if (rc == -ENOENT) { rc = fs_mkdir(path); } else if (rc == 0) { if (entry.type != FS_DIR_ENTRY_DIR) { rc = -ENOTDIR; } } } /* Flash am Ende wieder in den Suspend schicken */ fs_mgmt_pm_flash_suspend(); return rc; } int fs_mgmt_init(void) { k_mutex_init(&flash_pm_lock); if (!device_is_ready(flash_dev)) { LOG_ERR("Flash device not ready!"); return -ENODEV; } fs_mgmt_pm_flash_resume(); int rc = fs_mount(&fs_storage_mnt); if (rc < 0) { LOG_ERR("Error mounting filesystem: %d", rc); return rc; } fs_mgmt_pm_flash_suspend(); LOG_DBG("Filesystem mounted successfully"); return 0; }