From 6d1686e0e9f0a99337be4af5cf8c70e68e8ebdaa Mon Sep 17 00:00:00 2001 From: swym <0xfd000000@gmail.com> Date: Fri, 9 Aug 2019 14:50:45 +0000 Subject: [PATCH] feat(fatfs): Implement global FLASH lock - Implement de-initialization - Wrap filesystem operations in semaphore - Introduce EpicFileSystem object and move epic_file_FOO(...) imlementations into efs_FOO(EpicFileSystem*, ...) functions. - epic_file_FOO(...) functions are now wrappers around the _fs_ functions, but lock and unlock the global filesystem object before & after calls. This way, all efs_ functions can assume that the necessary lock has been acquired. - libff: don't use FF_FS_REENTRANT, our own FS lock is enough --- epicardium/fs/filesystem_fat.c | 478 +++++++++++++++++++++++++++++ epicardium/fs/internal.h | 41 +++ epicardium/main.c | 1 + epicardium/meson.build | 1 + epicardium/modules/fatfs.c | 150 --------- epicardium/modules/fatfs_fileops.c | 300 ------------------ epicardium/modules/fileops.c | 97 ++++++ epicardium/modules/filesystem.h | 25 ++ epicardium/modules/meson.build | 3 +- epicardium/modules/modules.h | 9 - lib/ff13/Source/diskio.c | 40 ++- lib/ff13/Source/diskio.h | 1 + lib/ff13/Source/ffconf.h | 4 +- pycardium/mpconfigport.h | 1 + 14 files changed, 677 insertions(+), 474 deletions(-) create mode 100644 epicardium/fs/filesystem_fat.c create mode 100644 epicardium/fs/internal.h delete mode 100644 epicardium/modules/fatfs.c delete mode 100644 epicardium/modules/fatfs_fileops.c create mode 100644 epicardium/modules/fileops.c create mode 100644 epicardium/modules/filesystem.h diff --git a/epicardium/fs/filesystem_fat.c b/epicardium/fs/filesystem_fat.c new file mode 100644 index 00000000..a96198fe --- /dev/null +++ b/epicardium/fs/filesystem_fat.c @@ -0,0 +1,478 @@ +/* + * Implementation of efs_ API functions for a FatFS specific + * EpicFileSystem + */ + +#include <errno.h> +#include <stddef.h> +#include <stdio.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#include <ff.h> +#include <diskio.h> + +#include <FreeRTOS.h> +#include <semphr.h> + +#include "fs/internal.h" +#include "modules/filesystem.h" +#include "epicardium.h" +#include "card10.h" +#include "modules/log.h" + +#define SSLOG_INFO(...) LOG_INFO("fatfs", __VA_ARGS__) +#define SSLOG_ERR(...) LOG_ERR("fatfs", __VA_ARGS__) + +#ifndef EPIC_FAT_STATIC_SEMAPHORE +#define EPIC_FAT_STATIC_SEMAPHORE 0 +#endif + +/* clang-format off */ +#define EPIC_FAT_MAX_OPENED (1 << (EPIC_FAT_FD_INDEX_BITS)) +#define EPIC_FAT_FD_GENERATION_BITS (31 - (EPIC_FAT_FD_INDEX_BITS)) +#define EPIC_FAT_FD_INDEX_MASK (uint32_t)((1u << EPIC_FAT_FD_INDEX_BITS) - 1) +#define EPIC_FAT_FD_INDEX(fd) ((uint32_t)(fd)&EPIC_FAT_FD_INDEX_MASK) +#define EPIC_FAT_FD_GENERATION(fd) ((uint32_t)(fd) >> EPIC_FAT_FD_INDEX_BITS) +#define EPIC_FAT_FD_MAX_GENERATION (uint32_t)((1u << EPIC_FAT_FD_GENERATION_BITS) - 1) +#define EPIC_FAT_FD(idx, gen) (int)(((uint32_t)(gen) << EPIC_FAT_FD_INDEX_BITS) \ + | ((uint32_t)(idx)&EPIC_FAT_FD_INDEX_MASK)) +/* clang-format on */ + +struct FatObject { + uint32_t generation; + enum epic_stat_type type; + union { + FIL file; + DIR dir; + }; +}; + +struct EpicFileSystem { + struct FatObject pool[EPIC_FAT_MAX_OPENED]; + uint32_t generationCount; + bool initialized; + FATFS FatFs; +}; + +// this table converts from FRESULT to POSIX errno +static const int s_libffToErrno[20]; + +static const char *f_get_rc_string(FRESULT rc); +static bool globalLockAccquire(); +static void globalLockRelease(); +static void efs_close_all(EpicFileSystem *fs); + +static bool efs_get_opened( + EpicFileSystem *fs, + int i, + enum epic_stat_type expected, + struct FatObject **res, + int *rc +); + +static EpicFileSystem s_globalFileSystem; + +#if (EPIC_FAT_STATIC_SEMAPHORE == 1) +static StaticSemaphore_t s_globalLockBuffer; +#endif + +static SemaphoreHandle_t s_globalLock = NULL; + +void fatfs_init() +{ + static volatile bool s_initCalled = false; + //this has to be called vefore any tasks have been started! + // ...not 100% water-tight though, since tick count might be zero even after vTaskStartScheduler + // has been called... + assert(xTaskGetTickCount() == configINITIAL_TICK_COUNT); + assert(!s_initCalled); + s_initCalled = true; + +#if (EPIC_FAT_STATIC_SEMAPHORE == 1) + s_globalLock = xSemaphoreCreateMutexStatic(&s_globalLockBuffer); +#else + s_globalLock = xSemaphoreCreateMutex(); +#endif + fatfs_attach(); +} + +/* + * NOTE about attach/detach: + * + * while in detach, we're calling diskio_deinitialize (a function that is + * originally not present in libff's diskio.h), we do not need to call + * diskio_initialize in attach, since it will implicitly be called by + * any f_ operation, via libff's find_volume for volumes that have not + * been mounted yet. + * + */ +int fatfs_attach() +{ + FRESULT ff_res; + int rc = 0; + if (globalLockAccquire()) { + EpicFileSystem *fs = &s_globalFileSystem; + if (!fs->initialized) { + ff_res = f_mount(&fs->FatFs, "/", 0); + if (ff_res == FR_OK) { + fs->initialized = true; + SSLOG_INFO("FatFs mounted\n"); + } else { + SSLOG_ERR( + "f_mount error %s\n", + f_get_rc_string(ff_res) + ); + rc = -s_libffToErrno[ff_res]; + } + } + + globalLockRelease(); + } else { + SSLOG_ERR("Failed to lock\n"); + } + return rc; +} + +void fatfs_detach() +{ + FRESULT ff_res; + EpicFileSystem *fs; + if (efs_lock_global(&fs) == 0) { + efs_close_all(fs); + + //unmount by passing NULL as fs object, will destroy our sync object via ff_del_syncobj + ff_res = f_mount(NULL, "/", 0); + if (ff_res != FR_OK) { + SSLOG_ERR( + "f_mount (unmount) error %s\n", + f_get_rc_string(ff_res) + ); + } + + fs->initialized = false; + disk_deinitialize(); + SSLOG_INFO("detached\n"); + efs_unlock_global(fs); + } +} + +static const char *f_get_rc_string(FRESULT rc) +{ + static const TCHAR *rcstrings = + _T("OK\0DISK_ERR\0INT_ERR\0NOT_READY\0NO_FILE\0NO_PATH\0INVALID_NAME\0") + _T("DENIED\0EXIST\0INVALID_OBJECT\0WRITE_PROTECTED\0INVALID_DRIVE\0") + _T("NOT_ENABLED\0NO_FILESYSTEM\0MKFS_ABORTED\0TIMEOUT\0LOCKED\0") + _T("NOT_ENOUGH_CORE\0TOO_MANY_OPEN_FILES\0INVALID_PARAMETER\0"); + + FRESULT i; + const char *p = rcstrings; + + for (i = 0; i != rc && *p; i++) { + while (*p++) + ; + } + return p; +} + +static bool globalLockAccquire() +{ + return (int)(xSemaphoreTake(s_globalLock, FF_FS_TIMEOUT) == pdTRUE); +} + +static void globalLockRelease() +{ + xSemaphoreGive(s_globalLock); +} + +int efs_lock_global(EpicFileSystem **fs) +{ + *fs = NULL; + if (!globalLockAccquire()) { + return -EBUSY; + } + if (!s_globalFileSystem.initialized) { + globalLockRelease(); + return -ENODEV; + } + *fs = &s_globalFileSystem; + return 0; +} + +void efs_unlock_global(EpicFileSystem *fs) +{ + (void)fs; + globalLockRelease(); +} + +static bool efs_get_opened( + EpicFileSystem *fs, + int fd, + enum epic_stat_type expected, + struct FatObject **obj, + int *rc +) { + uint32_t index = EPIC_FAT_FD_INDEX(fd); + uint32_t generation = EPIC_FAT_FD_GENERATION(fd); + + *obj = NULL; + *rc = -EBADF; + + if (index >= EPIC_FAT_MAX_OPENED || + generation >= EPIC_FAT_FD_MAX_GENERATION) { + return false; + } + if (fs->pool[index].type != expected || + fs->pool[index].generation != generation) { + return false; + } + + *obj = &fs->pool[index]; + *rc = 0; + return true; +} + +/* here we're trying to mirror glibc's behaviour: + * any combination of rwax parses but only the first of those flags wins: + * - rw, ra, rr all open read-only + * a `+` at any position but the first turns into read-write + * any other character at any position yields EINVAL + */ +static inline bool parse_mode(const char *mstring, int *mode) +{ + switch (mstring[0]) { + case 'r': + *mode = FA_READ; + break; + case 'w': + *mode = FA_CREATE_ALWAYS | FA_WRITE; + break; + case 'x': + //in constrast to FA_CREATE_ALWAYS, FA_CREATE_NEW fails for existing files + *mode = FA_WRITE | FA_CREATE_NEW; + break; + case 'a': + //in constrast to FA_CREATE_ALWAYS, FA_CREATE_NEW fails for existing files + *mode = FA_WRITE | FA_OPEN_APPEND; + break; + default: + return false; + } + while (*mstring) { + switch (*mstring++) { + case '+': //turns any of r,w,x into read&write + *mode |= FA_READ | FA_WRITE; + break; + case 'r': //fallthrough intentional + case 'w': //fallthrough intentional + case 'a': //fallthrough intentional + case 'x': //fallthrough intentional + case 'b': //fallthrough intentional + break; + default: + return false; + } + } + return true; +} + +int efs_open(EpicFileSystem *fs, const char *filename, const char *modeString) +{ + struct FatObject *o = NULL; + uint32_t index, generation; + int mode = 0; + int res; + + //find free object to use + for (index = 0; index < EPIC_FAT_MAX_OPENED; ++index) { + if (fs->pool[index].type == EPICSTAT_NONE) { + break; + } + } + if (index == EPIC_FAT_MAX_OPENED) { + return -s_libffToErrno[FR_TOO_MANY_OPEN_FILES]; + } + + generation = fs->generationCount++; + if (generation == EPIC_FAT_FD_MAX_GENERATION) { + fs->generationCount = 1; + } + o = &fs->pool[index]; + + if (!parse_mode(modeString, &mode)) { + return -EINVAL; + } + + res = f_open(&o->file, filename, mode); + if (res != FR_OK) { + return -s_libffToErrno[res]; + } + + o->type = EPICSTAT_FILE; + o->generation = generation; + + // for 'a' mode, we must begin at the end of the file + if ((mode & FA_OPEN_APPEND) != 0) { + f_lseek(&o->file, f_size(&o->file)); + } + + return EPIC_FAT_FD(index, generation); +} + +int efs_close(EpicFileSystem *fs, int fd) +{ + int res; + struct FatObject *o; + if (efs_get_opened(fs, fd, EPICSTAT_FILE, &o, &res)) { + res = f_close(&o->file); + if (res != FR_OK) { + return -s_libffToErrno[res]; + } + + o->type = EPICSTAT_NONE; + o->generation = 0; + } + return res; +} + +void efs_close_all(EpicFileSystem *fs) +{ + for (int i = 0; i < EPIC_FAT_MAX_OPENED; ++i) { + switch (fs->pool[i].type) { + case EPICSTAT_FILE: + f_close(&fs->pool[i].file); + break; + case EPICSTAT_DIR: + //NYI + break; + case EPICSTAT_NONE: + break; + } + fs->pool[i].type = EPICSTAT_NONE; + fs->pool[i].generation = 0; + } +} + +int efs_read(EpicFileSystem *fs, int fd, void *buf, size_t nbytes) +{ + unsigned int nread = 0; + + int res; + struct FatObject *o; + if (efs_get_opened(fs, fd, EPICSTAT_FILE, &o, &res)) { + res = f_read(&o->file, buf, nbytes, &nread); + if (res != FR_OK) { + return -s_libffToErrno[res]; + } + res = (int)nread; + } + + return res; +} + +int efs_write(EpicFileSystem *fs, int fd, const void *buf, size_t nbytes) +{ + unsigned int nwritten = 0; + + int res; + struct FatObject *o; + if (efs_get_opened(fs, fd, EPICSTAT_FILE, &o, &res)) { + res = f_write(&o->file, buf, nbytes, &nwritten); + if (res != FR_OK) { + res = -s_libffToErrno[res]; + } else { + res = (int)nwritten; + } + } + return res; +} + +int efs_flush(EpicFileSystem *fs, int fd) +{ + int res = 0; + struct FatObject *o; + if (efs_get_opened(fs, fd, EPICSTAT_FILE, &o, &res)) { + res = f_sync(&o->file); + if (res != FR_OK) { + res = -s_libffToErrno[res]; + } + } + + return res; +} + +int efs_seek(EpicFileSystem *fs, int fd, long offset, int whence) +{ + int res = 0; + struct FatObject *o; + if (efs_get_opened(fs, fd, EPICSTAT_FILE, &o, &res)) { + switch (whence) { + case SEEK_SET: + res = f_lseek(&o->file, offset); + break; + + case SEEK_CUR: + res = f_lseek(&o->file, f_tell(&o->file) + offset); + break; + + case SEEK_END: + res = f_lseek(&o->file, f_size(&o->file) + offset); + break; + default: + return -EINVAL; + } + res = -s_libffToErrno[res]; + } + return res; +} + +int efs_tell(EpicFileSystem *fs, int fd) +{ + int res; + struct FatObject *o; + if (efs_get_opened(fs, fd, EPICSTAT_FILE, &o, &res)) { + //f_tell simply accesses fp->fptr so no errors are expected - return directly + res = f_tell(&o->file); + } + return res; +} + +int efs_stat(EpicFileSystem *fs, const char *filename, struct epic_stat *stat) +{ + int res = 0; + FILINFO finfo; + res = f_stat(filename, &finfo); + if (res == 0) { + if (finfo.fattrib & AM_DIR) { + stat->type = EPICSTAT_DIR; + } else { + stat->type = EPICSTAT_FILE; + } + } + return -s_libffToErrno[res]; +} + +static const int s_libffToErrno[20] = { + [FR_OK] = 0, + [FR_DISK_ERR] = EIO, + [FR_INT_ERR] = EIO, + [FR_NOT_READY] = EBUSY, + [FR_NO_FILE] = ENOENT, + [FR_NO_PATH] = ENOENT, + [FR_INVALID_NAME] = EINVAL, + [FR_DENIED] = EACCES, + [FR_EXIST] = EEXIST, + [FR_INVALID_OBJECT] = EINVAL, + [FR_WRITE_PROTECTED] = EROFS, + [FR_INVALID_DRIVE] = ENODEV, + [FR_NOT_ENABLED] = ENODEV, + [FR_NO_FILESYSTEM] = ENODEV, + [FR_MKFS_ABORTED] = EIO, + [FR_TIMEOUT] = EIO, + [FR_LOCKED] = EIO, + [FR_NOT_ENOUGH_CORE] = ENOMEM, + [FR_TOO_MANY_OPEN_FILES] = EMFILE, + [FR_INVALID_PARAMETER] = EINVAL, +}; diff --git a/epicardium/fs/internal.h b/epicardium/fs/internal.h new file mode 100644 index 00000000..b3f3acb3 --- /dev/null +++ b/epicardium/fs/internal.h @@ -0,0 +1,41 @@ +#ifndef EPICARCIUM_FS_INTERNAL_H_INCLUDED +#define EPICARCIUM_FS_INTERNAL_H_INCLUDED + +#include <stdint.h> +#include <stddef.h> +#include <stdbool.h> + +#include "epicardium.h" + +/* Number of bits to use for indexing into our internal pool of files/directories + * This indirectly specifies the size of the pool as 1^EPIC_FAT_FD_INDEX_BITS + * Increase if number of open file descriptors is not enough, but be aware of + * memory usage of the pool! + */ +#define EPIC_FAT_FD_INDEX_BITS 4 +#define EPIC_FAT_STATIC_SEMAPHORE 1 + +// forward declaration, actual definition is in filesystem_fat.c +typedef struct EpicFileSystem EpicFileSystem; + +int efs_open(EpicFileSystem *fs, const char *filename, const char *modeString); +int efs_close(EpicFileSystem *fs, int fd); +int efs_read(EpicFileSystem *fs, int fd, void *buf, size_t nbytes); +int efs_write(EpicFileSystem *fs, int fd, const void *buf, size_t nbytes); +int efs_flush(EpicFileSystem *fs, int fd); +int efs_seek(EpicFileSystem *fs, int fd, long offset, int whence); +int efs_tell(EpicFileSystem *fs, int fd); +int efs_stat(EpicFileSystem *fs, const char *filename, struct epic_stat *stat); +/** + * lock global filesystem + * + * locks the global mutex and, if the global EpicFileSystem has been initialized correctly + * passes it to *fs + * + * Upon successful return, the filesystem has to be re-locked with epic_fs_unlock_global + * In case of error, the filesystem will be left in a locked state. + */ +int efs_lock_global(EpicFileSystem** fs); +void efs_unlock_global(EpicFileSystem* fs); + +#endif// EPICARCIUM_FS_INTERNAL_H_INCLUDED diff --git a/epicardium/main.c b/epicardium/main.c index ebadaf95..89d5c07d 100644 --- a/epicardium/main.c +++ b/epicardium/main.c @@ -15,6 +15,7 @@ #include "modules/modules.h" #include "modules/log.h" #include "modules/stream.h" +#include "modules/filesystem.h" #include "api/interrupt-sender.h" #include <Heart.h> diff --git a/epicardium/meson.build b/epicardium/meson.build index 9b3880a8..9c8b12b8 100644 --- a/epicardium/meson.build +++ b/epicardium/meson.build @@ -74,6 +74,7 @@ elf = executable( 'cdcacm.c', 'main.c', 'support.c', + 'fs/filesystem_fat.c', module_sources, l0der_sources, ble_sources, diff --git a/epicardium/modules/fatfs.c b/epicardium/modules/fatfs.c deleted file mode 100644 index 1962f493..00000000 --- a/epicardium/modules/fatfs.c +++ /dev/null @@ -1,150 +0,0 @@ -/* - * support routines for FatFs - */ - -#include <errno.h> -#include <stddef.h> -#include <stdio.h> -#include <stdbool.h> -#include <stdlib.h> - -#include <ff.h> - -#include <FreeRTOS.h> -#include <semphr.h> - -#include "modules.h" -#include "epicardium.h" - -#ifndef EPIC_FAT_STATIC_SEMAPHORE -#define EPIC_FAT_STATIC_SEMAPHORE 0 -#endif - -static const TCHAR *rcstrings = - _T("OK\0DISK_ERR\0INT_ERR\0NOT_READY\0NO_FILE\0NO_PATH\0INVALID_NAME\0") - _T("DENIED\0EXIST\0INVALID_OBJECT\0WRITE_PROTECTED\0INVALID_DRIVE\0") - _T("NOT_ENABLED\0NO_FILESYSTEM\0MKFS_ABORTED\0TIMEOUT\0LOCKED\0") - _T("NOT_ENOUGH_CORE\0TOO_MANY_OPEN_FILES\0INVALID_PARAMETER\0"); - -static bool mount(void); - -DIR dir; -FATFS FatFs; - -#if (EPIC_FAT_STATIC_SEMAPHORE == 1) -StaticSemaphore_t xSemaphoreBuffer; -#endif - -static volatile bool s_fatfs_initiaized = false; - -void fatfs_init() -{ - if (mount()) { - s_fatfs_initiaized = true; - printf("FatFs mounted\n"); - } -} - -const char *f_get_rc_string(FRESULT rc) -{ - FRESULT i; - const char *p = rcstrings; - - for (i = 0; i != rc && *p; i++) { - while (*p++) - ; - } - return p; -} - -static bool mount() -{ - FRESULT res; - res = f_mount(&FatFs, "/", 0); - if (res != FR_OK) { - printf("f_mount error %s\n", f_get_rc_string(res)); - return false; - } - - res = f_opendir(&dir, "0:"); - if (res != FR_OK) { - printf("f_opendir error %s\n", f_get_rc_string(res)); - return false; - } - - return true; -} - -/*------------------------------------------------------------------------*/ -/* Create a Synchronization Object */ -/*------------------------------------------------------------------------*/ -/* This function is called in f_mount() function to create a new -/ synchronization object for the volume, such as semaphore and mutex. -/ When a 0 is returned, the f_mount() function fails with FR_INT_ERR. -*/ - -/* - * Return value: - * - 1: Function succeeded - * - 0: Could not create the sync object - */ -int ff_cre_syncobj(BYTE vol, FF_SYNC_t *sobj) -{ -#if (EPIC_FAT_STATIC_SEMAPHORE == 1) - *sobj = xSemaphoreCreateMutexStatic(&xSemaphoreBuffer); -#else - *sobj = xSemaphoreCreateMutex(); -#endif //EPIC_FAT_STATIC_SEMAPHORE - - return (int)(*sobj != NULL); -} - -/*------------------------------------------------------------------------*/ -/* Delete a Synchronization Object */ -/*------------------------------------------------------------------------*/ -/* This function is called in f_mount() function to delete a synchronization -/ object that created with ff_cre_syncobj() function. When a 0 is returned, -/ the f_mount() function fails with FR_INT_ERR. -*/ - -/* - * Return value: - * - 1: Function succeeded - * - 0: Could not delete due to an error - */ -int ff_del_syncobj(FF_SYNC_t sobj) -{ - /* FreeRTOS */ - vSemaphoreDelete(sobj); - return 1; -} - -/*------------------------------------------------------------------------*/ -/* Request Grant to Access the Volume */ -/*------------------------------------------------------------------------*/ -/* This function is called on entering file functions to lock the volume. -/ When a 0 is returned, the file function fails with FR_TIMEOUT. -*/ - -/* - * Return value: - * - 1: Got a grant to access the volume - * - 0: Could not get a grant - */ -int ff_req_grant(FF_SYNC_t sobj) -{ - /* FreeRTOS */ - return (int)(xSemaphoreTake(sobj, FF_FS_TIMEOUT) == pdTRUE); -} - -/*------------------------------------------------------------------------*/ -/* Release Grant to Access the Volume */ -/*------------------------------------------------------------------------*/ -/* This function is called on leaving file functions to unlock the volume. -*/ - -void ff_rel_grant(FF_SYNC_t sobj) -{ - /* FreeRTOS */ - xSemaphoreGive(sobj); -} diff --git a/epicardium/modules/fatfs_fileops.c b/epicardium/modules/fatfs_fileops.c deleted file mode 100644 index de083fcb..00000000 --- a/epicardium/modules/fatfs_fileops.c +++ /dev/null @@ -1,300 +0,0 @@ -#include <errno.h> -#include <stddef.h> -#include <stdbool.h> -#include <stdio.h> - -#include <ff.h> - -#include "modules.h" -#include "epicardium.h" - -#define EPIC_FAT_FD_GENERATION_BITS (31 - (EPIC_FAT_FD_INDEX_BITS)) -#define EPIC_FAT_MAX_OPENED (1 << (EPIC_FAT_FD_INDEX_BITS)) -#define EPIC_FAT_FD_INDEX_MASK (uint32_t)((1u << EPIC_FAT_FD_INDEX_BITS) - 1) -#define EPIC_FAT_FD_INDEX(fd) ((uint32_t)(fd)&EPIC_FAT_FD_INDEX_MASK) -#define EPIC_FAT_FD_GENERATION(fd) ((uint32_t)(fd) >> EPIC_FAT_FD_INDEX_BITS) -#define EPIC_FAT_FD_MAX_GENERATION \ - (uint32_t)((1u << EPIC_FAT_FD_GENERATION_BITS) - 1) -#define EPIC_FAT_FD(idx, gen) \ - (int)(((uint32_t)(gen) << EPIC_FAT_FD_INDEX_BITS) | \ - ((uint32_t)(idx)&EPIC_FAT_FD_INDEX_MASK)) - -// this table converts from FRESULT to POSIX errno -const int fresult_to_errno_table[20] = { - [FR_OK] = 0, - [FR_DISK_ERR] = EIO, - [FR_INT_ERR] = EIO, - [FR_NOT_READY] = EBUSY, - [FR_NO_FILE] = ENOENT, - [FR_NO_PATH] = ENOENT, - [FR_INVALID_NAME] = EINVAL, - [FR_DENIED] = EACCES, - [FR_EXIST] = EEXIST, - [FR_INVALID_OBJECT] = EINVAL, - [FR_WRITE_PROTECTED] = EROFS, - [FR_INVALID_DRIVE] = ENODEV, - [FR_NOT_ENABLED] = ENODEV, - [FR_NO_FILESYSTEM] = ENODEV, - [FR_MKFS_ABORTED] = EIO, - [FR_TIMEOUT] = EIO, - [FR_LOCKED] = EIO, - [FR_NOT_ENOUGH_CORE] = ENOMEM, - [FR_TOO_MANY_OPEN_FILES] = EMFILE, - [FR_INVALID_PARAMETER] = EINVAL, -}; - -struct FatObject { - uint32_t generation; - enum epic_stat_type type; - union { - FIL file; - DIR dir; - }; -}; - -static int -get_fat_object(int i, enum epic_stat_type expected, struct FatObject **res); - -static struct FatObject s_openedObjects[EPIC_FAT_MAX_OPENED]; -static uint32_t s_fatfs_generationCount = 1; - -int get_fat_object(int fd, enum epic_stat_type expected, struct FatObject **res) -{ - uint32_t index = EPIC_FAT_FD_INDEX(fd); - uint32_t generation = EPIC_FAT_FD_GENERATION(fd); - if (index >= EPIC_FAT_MAX_OPENED) { - *res = NULL; - return EBADF; - } - if (generation >= EPIC_FAT_FD_MAX_GENERATION) { - *res = NULL; - return EBADF; - } - if (s_openedObjects[index].type != expected) { - *res = NULL; - return EBADF; - } - if (s_openedObjects[index].generation != generation) { - *res = NULL; - return EBADF; - } - *res = &s_openedObjects[index]; - return 0; -} - -/* here we're trying to mirror glibc's behaviour: - * any combination of rwax parses but only the first of those flags wins: - * - rw, ra, rr all open read-only - * a `+` at any position but the first turns into read-write - * any other character at any position yields EINVAL - */ -static inline bool parse_mode(const char *mstring, int *mode) -{ - switch (mstring[0]) { - case 'r': - *mode = FA_READ; - break; - case 'w': - *mode = FA_CREATE_ALWAYS | FA_WRITE; - break; - case 'x': - //in constrast to FA_CREATE_ALWAYS, FA_CREATE_NEW fails for existing files - *mode = FA_WRITE | FA_CREATE_NEW; - break; - case 'a': - //in constrast to FA_CREATE_ALWAYS, FA_CREATE_NEW fails for existing files - *mode = FA_WRITE | FA_OPEN_APPEND; - break; - default: - return false; - } - while (*mstring) { - switch (*mstring++) { - case '+': //turns any of r,w,x into read&write - *mode |= FA_READ | FA_WRITE; - break; - case 'r': //fallthrough intentional - case 'w': //fallthrough intentional - case 'a': //fallthrough intentional - case 'x': //fallthrough intentional - case 'b': //fallthrough intentional - break; - default: - return false; - } - } - return true; -} - -int epic_file_open(const char *filename, const char *modeString) -{ - struct FatObject *o = NULL; - uint32_t index, generation; - int mode = 0; - int res; - - //find free object to use - for (index = 0; index < EPIC_FAT_MAX_OPENED; ++index) { - if (s_openedObjects[index].type == EPICSTAT_NONE) { - break; - } - } - if (index == EPIC_FAT_MAX_OPENED) { - return -fresult_to_errno_table[FR_TOO_MANY_OPEN_FILES]; - } - generation = s_fatfs_generationCount++; - if (generation == EPIC_FAT_FD_MAX_GENERATION) { - s_fatfs_generationCount = 1; - } - o = &s_openedObjects[index]; - - if (!parse_mode(modeString, &mode)) { - return -EINVAL; - } - - res = f_open(&o->file, filename, mode); - if (res != FR_OK) { - return -fresult_to_errno_table[res]; - } - o->type = EPICSTAT_FILE; - o->generation = generation; - - // for 'a' mode, we must begin at the end of the file - if ((mode & FA_OPEN_APPEND) != 0) { - f_lseek(&o->file, f_size(&o->file)); - } - - return EPIC_FAT_FD(index, generation); -} - -int epic_file_close(int fd) -{ - int res; - struct FatObject *o; - res = get_fat_object(fd, EPICSTAT_FILE, &o); - if (res) { - return -res; - } - - res = f_close(&o->file); - if (res != FR_OK) { - return -fresult_to_errno_table[res]; - } - - o->type = EPICSTAT_NONE; - o->generation = 0; - return 0; -} - -int epic_file_read(int fd, void *buf, size_t nbytes) -{ - unsigned int nread = 0; - - int res; - struct FatObject *o; - res = get_fat_object(fd, EPICSTAT_FILE, &o); - if (res) { - return -res; - } - - res = f_read(&o->file, buf, nbytes, &nread); - if (res != FR_OK) { - return -fresult_to_errno_table[res]; - } - - return (int)nread; -} - -int epic_file_write(int fd, const void *buf, size_t nbytes) -{ - unsigned int nwritten = 0; - - int res; - struct FatObject *o; - res = get_fat_object(fd, EPICSTAT_FILE, &o); - if (res) { - return -res; - } - res = f_write(&o->file, buf, nbytes, &nwritten); - if (res != FR_OK) { - return -fresult_to_errno_table[res]; - } - - return (int)nwritten; -} - -int epic_file_flush(int fd) -{ - int res; - struct FatObject *o; - res = get_fat_object(fd, EPICSTAT_FILE, &o); - if (res) { - return -res; - } - res = f_sync(&o->file); - if (res != FR_OK) { - return -fresult_to_errno_table[res]; - } - - return 0; -} - -int epic_file_seek(int fd, long offset, int whence) -{ - int res; - struct FatObject *o; - res = get_fat_object(fd, EPICSTAT_FILE, &o); - if (res) { - return -res; - } - switch (whence) { - case SEEK_SET: - res = f_lseek(&o->file, offset); - break; - - case SEEK_CUR: - res = f_lseek(&o->file, f_tell(&o->file) + offset); - break; - - case SEEK_END: - res = f_lseek(&o->file, f_size(&o->file) + offset); - break; - default: - return -EINVAL; - } - if (res != FR_OK) { - return -fresult_to_errno_table[res]; - } - - return 0; -} - -int epic_file_tell(int fd) -{ - int res; - struct FatObject *o; - res = get_fat_object(fd, EPICSTAT_FILE, &o); - if (res) { - return -res; - } - //f_tell simply accesses fp->fptr so no errors are expected - return directly - return f_tell(&o->file); -} - -int epic_file_stat(const char *filename, struct epic_stat *stat) -{ - int res; - FILINFO finfo; - res = f_stat(filename, &finfo); - if (res != FR_OK) { - return -fresult_to_errno_table[res]; - } - - if (finfo.fattrib & AM_DIR) { - stat->type = EPICSTAT_DIR; - } else { - stat->type = EPICSTAT_FILE; - } - - return 0; -} diff --git a/epicardium/modules/fileops.c b/epicardium/modules/fileops.c new file mode 100644 index 00000000..fc13417b --- /dev/null +++ b/epicardium/modules/fileops.c @@ -0,0 +1,97 @@ +/** + * Implemention of the epicardium epic_file_ api that operates on + * the global EpicFileSystem instance + * + * All functions lock & unlock the global FS object + * + */ + +#include "fs/internal.h" + +int epic_file_open(const char *filename, const char *mode) +{ + EpicFileSystem *fs; + int res = efs_lock_global(&fs); + if (res == 0) { + res = efs_open(fs, filename, mode); + efs_unlock_global(fs); + } + return res; +} + +int epic_file_close(int fd) +{ + EpicFileSystem *fs; + int res = efs_lock_global(&fs); + if (res == 0) { + res = efs_close(fs, fd); + efs_unlock_global(fs); + } + return res; +} + +int epic_file_read(int fd, void *buf, size_t nbytes) +{ + EpicFileSystem *fs; + int res = efs_lock_global(&fs); + if (res == 0) { + res = efs_read(fs, fd, buf, nbytes); + efs_unlock_global(fs); + } + return res; +} + +int epic_file_write(int fd, const void *buf, size_t nbytes) +{ + EpicFileSystem *fs; + int res = efs_lock_global(&fs); + if (res == 0) { + res = efs_write(fs, fd, buf, nbytes); + efs_unlock_global(fs); + } + return res; +} + +int epic_file_flush(int fd) +{ + EpicFileSystem *fs; + int res = efs_lock_global(&fs); + if (res == 0) { + res = efs_flush(fs, fd); + efs_unlock_global(fs); + } + return res; +} + +int epic_file_seek(int fd, long offset, int whence) +{ + EpicFileSystem *fs; + int res = efs_lock_global(&fs); + if (res == 0) { + res = efs_seek(fs, fd, offset, whence); + efs_unlock_global(fs); + } + return res; +} + +int epic_file_tell(int fd) +{ + EpicFileSystem *fs; + int res = efs_lock_global(&fs); + if (res == 0) { + res = efs_tell(fs, fd); + efs_unlock_global(fs); + } + return res; +} + +int epic_file_stat(const char *filename, struct epic_stat *stat) +{ + EpicFileSystem *fs; + int res = efs_lock_global(&fs); + if (res == 0) { + res = efs_stat(fs, filename, stat); + efs_unlock_global(fs); + } + return res; +} diff --git a/epicardium/modules/filesystem.h b/epicardium/modules/filesystem.h new file mode 100644 index 00000000..7a638d5a --- /dev/null +++ b/epicardium/modules/filesystem.h @@ -0,0 +1,25 @@ +#ifndef EPICARDIUM_MODULE_FILESYSTEM_INCLUDED +#define EPICARDIUM_MODULE_FILESYSTEM_INCLUDED + +/* ---------- FAT fs ------------------------------------------------------ */ + +#include <stdbool.h> +#include "epicardium.h" + +/** + * module initialization - to be called once at startup before any FreeRTOS tasks + * have been started + * + * calls fatfs_attach + */ +void fatfs_init(void); + +/** + * initialize and mount the FLASH storage + */ +int fatfs_attach(void); + +/** close all opened FDs, sync and deinitialize FLASH layer */ +void fatfs_detach(void); + +#endif//EPICARDIUM_MODULE_FILESYSTEM_INCLUDED diff --git a/epicardium/modules/meson.build b/epicardium/modules/meson.build index 35b6e819..b3cf4eb2 100644 --- a/epicardium/modules/meson.build +++ b/epicardium/modules/meson.build @@ -1,7 +1,6 @@ module_sources = files( 'display.c', - 'fatfs.c', - 'fatfs_fileops.c', + 'fileops.c', 'leds.c', 'light_sensor.c', 'log.c', diff --git a/epicardium/modules/modules.h b/epicardium/modules/modules.h index 5dbda13c..4a7bc255 100644 --- a/epicardium/modules/modules.h +++ b/epicardium/modules/modules.h @@ -2,15 +2,6 @@ #define MODULES_H #include <stdint.h> -/* ---------- FAT fs ------------------------------------------------------ */ -/* Number of bits to use for indexing into our internal pool of files/directories - * This indirectly specifies the size of the pool as 2^EPIC_FAT_FD_INDEX_BITS - * Increase if number of open file descriptors is not enough, but be aware of - * memory usage of the pool! - */ -#define EPIC_FAT_FD_INDEX_BITS 4 -#define EPIC_FAT_STATIC_SEMAPHORE 1 -void fatfs_init(void); /* ---------- Serial ------------------------------------------------------- */ #define SERIAL_READ_BUFFER_SIZE 128 diff --git a/lib/ff13/Source/diskio.c b/lib/ff13/Source/diskio.c index 69458836..6b90b705 100644 --- a/lib/ff13/Source/diskio.c +++ b/lib/ff13/Source/diskio.c @@ -8,6 +8,7 @@ /*-----------------------------------------------------------------------*/ #include "diskio.h" /* FatFs lower layer API */ +#include <stdbool.h> /* Definitions of physical drive number for each drive */ #define DEV_FLASH 0 /* Example: Map MMC/SD card to physical drive 1 */ @@ -19,6 +20,7 @@ /*local vaiables*/ static uint8_t rtc_en; +static bool s_diskio_initialized = false; #if SDHC /* # of times to check for a card, should be > 1 to detect both SD and MMC */ @@ -57,12 +59,14 @@ DSTATUS disk_status ( #define STA_PROTECT 0x04 /* Write protected */ #endif - DSTATUS status = 0; - if(pdrv == 0) { - if(mx25_ready()) { - status = RES_OK; - } - } + DSTATUS status = 0; + if (!s_diskio_initialized) { + status = STA_NOINIT | STA_NODISK; + } else if (pdrv == 0) { + if (mx25_ready()) { + status = RES_OK; + } + } #if SDHC if(pdrv == 1) { @@ -87,7 +91,8 @@ DSTATUS disk_initialize ( { DSTATUS status = STA_NOINIT; - rtc_en = 0; + rtc_en = 0; + s_diskio_initialized = true; #if (FF_FS_NORTC == 0) //Initialize RTC if (MXC_RTC->cn & MXC_F_RTC_CN_WE) { @@ -123,7 +128,14 @@ DSTATUS disk_initialize ( return status; } - +void disk_deinitialize() +{ + if (s_diskio_initialized) { + mx25_sync(); + mx25_stop(); //XXX: or should we not? + s_diskio_initialized = false; + } +} /*-----------------------------------------------------------------------*/ /* Read Sector(s) */ @@ -138,7 +150,9 @@ DRESULT disk_read ( { DRESULT status = RES_ERROR; - if(pdrv == 0) { + if (!s_diskio_initialized) { + status = STA_NOINIT | STA_NODISK; + } else if (pdrv == 0) { int sector_offset; status = RES_OK; for(sector_offset = 0; sector_offset < count; sector_offset++) { @@ -174,7 +188,9 @@ DRESULT disk_write ( { DRESULT status = RES_ERROR; - if(pdrv == 0) { + if (!s_diskio_initialized) { + status = STA_NOINIT | STA_NODISK; + } else if (pdrv == 0) { int sector_offset; status = RES_OK; for(sector_offset = 0; sector_offset < count; sector_offset++) { @@ -212,7 +228,9 @@ DRESULT disk_ioctl ( { DRESULT status = RES_PARERR; - if(pdrv == 0) { + if (!s_diskio_initialized) { + status = STA_NOINIT | STA_NODISK; + } else if (pdrv == 0) { switch(cmd) { case CTRL_SYNC: /* Mandatory */ diff --git a/lib/ff13/Source/diskio.h b/lib/ff13/Source/diskio.h index 40551fbd..5dc221e2 100644 --- a/lib/ff13/Source/diskio.h +++ b/lib/ff13/Source/diskio.h @@ -34,6 +34,7 @@ typedef enum { /* Prototypes for disk control functions */ DSTATUS disk_initialize (BYTE pdrv); +void disk_deinitialize(void); DSTATUS disk_status (BYTE pdrv); DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count); DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count); diff --git a/lib/ff13/Source/ffconf.h b/lib/ff13/Source/ffconf.h index 3a14f3dc..f4b54f33 100644 --- a/lib/ff13/Source/ffconf.h +++ b/lib/ff13/Source/ffconf.h @@ -248,10 +248,10 @@ / lock control is independent of re-entrancy. */ -#define FF_FS_REENTRANT 1 +#define FF_FS_REENTRANT 0 #define FF_FS_TIMEOUT 1000 //in FreeRTOS, SemaphoreHandle_t is a typedef for QueueHandle_t, which is just a typedef for: -#define FF_SYNC_t struct QueueDefinition * +//#define FF_SYNC_t struct QueueDefinition * /* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs / module itself. Note that regardless of this option, file access to different diff --git a/pycardium/mpconfigport.h b/pycardium/mpconfigport.h index 3230d432..38934682 100644 --- a/pycardium/mpconfigport.h +++ b/pycardium/mpconfigport.h @@ -36,6 +36,7 @@ #define MICROPY_PY_URE_SUB (1) #define MICROPY_PY_UTIME_MP_HAL (1) #define MICROPY_PY_IO_FILEIO (1) +#define MICROPY_PY_UERRNO (1) /* Modules */ #define MODULE_DISPLAY_ENABLED (1) -- GitLab