diff --git a/components/fatfs/src/diskio.c b/components/fatfs/src/diskio.c index 004cfc5be..c61702f51 100644 --- a/components/fatfs/src/diskio.c +++ b/components/fatfs/src/diskio.c @@ -18,39 +18,71 @@ #include static const char* TAG = "ff_diskio"; -static ff_diskio_impl_t s_impls[_VOLUMES] = { { 0 } }; +static ff_diskio_impl_t * s_impls[_VOLUMES]; static sdmmc_card_t* s_cards[_VOLUMES] = { NULL }; +static bool s_impls_initialized = false; PARTITION VolToPart[] = { {0, 1}, /* Logical drive 0 ==> Physical drive 0, 1st partition */ {1, 0} /* Logical drive 1 ==> Physical drive 1, auto detection */ }; +esp_err_t ff_diskio_get_drive(BYTE* out_pdrv) +{ + BYTE i; + for(i=0; i<_VOLUMES; i++) { + if (!s_impls[i]) { + *out_pdrv = i; + return ESP_OK; + } + } + return ESP_ERR_NOT_FOUND; +} + void ff_diskio_register(BYTE pdrv, const ff_diskio_impl_t* discio_impl) { assert(pdrv < _VOLUMES); - memcpy(&s_impls[pdrv], discio_impl, sizeof(ff_diskio_impl_t)); + + if (!s_impls_initialized) { + s_impls_initialized = true; + memset(s_impls, 0, _VOLUMES * sizeof(ff_diskio_impl_t*)); + } + + if (s_impls[pdrv]) { + ff_diskio_impl_t* im = s_impls[pdrv]; + s_impls[pdrv] = NULL; + free(im); + } + + if (!discio_impl) { + return; + } + + ff_diskio_impl_t * impl = (ff_diskio_impl_t *)malloc(sizeof(ff_diskio_impl_t)); + assert(impl != NULL); + memcpy(impl, discio_impl, sizeof(ff_diskio_impl_t)); + s_impls[pdrv] = impl; } DSTATUS ff_disk_initialize (BYTE pdrv) { - return s_impls[pdrv].init(pdrv); + return s_impls[pdrv]->init(pdrv); } DSTATUS ff_disk_status (BYTE pdrv) { - return s_impls[pdrv].status(pdrv); + return s_impls[pdrv]->status(pdrv); } DRESULT ff_disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count) { - return s_impls[pdrv].read(pdrv, buff, sector, count); + return s_impls[pdrv]->read(pdrv, buff, sector, count); } DRESULT ff_disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count) { - return s_impls[pdrv].write(pdrv, buff, sector, count); + return s_impls[pdrv]->write(pdrv, buff, sector, count); } DRESULT ff_disk_ioctl (BYTE pdrv, BYTE cmd, void* buff) { - return s_impls[pdrv].ioctl(pdrv, cmd, buff); + return s_impls[pdrv]->ioctl(pdrv, cmd, buff); } DWORD get_fattime(void) diff --git a/components/fatfs/src/diskio.h b/components/fatfs/src/diskio.h index 7c224809f..64d5d5b8d 100644 --- a/components/fatfs/src/diskio.h +++ b/components/fatfs/src/diskio.h @@ -58,16 +58,19 @@ typedef struct { } ff_diskio_impl_t; /** - * Register diskio driver for given drive number. + * Register or unregister diskio driver for given drive number. * * When FATFS library calls one of disk_xxx functions for driver number pdrv, * corresponding function in discio_impl for given pdrv will be called. * * @param pdrv drive number - * @param discio_impl pointer to ff_diskio_impl_t structure with diskio functions + * @param discio_impl pointer to ff_diskio_impl_t structure with diskio functions + * or NULL to unregister and free previously registered drive */ void ff_diskio_register(BYTE pdrv, const ff_diskio_impl_t* discio_impl); +#define ff_diskio_unregister(pdrv_) ff_diskio_register(pdrv_, NULL) + /** * Register SD/MMC diskio driver * @@ -76,6 +79,16 @@ void ff_diskio_register(BYTE pdrv, const ff_diskio_impl_t* discio_impl); */ void ff_diskio_register_sdmmc(BYTE pdrv, sdmmc_card_t* card); +/** + * Get next available drive number + * + * @param out_pdrv pointer to the byte to set if successful + * + * @return ESP_OK on success + * ESP_ERR_NOT_FOUND if all drives are attached + */ +esp_err_t ff_diskio_get_drive(BYTE* out_pdrv); + /* Disk Status Bits (DSTATUS) */ #define STA_NOINIT 0x01 /* Drive not initialized */ diff --git a/components/fatfs/src/esp_vfs_fat.h b/components/fatfs/src/esp_vfs_fat.h index 087d6cf77..4e27dce69 100644 --- a/components/fatfs/src/esp_vfs_fat.h +++ b/components/fatfs/src/esp_vfs_fat.h @@ -51,11 +51,31 @@ esp_err_t esp_vfs_fat_register(const char* base_path, const char* fat_drive, * @note FATFS structure returned by esp_vfs_fat_register is destroyed after * this call. Make sure to call f_mount function to unmount it before * calling esp_vfs_fat_unregister. + * This function is left for compatibility and will be changed in + * future versions to accept base_path and replace the method below * @return * - ESP_OK on success * - ESP_ERR_INVALID_STATE if FATFS is not registered in VFS */ -esp_err_t esp_vfs_fat_unregister(); +esp_err_t esp_vfs_fat_unregister() __attribute__((deprecated)); + +/** + * @brief Un-register FATFS from VFS + * + * @note FATFS structure returned by esp_vfs_fat_register is destroyed after + * this call. Make sure to call f_mount function to unmount it before + * calling esp_vfs_fat_unregister_ctx. + * Difference between this function and the one above is that this one + * will release the correct drive, while the one above will release + * the last registered one + * + * @param base_path path prefix where FATFS is registered. This is the same + * used when esp_vfs_fat_register was called + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if FATFS is not registered in VFS + */ +esp_err_t esp_vfs_fat_unregister_path(const char* base_path); /** * @brief Configuration arguments for esp_vfs_fat_sdmmc_mount function diff --git a/components/fatfs/src/vfs_fat.c b/components/fatfs/src/vfs_fat.c index 73bed5eaa..b9f5e40aa 100644 --- a/components/fatfs/src/vfs_fat.c +++ b/components/fatfs/src/vfs_fat.c @@ -28,6 +28,7 @@ typedef struct { char fat_drive[8]; + char base_path[ESP_VFS_PATH_MAX]; size_t max_files; FATFS fs; FIL files[0]; @@ -42,7 +43,6 @@ typedef struct { struct dirent cur_dirent; } vfs_fat_dir_t; - static const char* TAG = "vfs_fat"; static size_t vfs_fat_write(void* p, int fd, const void * data, size_t size); @@ -64,15 +64,42 @@ static int vfs_fat_closedir(void* ctx, DIR* pdir); static int vfs_fat_mkdir(void* ctx, const char* name, mode_t mode); static int vfs_fat_rmdir(void* ctx, const char* name); - -static char s_base_path[ESP_VFS_PATH_MAX]; +static vfs_fat_ctx_t* s_fat_ctxs[_VOLUMES] = { NULL, NULL }; +//backwards-compatibility with esp_vfs_fat_unregister() static vfs_fat_ctx_t* s_fat_ctx = NULL; +static size_t find_context_index_by_path(const char* base_path) +{ + for(size_t i=0; i<_VOLUMES; i++) { + if (s_fat_ctxs[i] && !strcmp(s_fat_ctxs[i]->base_path, base_path)) { + return i; + } + } + return _VOLUMES; +} + +static size_t find_unused_context_index() +{ + for(size_t i=0; i<_VOLUMES; i++) { + if (!s_fat_ctxs[i]) { + return i; + } + } + return _VOLUMES; +} + esp_err_t esp_vfs_fat_register(const char* base_path, const char* fat_drive, size_t max_files, FATFS** out_fs) { - if (s_fat_ctx) { + size_t ctx = find_context_index_by_path(base_path); + if (ctx < _VOLUMES) { return ESP_ERR_INVALID_STATE; } + + ctx = find_unused_context_index(); + if (ctx == _VOLUMES) { + return ESP_ERR_NO_MEM; + } + const esp_vfs_t vfs = { .flags = ESP_VFS_FLAG_CONTEXT_PTR, .write_p = &vfs_fat_write, @@ -95,22 +122,45 @@ esp_err_t esp_vfs_fat_register(const char* base_path, const char* fat_drive, siz .rmdir_p = &vfs_fat_rmdir }; size_t ctx_size = sizeof(vfs_fat_ctx_t) + max_files * sizeof(FIL); - s_fat_ctx = (vfs_fat_ctx_t*) calloc(1, ctx_size); - if (s_fat_ctx == NULL) { + vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) calloc(1, ctx_size); + if (fat_ctx == NULL) { return ESP_ERR_NO_MEM; } - s_fat_ctx->max_files = max_files; - strncpy(s_fat_ctx->fat_drive, fat_drive, sizeof(s_fat_ctx->fat_drive) - 1); - *out_fs = &s_fat_ctx->fs; - esp_err_t err = esp_vfs_register(base_path, &vfs, s_fat_ctx); + fat_ctx->max_files = max_files; + strlcpy(fat_ctx->fat_drive, fat_drive, sizeof(fat_ctx->fat_drive) - 1); + strlcpy(fat_ctx->base_path, base_path, sizeof(fat_ctx->base_path) - 1); + + esp_err_t err = esp_vfs_register(base_path, &vfs, fat_ctx); if (err != ESP_OK) { - free(s_fat_ctx); - s_fat_ctx = NULL; + free(fat_ctx); return err; } - _lock_init(&s_fat_ctx->lock); - strncpy(s_base_path, base_path, sizeof(s_base_path) - 1); - s_base_path[sizeof(s_base_path) - 1] = 0; + + _lock_init(&fat_ctx->lock); + s_fat_ctxs[ctx] = fat_ctx; + + //compatibility + s_fat_ctx = fat_ctx; + + *out_fs = &fat_ctx->fs; + + return ESP_OK; +} + +esp_err_t esp_vfs_fat_unregister_path(const char* base_path) +{ + size_t ctx = find_context_index_by_path(base_path); + if (ctx == _VOLUMES) { + return ESP_ERR_INVALID_STATE; + } + vfs_fat_ctx_t* fat_ctx = s_fat_ctxs[ctx]; + esp_err_t err = esp_vfs_unregister(fat_ctx->base_path); + if (err != ESP_OK) { + return err; + } + _lock_close(&fat_ctx->lock); + free(fat_ctx); + s_fat_ctxs[ctx] = NULL; return ESP_OK; } @@ -119,12 +169,10 @@ esp_err_t esp_vfs_fat_unregister() if (s_fat_ctx == NULL) { return ESP_ERR_INVALID_STATE; } - esp_err_t err = esp_vfs_unregister(s_base_path); + esp_err_t err = esp_vfs_fat_unregister_path(s_fat_ctx->base_path); if (err != ESP_OK) { return err; } - _lock_close(&s_fat_ctx->lock); - free(s_fat_ctx); s_fat_ctx = NULL; return ESP_OK; } @@ -197,11 +245,23 @@ static void file_cleanup(vfs_fat_ctx_t* ctx, int fd) memset(&ctx->files[fd], 0, sizeof(FIL)); } +static void prepend_drive_to_path(void * ctx, const char * path, const char * path2){ + static char buf[FILENAME_MAX+3]; + static char buf2[FILENAME_MAX+3]; + sprintf(buf, "%s%s", ((vfs_fat_ctx_t*)ctx)->fat_drive, path); + path = (const char *)buf; + if(path2){ + sprintf(buf2, "%s%s", ((vfs_fat_ctx_t*)ctx)->fat_drive, path2); + path2 = (const char *)buf; + } +} + static int vfs_fat_open(void* ctx, const char * path, int flags, int mode) { + prepend_drive_to_path(ctx, path, NULL); ESP_LOGV(TAG, "%s: path=\"%s\", flags=%x, mode=%x", __func__, path, flags, mode); vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx; - _lock_acquire(&s_fat_ctx->lock); + _lock_acquire(&fat_ctx->lock); int fd = get_next_fd(fat_ctx); if (fd < 0) { ESP_LOGE(TAG, "open: no free file descriptors"); @@ -218,7 +278,7 @@ static int vfs_fat_open(void* ctx, const char * path, int flags, int mode) goto out; } out: - _lock_release(&s_fat_ctx->lock); + _lock_release(&fat_ctx->lock); return fd; } @@ -257,7 +317,7 @@ static ssize_t vfs_fat_read(void* ctx, int fd, void * dst, size_t size) static int vfs_fat_close(void* ctx, int fd) { vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx; - _lock_acquire(&s_fat_ctx->lock); + _lock_acquire(&fat_ctx->lock); FIL* file = &fat_ctx->files[fd]; FRESULT res = f_close(file); file_cleanup(fat_ctx, fd); @@ -267,7 +327,7 @@ static int vfs_fat_close(void* ctx, int fd) errno = fresult_to_errno(res); rc = -1; } - _lock_release(&s_fat_ctx->lock); + _lock_release(&fat_ctx->lock); return rc; } @@ -308,6 +368,7 @@ static int vfs_fat_fstat(void* ctx, int fd, struct stat * st) static int vfs_fat_stat(void* ctx, const char * path, struct stat * st) { + prepend_drive_to_path(ctx, path, NULL); FILINFO info; FRESULT res = f_stat(path, &info); if (res != FR_OK) { @@ -337,6 +398,7 @@ static int vfs_fat_stat(void* ctx, const char * path, struct stat * st) static int vfs_fat_unlink(void* ctx, const char *path) { + prepend_drive_to_path(ctx, path, NULL); FRESULT res = f_unlink(path); if (res != FR_OK) { ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); @@ -348,6 +410,7 @@ static int vfs_fat_unlink(void* ctx, const char *path) static int vfs_fat_link(void* ctx, const char* n1, const char* n2) { + prepend_drive_to_path(ctx, n1, n2); const size_t copy_buf_size = 4096; void* buf = malloc(copy_buf_size); if (buf == NULL) { @@ -402,6 +465,7 @@ fail1: static int vfs_fat_rename(void* ctx, const char *src, const char *dst) { + prepend_drive_to_path(ctx, src, dst); FRESULT res = f_rename(src, dst); if (res != FR_OK) { ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); @@ -413,6 +477,7 @@ static int vfs_fat_rename(void* ctx, const char *src, const char *dst) static DIR* vfs_fat_opendir(void* ctx, const char* name) { + prepend_drive_to_path(ctx, name, NULL); vfs_fat_dir_t* fat_dir = calloc(1, sizeof(vfs_fat_dir_t)); if (!fat_dir) { errno = ENOMEM; @@ -517,6 +582,7 @@ static void vfs_fat_seekdir(void* ctx, DIR* pdir, long offset) static int vfs_fat_mkdir(void* ctx, const char* name, mode_t mode) { (void) mode; + prepend_drive_to_path(ctx, name, NULL); FRESULT res = f_mkdir(name); if (res != FR_OK) { ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); @@ -528,6 +594,7 @@ static int vfs_fat_mkdir(void* ctx, const char* name, mode_t mode) static int vfs_fat_rmdir(void* ctx, const char* name) { + prepend_drive_to_path(ctx, name, NULL); FRESULT res = f_unlink(name); if (res != FR_OK) { ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); diff --git a/components/fatfs/src/vfs_fat_sdmmc.c b/components/fatfs/src/vfs_fat_sdmmc.c index 77620f5d8..a9e5362dc 100644 --- a/components/fatfs/src/vfs_fat_sdmmc.c +++ b/components/fatfs/src/vfs_fat_sdmmc.c @@ -13,6 +13,7 @@ // limitations under the License. #include +#include #include "esp_log.h" #include "esp_vfs.h" #include "esp_vfs_fat.h" @@ -22,6 +23,8 @@ static const char* TAG = "vfs_fat_sdmmc"; static sdmmc_card_t* s_card = NULL; +static uint8_t s_pdrv = 0; +static char * s_base_path = NULL; esp_err_t esp_vfs_fat_sdmmc_mount(const char* base_path, const sdmmc_host_t* host_config, @@ -35,6 +38,20 @@ esp_err_t esp_vfs_fat_sdmmc_mount(const char* base_path, if (s_card != NULL) { return ESP_ERR_INVALID_STATE; } + + // connect SDMMC driver to FATFS + BYTE pdrv = 0xFF; + if (ff_diskio_get_drive(&pdrv) != ESP_OK || pdrv == 0xFF) { + ESP_LOGD(TAG, "the maximum count of volumes is already mounted"); + return ESP_ERR_NO_MEM; + } + + s_base_path = strdup(base_path); + if(!s_base_path){ + ESP_LOGD(TAG, "could not copy base_path"); + return ESP_ERR_NO_MEM; + } + // enable SDMMC sdmmc_host_init(); @@ -60,12 +77,13 @@ esp_err_t esp_vfs_fat_sdmmc_mount(const char* base_path, *out_card = s_card; } - // connect SDMMC driver to FATFS - ff_diskio_register_sdmmc(0, s_card); + ff_diskio_register_sdmmc(pdrv, s_card); + s_pdrv = pdrv; + char drv[3] = {(char)('0' + pdrv), ':', 0}; // connect FATFS to VFS FATFS* fs; - err = esp_vfs_fat_register(base_path, "", mount_config->max_files, &fs); + err = esp_vfs_fat_register(base_path, drv, mount_config->max_files, &fs); if (err == ESP_ERR_INVALID_STATE) { // it's okay, already registered with VFS } else if (err != ESP_OK) { @@ -74,7 +92,7 @@ esp_err_t esp_vfs_fat_sdmmc_mount(const char* base_path, } // Try to mount partition - FRESULT res = f_mount(fs, "", 1); + FRESULT res = f_mount(fs, drv, 1); if (res != FR_OK) { err = ESP_FAIL; ESP_LOGW(TAG, "failed to mount card (%d)", res); @@ -84,7 +102,7 @@ esp_err_t esp_vfs_fat_sdmmc_mount(const char* base_path, ESP_LOGW(TAG, "partitioning card"); DWORD plist[] = {100, 0, 0, 0}; workbuf = malloc(workbuf_size); - res = f_fdisk(0, plist, workbuf); + res = f_fdisk(s_pdrv, plist, workbuf); if (res != FR_OK) { err = ESP_FAIL; ESP_LOGD(TAG, "f_fdisk failed (%d)", res); @@ -99,7 +117,7 @@ esp_err_t esp_vfs_fat_sdmmc_mount(const char* base_path, } free(workbuf); ESP_LOGW(TAG, "mounting again"); - res = f_mount(fs, "", 0); + res = f_mount(fs, drv, 0); if (res != FR_OK) { err = ESP_FAIL; ESP_LOGD(TAG, "f_mount failed after formatting (%d)", res); @@ -111,7 +129,8 @@ esp_err_t esp_vfs_fat_sdmmc_mount(const char* base_path, fail: sdmmc_host_deinit(); free(workbuf); - esp_vfs_unregister(base_path); + esp_vfs_fat_unregister_path(base_path); + ff_diskio_unregister(pdrv); free(s_card); s_card = NULL; return err; @@ -123,10 +142,15 @@ esp_err_t esp_vfs_fat_sdmmc_unmount() return ESP_ERR_INVALID_STATE; } // unmount - f_mount(0, "", 0); + char drv[3] = {(char)('0' + s_pdrv), ':', 0}; + f_mount(0, drv, 0); // release SD driver + ff_diskio_unregister(s_pdrv); free(s_card); s_card = NULL; sdmmc_host_deinit(); - return esp_vfs_fat_unregister(); + esp_err_t err = esp_vfs_fat_unregister_path(s_base_path); + free(s_base_path); + s_base_path = NULL; + return err; } diff --git a/docs/api/storage/fatfs.rst b/docs/api/storage/fatfs.rst index d2efc87ab..6dd04ae3e 100644 --- a/docs/api/storage/fatfs.rst +++ b/docs/api/storage/fatfs.rst @@ -8,7 +8,7 @@ Additionally, FatFs has been modified to support run-time pluggable disk IO laye Using FatFs with VFS -------------------- -``esp_vfs_fat.h`` header file defines functions to connect FatFs with VFS. ``esp_vfs_fat_register`` function allocates a ``FATFS`` structure, and registers a given path prefix in VFS. Subsequent operations on files starting with this prefix are forwarded to FatFs APIs. ``esp_vfs_fat_unregister`` function deletes the registration with VFS, and frees the ``FATFS`` structure. +``esp_vfs_fat.h`` header file defines functions to connect FatFs with VFS. ``esp_vfs_fat_register`` function allocates a ``FATFS`` structure, and registers a given path prefix in VFS. Subsequent operations on files starting with this prefix are forwarded to FatFs APIs. ``esp_vfs_fat_unregister_path`` function deletes the registration with VFS, and frees the ``FATFS`` structure. Most applications will use the following flow when working with ``esp_vfs_fat_`` functions: @@ -28,12 +28,12 @@ Most applications will use the following flow when working with ``esp_vfs_fat_`` 8. Call ``ff_diskio_register`` with NULL ``ff_diskio_impl_t*`` argument and the same drive number. -9. Call ``esp_vfs_fat_unregister`` to remove FatFs from VFS, and free the ``FATFS`` structure allocated on step 1. +9. Call ``esp_vfs_fat_unregister_path`` with the path where the file system is mounted to remove FatFs from VFS, and free the ``FATFS`` structure allocated on step 1. Convenience functions, ``esp_vfs_fat_sdmmc_mount`` and ``esp_vfs_fat_sdmmc_unmount``, which wrap these steps and also handle SD card initialization, are described in the next section. .. doxygenfunction:: esp_vfs_fat_register -.. doxygenfunction:: esp_vfs_fat_unregister +.. doxygenfunction:: esp_vfs_fat_unregister_path Using FatFs with VFS and SD cards