373 lines
9.0 KiB
C
373 lines
9.0 KiB
C
#include <zephyr/fs/littlefs.h>
|
|
#include <zephyr/fs/fs.h>
|
|
#include <zephyr/logging/log.h>
|
|
#include <zephyr/pm/device.h>
|
|
|
|
#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;
|
|
} |