716 lines
18 KiB
C
716 lines
18 KiB
C
|
|
/*
|
||
|
|
* Copyright 2024, Hiroyuki OYAMA
|
||
|
|
*
|
||
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
||
|
|
*/
|
||
|
|
|
||
|
|
// #include <stdarg.h>
|
||
|
|
#include <stdio.h>
|
||
|
|
// #include <stdlib.h>
|
||
|
|
// #include <string.h>
|
||
|
|
//#include <errno.h>
|
||
|
|
//#include <time.h>
|
||
|
|
// #include <unistd.h>
|
||
|
|
#include "blockdevice.h"
|
||
|
|
#include "fat.h"
|
||
|
|
#include "ff.h"
|
||
|
|
#include "diskio.h"
|
||
|
|
#include "VFS.h"
|
||
|
|
|
||
|
|
#ifndef ETIMEDOUT
|
||
|
|
#define ETIMEDOUT 116
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#undef PATH_MAX
|
||
|
|
#define PATH_MAX 256
|
||
|
|
|
||
|
|
static WORD disk_get_sector_size(blockdevice_t *block_dev)
|
||
|
|
{
|
||
|
|
size_t sector_size = block_dev->erase_size;
|
||
|
|
|
||
|
|
WORD ssize = sector_size;
|
||
|
|
if (ssize < 512)
|
||
|
|
{
|
||
|
|
ssize = 512;
|
||
|
|
}
|
||
|
|
return ssize;
|
||
|
|
}
|
||
|
|
|
||
|
|
static DWORD disk_get_sector_count(blockdevice_t *block_dev)
|
||
|
|
{
|
||
|
|
DWORD scount = (block_dev->size(block_dev)) / disk_get_sector_size(block_dev);
|
||
|
|
return scount;
|
||
|
|
}
|
||
|
|
|
||
|
|
static DSTATUS fatfs_status(void *status)
|
||
|
|
{
|
||
|
|
return RES_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
static DSTATUS fatfs_init(void *init_dev)
|
||
|
|
{
|
||
|
|
blockdevice_t *block_dev = (blockdevice_t *)init_dev;
|
||
|
|
return block_dev->init(block_dev);
|
||
|
|
}
|
||
|
|
|
||
|
|
static DRESULT fatfs_read(void *dev, BYTE *buff, DWORD sector, UINT count)
|
||
|
|
{
|
||
|
|
blockdevice_t *block_dev = (blockdevice_t *)dev;
|
||
|
|
DWORD ssize = disk_get_sector_size(block_dev);
|
||
|
|
bd_size_t addr = (bd_size_t)sector * ssize;
|
||
|
|
bd_size_t size = count * ssize;
|
||
|
|
int err = block_dev->read(block_dev, (const void *)buff, addr, size);
|
||
|
|
return err ? RES_PARERR : RES_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
static DRESULT fatfs_write(void *dev, BYTE *buff, DWORD sector, UINT count)
|
||
|
|
{
|
||
|
|
blockdevice_t *block_dev = (blockdevice_t *)dev;
|
||
|
|
DWORD ssize = disk_get_sector_size(block_dev);
|
||
|
|
bd_size_t addr = (bd_size_t)sector * ssize;
|
||
|
|
bd_size_t size = count * ssize;
|
||
|
|
|
||
|
|
int err = block_dev->erase(block_dev, addr, size);
|
||
|
|
if (err)
|
||
|
|
{
|
||
|
|
return RES_PARERR;
|
||
|
|
}
|
||
|
|
err = block_dev->program(block_dev, buff, addr, size);
|
||
|
|
if (err)
|
||
|
|
{
|
||
|
|
return RES_PARERR;
|
||
|
|
}
|
||
|
|
|
||
|
|
return RES_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
static DRESULT fatfs_ioctl(void *dev, BYTE cmd, void *buff)
|
||
|
|
{
|
||
|
|
blockdevice_t *block_dev = (blockdevice_t *)dev;
|
||
|
|
switch (cmd)
|
||
|
|
{
|
||
|
|
case CTRL_SYNC:
|
||
|
|
if (block_dev == NULL)
|
||
|
|
{
|
||
|
|
return RES_NOTRDY;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
return RES_OK;
|
||
|
|
}
|
||
|
|
case GET_SECTOR_COUNT:
|
||
|
|
if (block_dev == NULL)
|
||
|
|
{
|
||
|
|
return RES_NOTRDY;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
*((DWORD *)buff) = disk_get_sector_count(block_dev);
|
||
|
|
return RES_OK;
|
||
|
|
}
|
||
|
|
case GET_SECTOR_SIZE:
|
||
|
|
if (block_dev == NULL)
|
||
|
|
{
|
||
|
|
return RES_NOTRDY;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
*((WORD *)buff) = disk_get_sector_size(block_dev);
|
||
|
|
return RES_OK;
|
||
|
|
}
|
||
|
|
case GET_BLOCK_SIZE:
|
||
|
|
*((DWORD *)buff) = 1; // default when not known
|
||
|
|
return RES_OK;
|
||
|
|
case CTRL_TRIM:
|
||
|
|
if (block_dev == NULL)
|
||
|
|
{
|
||
|
|
return RES_NOTRDY;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
DWORD *sectors = (DWORD *)buff;
|
||
|
|
DWORD ssize = disk_get_sector_size(block_dev);
|
||
|
|
bd_size_t addr = (bd_size_t)sectors[0] * ssize;
|
||
|
|
bd_size_t size = (bd_size_t)(sectors[1] - sectors[0] + 1) * ssize;
|
||
|
|
int err = block_dev->trim(block_dev, addr, size);
|
||
|
|
return err ? RES_PARERR : RES_OK;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return RES_PARERR;
|
||
|
|
}
|
||
|
|
|
||
|
|
static struct fatfs_diskio sdcdisk_driver = {
|
||
|
|
.status = fatfs_status,
|
||
|
|
.init = fatfs_init,
|
||
|
|
.read = fatfs_read,
|
||
|
|
.write = fatfs_write,
|
||
|
|
.ioctl = fatfs_ioctl};
|
||
|
|
|
||
|
|
typedef struct
|
||
|
|
{
|
||
|
|
FIL file;
|
||
|
|
} fat_file_t;
|
||
|
|
|
||
|
|
typedef struct
|
||
|
|
{
|
||
|
|
FATFS fatfs;
|
||
|
|
int id;
|
||
|
|
} filesystem_fat_context_t;
|
||
|
|
|
||
|
|
static const char FILESYSTEM_NAME[] = "FAT";
|
||
|
|
static blockdevice_t *_ffs[FF_VOLUMES] = {0};
|
||
|
|
|
||
|
|
static int fat_error_remap(FRESULT res)
|
||
|
|
{
|
||
|
|
switch (res)
|
||
|
|
{
|
||
|
|
case FR_OK: // (0) Succeeded
|
||
|
|
return 0;
|
||
|
|
case FR_DISK_ERR: // (1) A hard error occurred in the low level disk I/O layer
|
||
|
|
return -EIO;
|
||
|
|
case FR_INT_ERR: // (2) Assertion failed
|
||
|
|
return -1;
|
||
|
|
case FR_NOT_READY: // (3) The physical drive cannot work
|
||
|
|
return -EIO;
|
||
|
|
case FR_NO_FILE: // (4) Could not find the file
|
||
|
|
return -ENOENT;
|
||
|
|
case FR_NO_PATH: // (5) Could not find the path
|
||
|
|
return -ENOTDIR;
|
||
|
|
case FR_INVALID_NAME: // (6) The path name format is invalid
|
||
|
|
return -EINVAL;
|
||
|
|
case FR_DENIED: // (7) Access denied due to prohibited access or directory full
|
||
|
|
return -EACCES;
|
||
|
|
case FR_EXIST: // (8) Access denied due to prohibited access
|
||
|
|
return -EEXIST;
|
||
|
|
case FR_INVALID_OBJECT: // (9) The file/directory object is invalid
|
||
|
|
return -EBADF;
|
||
|
|
case FR_WRITE_PROTECTED: // (10) The physical drive is write protected
|
||
|
|
return -EACCES;
|
||
|
|
case FR_INVALID_DRIVE: // (11) The logical drive number is invalid
|
||
|
|
return -ENODEV;
|
||
|
|
case FR_NOT_ENABLED: // (12) The volume has no work area
|
||
|
|
return -ENODEV;
|
||
|
|
case FR_NO_FILESYSTEM: // (13) There is no valid FAT volume
|
||
|
|
return -EINVAL;
|
||
|
|
case FR_MKFS_ABORTED: // (14) The f_mkfs() aborted due to any problem
|
||
|
|
return -EIO;
|
||
|
|
case FR_TIMEOUT: // (15) Could not get a grant to access the volume within defined period
|
||
|
|
return -ETIMEDOUT;
|
||
|
|
case FR_LOCKED: // (16) The operation is rejected according to the file sharing policy
|
||
|
|
return -EBUSY;
|
||
|
|
case FR_NOT_ENOUGH_CORE: // (17) LFN working buffer could not be allocated
|
||
|
|
return -ENOMEM;
|
||
|
|
case FR_TOO_MANY_OPEN_FILES: // (18) Number of open files > FF_FS_LOCK
|
||
|
|
return -ENFILE;
|
||
|
|
case FR_INVALID_PARAMETER: // (19) Given parameter is invalid
|
||
|
|
return -EINVAL;
|
||
|
|
default:
|
||
|
|
return -res;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline void debug_if(int condition, const char *format, ...)
|
||
|
|
{
|
||
|
|
if (condition)
|
||
|
|
{
|
||
|
|
va_list args;
|
||
|
|
va_start(args, format);
|
||
|
|
vprintf(format, args);
|
||
|
|
va_end(args);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
static int mount(filesystem_t *fs, blockdevice_t *device, bool pending)
|
||
|
|
{
|
||
|
|
filesystem_fat_context_t *context = fs->context;
|
||
|
|
|
||
|
|
char _fsid[3] = {0};
|
||
|
|
for (size_t i = 0; i < FF_VOLUMES; i++)
|
||
|
|
{
|
||
|
|
if (_ffs[i] == NULL)
|
||
|
|
{
|
||
|
|
context->id = i;
|
||
|
|
_ffs[i] = device;
|
||
|
|
_fsid[0] = '0' + i;
|
||
|
|
_fsid[1] = ':';
|
||
|
|
_fsid[2] = '\0';
|
||
|
|
//这里再去注册
|
||
|
|
fatfs_register_drive(i,&sdcdisk_driver,device);
|
||
|
|
FRESULT res = f_mount(&context->fatfs, _fsid, !pending);
|
||
|
|
if (res != FR_OK)
|
||
|
|
{
|
||
|
|
_ffs[i] = NULL;
|
||
|
|
}
|
||
|
|
return fat_error_remap(res);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return -ENOMEM;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int unmount(filesystem_t *fs)
|
||
|
|
{
|
||
|
|
filesystem_fat_context_t *context = fs->context;
|
||
|
|
|
||
|
|
char _fsid[3] = "0:";
|
||
|
|
_fsid[0] = '0' + context->id;
|
||
|
|
|
||
|
|
FRESULT res = f_mount(NULL, _fsid, 0);
|
||
|
|
_ffs[context->id] = NULL;
|
||
|
|
return fat_error_remap(res);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int format(filesystem_t *fs, blockdevice_t *device)
|
||
|
|
{
|
||
|
|
filesystem_fat_context_t *context = fs->context;
|
||
|
|
|
||
|
|
if (!device->is_initialized)
|
||
|
|
{
|
||
|
|
int err = device->init(device);
|
||
|
|
if (err)
|
||
|
|
{
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
// erase first handful of blocks
|
||
|
|
bd_size_t header = 2 * device->erase_size;
|
||
|
|
int err = device->erase(device, 0, header);
|
||
|
|
if (err)
|
||
|
|
{
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
#if 0
|
||
|
|
size_t program_size = device->program_size;
|
||
|
|
|
||
|
|
|
||
|
|
void *buffer = VFS_malloc(program_size);
|
||
|
|
if (!buffer) {
|
||
|
|
return -ENOMEM;
|
||
|
|
}
|
||
|
|
VFS_memset(buffer, 0xFF, program_size);
|
||
|
|
for (size_t i = 0; i < header; i += program_size) {
|
||
|
|
err = device->program(device, buffer, i, program_size);
|
||
|
|
if (err) {
|
||
|
|
VFS_free(buffer);
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
VFS_free(buffer);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
// trim entire device to indicate it is unneeded
|
||
|
|
err = device->trim(device, 0, device->size(device));
|
||
|
|
if (err)
|
||
|
|
{
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
err = fs->mount(fs, device, true);
|
||
|
|
if (err)
|
||
|
|
{
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
uint8_t work[512];
|
||
|
|
|
||
|
|
char id[3] = "0:";
|
||
|
|
id[0] = '0' + context->id;
|
||
|
|
|
||
|
|
FRESULT res = f_mkfs((const TCHAR *)id, FM_ANY | FM_SFD, 0, work, 512);
|
||
|
|
if (res != FR_OK)
|
||
|
|
{
|
||
|
|
fs->unmount(fs);
|
||
|
|
return fat_error_remap(res);
|
||
|
|
}
|
||
|
|
|
||
|
|
err = fs->unmount(fs);
|
||
|
|
if (err)
|
||
|
|
{
|
||
|
|
return res;
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static const char *fat_path_prefix(char *dist, int id, const char *path)
|
||
|
|
{
|
||
|
|
if (id == 0)
|
||
|
|
{
|
||
|
|
VFS_strcpy(dist, path);
|
||
|
|
return (const char *)dist;
|
||
|
|
}
|
||
|
|
|
||
|
|
dist[0] = '0' + id;
|
||
|
|
dist[1] = ':';
|
||
|
|
VFS_strcpy(dist + strlen("0:"), path);
|
||
|
|
return (const char *)dist;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int file_remove(filesystem_t *fs, const char *path)
|
||
|
|
{
|
||
|
|
filesystem_fat_context_t *context = fs->context;
|
||
|
|
|
||
|
|
char fpath[PATH_MAX];
|
||
|
|
fat_path_prefix(fpath, context->id, path);
|
||
|
|
FRESULT res = f_unlink(fpath);
|
||
|
|
|
||
|
|
if (res != FR_OK)
|
||
|
|
{
|
||
|
|
debug_if(FFS_DBG, "f_unlink() failed: %d\n", res);
|
||
|
|
if (res == FR_DENIED)
|
||
|
|
return -ENOTEMPTY;
|
||
|
|
}
|
||
|
|
return fat_error_remap(res);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int file_rename(filesystem_t *fs, const char *oldpath, const char *newpath)
|
||
|
|
{
|
||
|
|
filesystem_fat_context_t *context = fs->context;
|
||
|
|
char oldfpath[PATH_MAX];
|
||
|
|
char newfpath[PATH_MAX];
|
||
|
|
fat_path_prefix(oldfpath, context->id, oldpath);
|
||
|
|
fat_path_prefix(newfpath, context->id, newpath);
|
||
|
|
|
||
|
|
FRESULT res = f_rename(oldfpath, newfpath);
|
||
|
|
|
||
|
|
if (res != FR_OK)
|
||
|
|
{
|
||
|
|
debug_if(FFS_DBG, "f_rename() failed: %d\n", res);
|
||
|
|
}
|
||
|
|
return fat_error_remap(res);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int file_mkdir(filesystem_t *fs, const char *path, mode_t mode)
|
||
|
|
{
|
||
|
|
(void)mode;
|
||
|
|
filesystem_fat_context_t *context = fs->context;
|
||
|
|
char fpath[PATH_MAX];
|
||
|
|
fat_path_prefix(fpath, context->id, path);
|
||
|
|
FRESULT res = f_mkdir(fpath);
|
||
|
|
|
||
|
|
if (res != FR_OK)
|
||
|
|
{
|
||
|
|
debug_if(FFS_DBG, "f_mkdir() failed: %d\n", res);
|
||
|
|
}
|
||
|
|
return fat_error_remap(res);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int file_rmdir(filesystem_t *fs, const char *path)
|
||
|
|
{
|
||
|
|
filesystem_fat_context_t *context = fs->context;
|
||
|
|
char fpath[PATH_MAX];
|
||
|
|
fat_path_prefix(fpath, context->id, path);
|
||
|
|
FRESULT res = f_unlink(fpath);
|
||
|
|
if (res != FR_OK)
|
||
|
|
{
|
||
|
|
debug_if(FFS_DBG, "f_unlink() failed: %d\n", res);
|
||
|
|
}
|
||
|
|
return fat_error_remap(res);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int file_stat(filesystem_t *fs, const char *path, struct VFS_stat *st)
|
||
|
|
{
|
||
|
|
filesystem_fat_context_t *context = fs->context;
|
||
|
|
char fpath[PATH_MAX1];
|
||
|
|
fat_path_prefix(fpath, context->id, path);
|
||
|
|
FILINFO f = {0};
|
||
|
|
|
||
|
|
FRESULT res = f_stat(fpath, &f);
|
||
|
|
|
||
|
|
if (res != FR_OK)
|
||
|
|
{
|
||
|
|
return fat_error_remap(res);
|
||
|
|
}
|
||
|
|
st->st_size = f.fsize;
|
||
|
|
st->st_mode = 0;
|
||
|
|
st->st_mode |= (f.fattrib & AM_DIR) ? S_IFDIR : S_IFREG;
|
||
|
|
st->st_mode |= (f.fattrib & AM_RDO) ? (S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) : (S_IRWXU | S_IRWXG | S_IRWXO);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int file_open(filesystem_t *fs, fs_file_t *file, const char *path, int flags)
|
||
|
|
{
|
||
|
|
BYTE open_mode;
|
||
|
|
if (flags & O_RDWR)
|
||
|
|
open_mode = FA_READ | FA_WRITE;
|
||
|
|
else if (flags & O_WRONLY)
|
||
|
|
open_mode = FA_WRITE;
|
||
|
|
else
|
||
|
|
open_mode = FA_READ;
|
||
|
|
if (flags & O_CREAT)
|
||
|
|
{
|
||
|
|
if (flags & O_TRUNC)
|
||
|
|
open_mode |= FA_CREATE_ALWAYS;
|
||
|
|
else
|
||
|
|
open_mode |= FA_OPEN_ALWAYS;
|
||
|
|
}
|
||
|
|
if (flags & O_APPEND)
|
||
|
|
open_mode |= FA_OPEN_APPEND;
|
||
|
|
|
||
|
|
char fpath[PATH_MAX];
|
||
|
|
filesystem_fat_context_t *context = fs->context;
|
||
|
|
fat_path_prefix(fpath, context->id, path);
|
||
|
|
fat_file_t *fat_file = file->context = VFS_calloc(1, sizeof(fat_file_t));
|
||
|
|
if (fat_file == NULL)
|
||
|
|
{
|
||
|
|
os_printf("file_open: Out of memory\n");
|
||
|
|
return -ENOMEM;
|
||
|
|
}
|
||
|
|
FRESULT res = f_open(&fat_file->file, fpath, open_mode);
|
||
|
|
|
||
|
|
if (res != FR_OK)
|
||
|
|
{
|
||
|
|
VFS_free(fat_file);
|
||
|
|
debug_if(FFS_DBG, "f_open('w') failed: %d\n", res);
|
||
|
|
return fat_error_remap(res);
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int file_close(filesystem_t *fs, fs_file_t *file)
|
||
|
|
{
|
||
|
|
(void)fs;
|
||
|
|
// filesystem_fat_context_t *context = fs->context;
|
||
|
|
fat_file_t *fat_file = file->context;
|
||
|
|
FRESULT res = f_close(&fat_file->file);
|
||
|
|
VFS_free(file->context);
|
||
|
|
file->context = NULL;
|
||
|
|
return fat_error_remap(res);
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t file_write(filesystem_t *fs, fs_file_t *file, const void *buffer, size_t size)
|
||
|
|
{
|
||
|
|
(void)fs;
|
||
|
|
// filesystem_fat_context_t *context = fs->context;
|
||
|
|
fat_file_t *fat_file = file->context;
|
||
|
|
|
||
|
|
UINT n;
|
||
|
|
|
||
|
|
FRESULT res = f_write(&(fat_file->file), buffer, size, &n);
|
||
|
|
if (res != FR_OK)
|
||
|
|
{
|
||
|
|
debug_if(FFS_DBG, "f_write() failed: %d", res);
|
||
|
|
return fat_error_remap(res);
|
||
|
|
}
|
||
|
|
res = f_sync(&fat_file->file);
|
||
|
|
|
||
|
|
if (res != FR_OK)
|
||
|
|
{
|
||
|
|
debug_if(FFS_DBG, "f_write() failed: %d", res);
|
||
|
|
return fat_error_remap(res);
|
||
|
|
}
|
||
|
|
return n;
|
||
|
|
}
|
||
|
|
|
||
|
|
static ssize_t file_read(filesystem_t *fs, fs_file_t *file, void *buffer, size_t size)
|
||
|
|
{
|
||
|
|
(void)fs;
|
||
|
|
// filesystem_fat_context_t *context = fs->context;
|
||
|
|
fat_file_t *fat_file = file->context;
|
||
|
|
|
||
|
|
UINT n;
|
||
|
|
|
||
|
|
FRESULT res = f_read(&fat_file->file, buffer, size, &n);
|
||
|
|
|
||
|
|
if (res != FR_OK)
|
||
|
|
{
|
||
|
|
debug_if(FFS_DBG, "f_read() failed: %d\n", res);
|
||
|
|
return fat_error_remap(res);
|
||
|
|
}
|
||
|
|
return n;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int file_sync(filesystem_t *fs, fs_file_t *file)
|
||
|
|
{
|
||
|
|
(void)fs;
|
||
|
|
// filesystem_fat_context_t *context = fs->context;
|
||
|
|
fat_file_t *fat_file = file->context;
|
||
|
|
|
||
|
|
FRESULT res = f_sync(&fat_file->file);
|
||
|
|
|
||
|
|
if (res != FR_OK)
|
||
|
|
{
|
||
|
|
debug_if(FFS_DBG, "f_sync() failed: %d\n", res);
|
||
|
|
}
|
||
|
|
return fat_error_remap(res);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int32_t file_seek(filesystem_t *fs, fs_file_t *file, int32_t offset, int whence)
|
||
|
|
{
|
||
|
|
(void)fs;
|
||
|
|
// filesystem_fat_context_t *context = fs->context;
|
||
|
|
fat_file_t *fat_file = file->context;
|
||
|
|
if (whence == SEEK_END)
|
||
|
|
offset += f_size(&fat_file->file);
|
||
|
|
else if (whence == SEEK_CUR)
|
||
|
|
offset += f_tell(&fat_file->file);
|
||
|
|
|
||
|
|
FRESULT res = f_lseek(&fat_file->file, offset);
|
||
|
|
|
||
|
|
if (res != FR_OK)
|
||
|
|
{
|
||
|
|
debug_if(FFS_DBG, "lseek failed: %d\n", res);
|
||
|
|
return fat_error_remap(res);
|
||
|
|
}
|
||
|
|
return (int32_t)fat_file->file.fptr;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int32_t file_tell(filesystem_t *fs, fs_file_t *file)
|
||
|
|
{
|
||
|
|
(void)fs;
|
||
|
|
// filesystem_fat_context_t *context = fs->context;
|
||
|
|
fat_file_t *fat_file = file->context;
|
||
|
|
int32_t res = f_tell(&fat_file->file);
|
||
|
|
|
||
|
|
return res;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int32_t file_size(filesystem_t *fs, fs_file_t *file)
|
||
|
|
{
|
||
|
|
(void)fs;
|
||
|
|
// filesystem_fat_context_t *context = fs->context;
|
||
|
|
fat_file_t *fat_file = file->context;
|
||
|
|
int32_t res = f_size(&fat_file->file);
|
||
|
|
|
||
|
|
return res;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int file_truncate(filesystem_t *fs, fs_file_t *file, int32_t length)
|
||
|
|
{
|
||
|
|
(void)fs;
|
||
|
|
// filesystem_fat_context_t *context = fs->context;
|
||
|
|
fat_file_t *fat_file = file->context;
|
||
|
|
FSIZE_t old_offset = f_tell(&fat_file->file);
|
||
|
|
FRESULT res = f_lseek(&fat_file->file, length);
|
||
|
|
if (res)
|
||
|
|
{
|
||
|
|
return fat_error_remap(res);
|
||
|
|
}
|
||
|
|
res = f_truncate(&fat_file->file);
|
||
|
|
if (res)
|
||
|
|
{
|
||
|
|
return fat_error_remap(res);
|
||
|
|
}
|
||
|
|
res = f_lseek(&fat_file->file, old_offset);
|
||
|
|
|
||
|
|
if (res)
|
||
|
|
{
|
||
|
|
return fat_error_remap(res);
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int dir_open(filesystem_t *fs, fs_dir_t *dir, const char *path)
|
||
|
|
{
|
||
|
|
filesystem_fat_context_t *context = fs->context;
|
||
|
|
char fpath[PATH_MAX];
|
||
|
|
fat_path_prefix(fpath, context->id, path);
|
||
|
|
|
||
|
|
DIR *dh = VFS_calloc(1, sizeof(DIR));
|
||
|
|
if (dh == NULL)
|
||
|
|
{
|
||
|
|
os_printf("dir_open: Out of memory\n");
|
||
|
|
return -ENOMEM;
|
||
|
|
}
|
||
|
|
FRESULT res = f_opendir(dh, fpath);
|
||
|
|
|
||
|
|
if (res != FR_OK)
|
||
|
|
{
|
||
|
|
debug_if(FFS_DBG, "f_opendir() failed: %d\n", res);
|
||
|
|
VFS_free(dh);
|
||
|
|
return fat_error_remap(res);
|
||
|
|
}
|
||
|
|
dir->context = dh;
|
||
|
|
dir->fd = -1;
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int dir_close(filesystem_t *fs, fs_dir_t *dir)
|
||
|
|
{
|
||
|
|
(void)fs;
|
||
|
|
// filesystem_fat_context_t *context = fs->context;
|
||
|
|
|
||
|
|
DIR *dh = (DIR *)dir->context;
|
||
|
|
|
||
|
|
FRESULT res = f_closedir(dh);
|
||
|
|
|
||
|
|
VFS_free(dh);
|
||
|
|
return fat_error_remap(res);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int dir_read(filesystem_t *fs, fs_dir_t *dir, struct dirent *ent)
|
||
|
|
{
|
||
|
|
(void)fs;
|
||
|
|
// filesystem_fat_context_t *context = fs->context;
|
||
|
|
|
||
|
|
DIR *dh = (DIR *)dir->context;
|
||
|
|
FILINFO finfo = {0};
|
||
|
|
FRESULT res = f_readdir(dh, &finfo);
|
||
|
|
|
||
|
|
if (res != FR_OK)
|
||
|
|
{
|
||
|
|
return fat_error_remap(res);
|
||
|
|
}
|
||
|
|
else if (finfo.fname[0] == 0)
|
||
|
|
{
|
||
|
|
return -ENOENT;
|
||
|
|
}
|
||
|
|
ent->d_type = (finfo.fattrib & AM_DIR) ? DT_DIR : DT_REG;
|
||
|
|
ent->fsize = finfo.fsize;
|
||
|
|
VFS_strncpy(ent->d_name, finfo.fname, FF_MAX_LFN);
|
||
|
|
ent->d_name[FF_MAX_LFN] = '\0';
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
filesystem_t *filesystem_fat_create()
|
||
|
|
{
|
||
|
|
|
||
|
|
|
||
|
|
filesystem_t *fs = VFS_calloc(1, sizeof(filesystem_t));
|
||
|
|
if (fs == NULL)
|
||
|
|
{
|
||
|
|
os_printf("filesystem_fat_create: Out of memory\n");
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
fs->type = FILESYSTEM_TYPE_FAT;
|
||
|
|
fs->name = FILESYSTEM_NAME;
|
||
|
|
fs->mount = mount;
|
||
|
|
fs->unmount = unmount;
|
||
|
|
fs->format = format;
|
||
|
|
fs->remove = file_remove;
|
||
|
|
fs->rename = file_rename;
|
||
|
|
fs->mkdir = file_mkdir;
|
||
|
|
fs->rmdir = file_rmdir;
|
||
|
|
fs->stat = file_stat;
|
||
|
|
fs->file_open = file_open;
|
||
|
|
fs->file_close = file_close;
|
||
|
|
fs->file_write = file_write;
|
||
|
|
fs->file_read = file_read;
|
||
|
|
fs->file_sync = file_sync;
|
||
|
|
fs->file_seek = file_seek;
|
||
|
|
fs->file_tell = file_tell;
|
||
|
|
fs->file_size = file_size;
|
||
|
|
fs->file_truncate = file_truncate;
|
||
|
|
fs->dir_open = dir_open;
|
||
|
|
fs->dir_close = dir_close;
|
||
|
|
fs->dir_read = dir_read;
|
||
|
|
filesystem_fat_context_t *context = VFS_calloc(1, sizeof(filesystem_fat_context_t));
|
||
|
|
if (context == NULL)
|
||
|
|
{
|
||
|
|
os_printf("filesystem_fat_create: Out of memory\n");
|
||
|
|
VFS_free(fs);
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
context->id = -1;
|
||
|
|
|
||
|
|
fs->context = context;
|
||
|
|
return fs;
|
||
|
|
}
|
||
|
|
|
||
|
|
void filesystem_fat_free(filesystem_t *fs)
|
||
|
|
{
|
||
|
|
VFS_free(fs->context);
|
||
|
|
fs->context = NULL;
|
||
|
|
VFS_free(fs);
|
||
|
|
fs = NULL;
|
||
|
|
}
|