vor ble umbau
This commit is contained in:
17
firmware/libs/fs_mgmt/CMakeLists.txt
Normal file
17
firmware/libs/fs_mgmt/CMakeLists.txt
Normal file
@@ -0,0 +1,17 @@
|
||||
if(CONFIG_FS_MGMT)
|
||||
zephyr_library()
|
||||
zephyr_library_sources(src/fs_mgmt.c)
|
||||
zephyr_include_directories(include)
|
||||
|
||||
if(CONFIG_FILE_SYSTEM_LITTLEFS)
|
||||
if(DEFINED ZEPHYR_LITTLEFS_MODULE_DIR)
|
||||
zephyr_include_directories(${ZEPHYR_LITTLEFS_MODULE_DIR})
|
||||
elseif(DEFINED WEST_TOPDIR)
|
||||
zephyr_include_directories(${WEST_TOPDIR}/modules/fs/littlefs)
|
||||
endif()
|
||||
|
||||
if(DEFINED ZEPHYR_BASE)
|
||||
zephyr_include_directories(${ZEPHYR_BASE}/modules/littlefs)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
40
firmware/libs/fs_mgmt/Kconfig
Normal file
40
firmware/libs/fs_mgmt/Kconfig
Normal file
@@ -0,0 +1,40 @@
|
||||
menuconfig FS_MGMT
|
||||
bool "File System Management"
|
||||
select FLASH
|
||||
select FLASH_MAP
|
||||
select FILE_SYSTEM
|
||||
select FILE_SYSTEM_LITTLEFS
|
||||
select FILE_SYSTEM_MKFS
|
||||
select FLASH_PAGE_LAYOUT
|
||||
select NORDIC_QSPI_NOR if SOC_SERIES_NRF52X && (SOC_NRF52840_QIAA || SOC_NRF52833_QIAA)
|
||||
help
|
||||
Library for initializing and managing the file system.
|
||||
|
||||
if FS_MGMT
|
||||
config FS_MGMT_MOUNT_POINT
|
||||
string "Littlefs Mount Point"
|
||||
default "/lfs"
|
||||
help
|
||||
Set the mount point for the Littlefs file system. Default is "/lfs".
|
||||
|
||||
if SOC_SERIES_NRF52X
|
||||
config PM_PARTITION_REGION_LITTLEFS_EXTERNAL
|
||||
default y
|
||||
|
||||
config PM_PARTITION_SIZE_LITTLEFS
|
||||
default 0x1000000
|
||||
endif # SOC_SERIES_NRF52X
|
||||
|
||||
config FS_LITTLEFS_READ_SIZE
|
||||
default 256
|
||||
config FS_LITTLEFS_PROG_SIZE
|
||||
default 256
|
||||
config FS_LITTLEFS_CACHE_SIZE
|
||||
default 4096
|
||||
config FS_LITTLEFS_LOOKAHEAD_SIZE
|
||||
default 512
|
||||
|
||||
module = FS_MGMT
|
||||
module-str = fs_mgmt
|
||||
source "subsys/logging/Kconfig.template.log_config"
|
||||
endif # FS_MGMT
|
||||
117
firmware/libs/fs_mgmt/include/fs_mgmt.h
Normal file
117
firmware/libs/fs_mgmt/include/fs_mgmt.h
Normal file
@@ -0,0 +1,117 @@
|
||||
#ifndef FS_MGMT_H
|
||||
#define FS_MGMT_H
|
||||
|
||||
#include <zephyr/fs/fs.h>
|
||||
|
||||
#define FS_MGMT_MAX_PATH_LENGTH 32
|
||||
#define FS_AUDIO_PATH CONFIG_FS_MGMT_MOUNT_POINT "/a"
|
||||
#define FS_SYSTEM_PATH CONFIG_FS_MGMT_MOUNT_POINT "/sys"
|
||||
|
||||
/**
|
||||
* @brief Initializes the filesystem management module.
|
||||
*/
|
||||
int fs_mgmt_init(void);
|
||||
|
||||
// /**
|
||||
// * @brief Puts the QSPI flash into deep sleep mode to save power
|
||||
// */
|
||||
// int fs_pm_flash_suspend(void);
|
||||
|
||||
// /**
|
||||
// * @brief Resumes the QSPI flash from deep sleep mode
|
||||
// */
|
||||
// int fs_pm_flash_resume(void);
|
||||
|
||||
/**
|
||||
* @brief Wrapper around fs_open that handles power management for the flash
|
||||
* Resumes the flash before opening and suspends it if opening fails
|
||||
* @param file Pointer to fs_file_t structure to be initialized
|
||||
* @param path Path to the file to open
|
||||
* @param mode Open flags (e.g. FS_O_READ, FS_O_WRITE)
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
int fs_mgmt_pm_open(struct fs_file_t *file, const char *path, fs_mode_t mode);
|
||||
|
||||
/**
|
||||
* @brief Wrapper around fs_close that handles power management for the flash
|
||||
* Resumes the flash after closing and suspends it if closing fails
|
||||
* @param file Pointer to fs_file_t structure to be closed
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
int fs_mgmt_pm_close(struct fs_file_t *file);
|
||||
|
||||
/**
|
||||
* @brief Wrapper around fs_opendir that handles power management for the flash
|
||||
* Resumes the flash before opening and suspends it if opening fails
|
||||
* @param dirp Pointer to fs_dir_t structure to be initialized
|
||||
* @param path Path to the directory to open
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
int fs_mgmt_pm_opendir(struct fs_dir_t *dirp, const char *path);
|
||||
|
||||
/**
|
||||
* @brief Wrapper around fs_closedir that handles power management for the flash
|
||||
* Resumes the flash after closing and suspends it if closing fails
|
||||
* @param dirp Pointer to fs_dir_t structure to be closed
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
int fs_mgmt_pm_closedir(struct fs_dir_t *dirp);
|
||||
|
||||
/**
|
||||
* @brief Unlinks (deletes) a file, ensuring the flash is active during the operation
|
||||
* @param path Path to the file to unlink
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
int fs_mgmt_pm_unlink(const char *path);
|
||||
|
||||
/**
|
||||
* @brief Wrapper around fs_statvfs that handles power management for the flash
|
||||
* Resumes the flash before getting stats and suspends it afterwards
|
||||
* @param path Path to the filesystem to get stats for
|
||||
* @param stat Pointer to fs_statvfs structure to be filled with stats
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
int fs_mgmt_pm_statvfs(const char *path, struct fs_statvfs *stat);
|
||||
|
||||
/**
|
||||
* @brief Wrapper around fs_stat that handles power management for the flash
|
||||
* Resumes the flash before stat and suspends it afterwards
|
||||
* @param path Path to file or directory
|
||||
* @param entry Pointer to fs_dirent structure to receive metadata
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
int fs_mgmt_pm_stat(const char *path, struct fs_dirent *entry);
|
||||
|
||||
/**
|
||||
* @brief Wrapper around fs_mkdir that handles power management for the flash
|
||||
* Resumes the flash before creating the directory and suspends it afterwards
|
||||
* @param path Path to the directory to create
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
int fs_mgmt_pm_mkdir(const char *path);
|
||||
|
||||
/**
|
||||
* @brief Wrapper around fs_rename that handles power management for the flash
|
||||
* Resumes the flash before renaming and suspends it afterwards
|
||||
* @param old_path Current path of the file or directory
|
||||
* @param new_path New path for the file or directory
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
int fs_mgmt_pm_rename(const char *old_path, const char *new_path);
|
||||
|
||||
/**
|
||||
* @brief Recursively creates directories for the given path, ensuring the flash is active during the operation
|
||||
* @param path Path to the directory to create (can include multiple levels, e.g. "/dir1/dir2/dir3")
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
int fs_mgmt_pm_mkdir_recursive(char *path);
|
||||
|
||||
/**
|
||||
* @brief Recursively removes a directory and all its contents, ensuring the flash is active during the operation
|
||||
* @param path Path to the directory to remove
|
||||
* @param max_len Maximum length of the path buffer
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
int fs_mgmt_pm_rm_recursive(char *path, size_t max_len);
|
||||
|
||||
#endif /* FS_MGMT_H */
|
||||
373
firmware/libs/fs_mgmt/src/fs_mgmt.c
Normal file
373
firmware/libs/fs_mgmt/src/fs_mgmt.c
Normal file
@@ -0,0 +1,373 @@
|
||||
#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;
|
||||
}
|
||||
Reference in New Issue
Block a user