Merge branch 'feature/fatfs_dynamic_pdrv' into 'master'

FATFS enable support for multiple drives

Current implementation has drive numbers and paths hardcoded to support
only one FATFS drive. Arduino has it's own SPI driver to allow
compatibility and flexibility. With the MR it is possible to have up to
```_VOLUMES``` drives connected (SPI, SDMMC and others) at the same
time and accessed through VFS

See merge request !478
This commit is contained in:
Ivan Grokhotkov 2017-03-03 11:40:37 +08:00
commit 4ecd26ce77
6 changed files with 200 additions and 44 deletions

View file

@ -18,39 +18,71 @@
#include <sys/time.h>
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)

View file

@ -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 */

View file

@ -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

View file

@ -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);

View file

@ -13,6 +13,7 @@
// limitations under the License.
#include <stdlib.h>
#include <string.h>
#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;
}

View file

@ -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