Initial commit: TXW82x FPV v2.7.0.7-42229 SDK + project sources
This commit is contained in:
55
sdk/lib/VFS/blockdevice.h
Normal file
55
sdk/lib/VFS/blockdevice.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2024, Hiroyuki OYAMA
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
#ifndef BLOCKDEVICE_H
|
||||
#define BLOCKDEVICE_H
|
||||
|
||||
/** \file blockdevice.h
|
||||
* \defgroup blockdevice blockdevice
|
||||
* \brief Block device abstraction layer for storage media
|
||||
*/
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef uint64_t bd_size_t;
|
||||
|
||||
enum bd_error {
|
||||
BD_ERROR_OK = 0, /*!< no error */
|
||||
BD_ERROR_DEVICE_ERROR = -4001, /*!< device specific error */
|
||||
};
|
||||
|
||||
/*! \brief block device abstract object
|
||||
* \ingroup blockdevice
|
||||
*
|
||||
* All block device objects implement blockdevice_t
|
||||
*/
|
||||
typedef struct blockdevice {
|
||||
int (*init)(struct blockdevice *device);
|
||||
int (*deinit)(struct blockdevice *device);
|
||||
int (*sync)(struct blockdevice *device);
|
||||
int (*read)(struct blockdevice *device, const void *buffer, bd_size_t addr, bd_size_t size);
|
||||
int (*program)(struct blockdevice *device, const void *buffer, bd_size_t addr, bd_size_t size);
|
||||
int (*erase)(struct blockdevice *device, bd_size_t addr, bd_size_t size);
|
||||
int (*trim)(struct blockdevice *device, bd_size_t addr, bd_size_t size);
|
||||
bd_size_t (*size)(struct blockdevice *device);
|
||||
size_t read_size;
|
||||
size_t erase_size;
|
||||
size_t program_size;
|
||||
const char *name;
|
||||
void *config;
|
||||
bool is_initialized;
|
||||
} blockdevice_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
33
sdk/lib/VFS/demo/VFS_demo_fs_heap.c
Normal file
33
sdk/lib/VFS/demo/VFS_demo_fs_heap.c
Normal file
@@ -0,0 +1,33 @@
|
||||
#include "vfs.h"
|
||||
#include "fat.h"
|
||||
#include "heap.h"
|
||||
|
||||
/*****************************************************************
|
||||
* 从内存申请一片空间,然后格式化成一个fat的文件系统,这里假装是sd
|
||||
***************************************************************/
|
||||
int vfs_demo_fs_heap_init(uint8_t *heap_buf,uint32_t heap_size)
|
||||
{
|
||||
blockdevice_t *heap = blockdevice_heap_create(heap_buf,heap_size);
|
||||
filesystem_t *fat = filesystem_fat_create();
|
||||
|
||||
uint8_t *src = (uint8_t*)0x10100000;
|
||||
hw_memcpy(heap_buf,src,heap_size);
|
||||
|
||||
os_printf("Heap:%X\n",heap);
|
||||
int err = fs_mount("/sd", fat, heap);
|
||||
if (err == -1) {
|
||||
os_printf("format / with FAT\n");
|
||||
err = fs_format(fat, heap);
|
||||
if (err == -1) {
|
||||
os_printf("fs_format error: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
err = fs_mount("/sd", fat, heap);
|
||||
os_printf("errno:%d\n",errno);
|
||||
if (err == -1) {
|
||||
os_printf("fs_mount error: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
31
sdk/lib/VFS/demo/VFS_demo_fs_sd.c
Normal file
31
sdk/lib/VFS/demo/VFS_demo_fs_sd.c
Normal file
@@ -0,0 +1,31 @@
|
||||
#include "vfs.h"
|
||||
#include "fat.h"
|
||||
|
||||
/*****************************************************************
|
||||
* 从内存申请一片空间,然后格式化成一个fat的文件系统,这里假装是sd
|
||||
***************************************************************/
|
||||
int vfs_demo_fs_sd_init()
|
||||
{
|
||||
extern blockdevice_t *blockdevice_sd_create();
|
||||
blockdevice_t *heap = blockdevice_sd_create();
|
||||
filesystem_t *fat = filesystem_fat_create();
|
||||
|
||||
|
||||
os_printf("Heap:%X\n",heap);
|
||||
int err = fs_mount("/sd", fat, heap);
|
||||
if (err == -1) {
|
||||
os_printf("format / with FAT\n");
|
||||
err = fs_format(fat, heap);
|
||||
if (err == -1) {
|
||||
os_printf("fs_format error: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
err = fs_mount("/sd", fat, heap);
|
||||
os_printf("errno:%d\n",errno);
|
||||
if (err == -1) {
|
||||
os_printf("fs_mount error: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
715
sdk/lib/VFS/fat.c
Normal file
715
sdk/lib/VFS/fat.c
Normal file
@@ -0,0 +1,715 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
65
sdk/lib/VFS/fat.h
Normal file
65
sdk/lib/VFS/fat.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2024, Hiroyuki OYAMA
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
#ifndef FAT_H
|
||||
#define FAT_H
|
||||
/** \file fat.h
|
||||
* \defgroup filesystem_fat filesystem_fat
|
||||
* \ingroup filesystem
|
||||
* \brief FAT file system
|
||||
*/
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "filesystem.h"
|
||||
|
||||
//#define S_IRWXU (S_IRUSR | S_IWUSR | S_IXUSR)
|
||||
//#define S_IRUSR 0000400 /* read permission, owner */
|
||||
//#define S_IWUSR 0000200 /* write permission, owner */
|
||||
//#define S_IXUSR 0000100/* execute/search permission, owner */
|
||||
//#define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
|
||||
//#define S_IRGRP 0000040 /* read permission, group */
|
||||
//#define S_IWGRP 0000020 /* write permission, grougroup */
|
||||
//#define S_IXGRP 0000010/* execute/search permission, group */
|
||||
//#define S_IRWXO (S_IROTH | S_IWOTH | S_IXOTH)
|
||||
//#define S_IROTH 0000004 /* read permission, other */
|
||||
//#define S_IWOTH 0000002 /* write permission, other */
|
||||
//#define S_IXOTH 0000001/* execute/search permission, other */
|
||||
|
||||
#ifndef S_IFDIR
|
||||
#define _IFDIR 0040000 /* directory */
|
||||
#define S_IFDIR 0100000
|
||||
#endif
|
||||
|
||||
#ifndef S_IFREG
|
||||
#define _IFREG 0040000 /* directory */
|
||||
#define S_IFREG _IFREG
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#define FFS_DBG 0
|
||||
|
||||
|
||||
/*! \brief Create FAT file system object
|
||||
* \ingroup filesystem_fat
|
||||
*
|
||||
* \return File system object. Returns NULL in case of failure.
|
||||
* \retval NULL failed to create file system object.
|
||||
*/
|
||||
filesystem_t *filesystem_fat_create();
|
||||
|
||||
/*! \brief Release FAT file system object
|
||||
* \ingroup filesystem_fat
|
||||
*
|
||||
* \param fs FAT file system object
|
||||
*/
|
||||
void filesystem_fat_free(filesystem_t *fs);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
120
sdk/lib/VFS/filesystem.h
Normal file
120
sdk/lib/VFS/filesystem.h
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright 2024, Hiroyuki OYAMA
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
#ifndef FILESYSTEM_H
|
||||
#define FILESYSTEM_H
|
||||
/** \file filesystem.h
|
||||
* \defgroup filesystem filesystem
|
||||
* \brief File system abstraction layer
|
||||
*/
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
//#include <unistd.h>
|
||||
#include "blockdevice.h"
|
||||
|
||||
#ifndef SEEK_SET
|
||||
#define SEEK_SET 0
|
||||
#endif
|
||||
|
||||
#ifndef SEEK_CUR
|
||||
#define SEEK_CUR 1
|
||||
#endif
|
||||
|
||||
#ifndef SEEK_END
|
||||
#define SEEK_END 2
|
||||
#endif
|
||||
|
||||
|
||||
struct VFS_stat {
|
||||
int st_size;
|
||||
int st_mode;
|
||||
};
|
||||
|
||||
#define PATH_MAX1 256
|
||||
#ifndef mode_t
|
||||
#define mode_t uint32_t
|
||||
#endif
|
||||
|
||||
enum {
|
||||
FILESYSTEM_TYPE_FAT,
|
||||
FILESYSTEM_TYPE_LITTLEFS,
|
||||
};
|
||||
|
||||
enum {
|
||||
DT_UNKNOWN = 0,
|
||||
DT_DIR = 4,
|
||||
DT_REG = 8,
|
||||
};
|
||||
|
||||
struct dirent {
|
||||
uint64_t fsize;
|
||||
uint8_t d_type;
|
||||
char d_name[255 + 1];
|
||||
};
|
||||
|
||||
/*! \brief file object
|
||||
* \ingroup filesystem
|
||||
*
|
||||
* Files operated on by file system objects are represented by fs_file_t objects
|
||||
*/
|
||||
typedef struct {
|
||||
int fd;
|
||||
void *context;
|
||||
} fs_file_t;
|
||||
|
||||
/*! \brief directory object
|
||||
* \ingroup filesystem
|
||||
*
|
||||
* Directories operated on by file system objects are represented by fs_dir_t objects
|
||||
*/
|
||||
typedef struct {
|
||||
int fd;
|
||||
void *context;
|
||||
struct dirent current;
|
||||
} fs_dir_t;
|
||||
|
||||
/*! \brief file system abstract object
|
||||
* \ingroup filesystem
|
||||
*
|
||||
* All file system objects implement filesystem_t
|
||||
*/
|
||||
typedef struct filesystem {
|
||||
uint8_t type;
|
||||
const char *name;
|
||||
void *context;
|
||||
|
||||
int (*mount)(struct filesystem *fs, blockdevice_t *device, bool pending);
|
||||
int (*unmount)(struct filesystem *fs);
|
||||
int (*format)(struct filesystem *fs, blockdevice_t *device);
|
||||
|
||||
int (*remove)(struct filesystem *fs, const char *path);
|
||||
int (*rename)(struct filesystem *fs, const char *oldpath, const char *newpath);
|
||||
int (*mkdir)(struct filesystem *fs, const char *path, mode_t mode);
|
||||
int (*rmdir)(struct filesystem *fs, const char *path);
|
||||
int (*stat)(struct filesystem *fs, const char *path, struct VFS_stat *st);
|
||||
|
||||
int (*file_open)(struct filesystem *fs, fs_file_t *file, const char *path, int flags);
|
||||
int (*file_close)(struct filesystem *fs, fs_file_t *file);
|
||||
int32_t (*file_write)(struct filesystem *fs, fs_file_t *file, const void *buffer, size_t size);
|
||||
int32_t (*file_read)(struct filesystem *fs, fs_file_t *file, void *buffer, size_t size);
|
||||
int (*file_sync)(struct filesystem *fs, fs_file_t *file);
|
||||
int32_t (*file_seek)(struct filesystem *fs, fs_file_t *file, int32_t offset, int whence);
|
||||
int32_t (*file_tell)(struct filesystem *fs, fs_file_t *file);
|
||||
int32_t (*file_size)(struct filesystem *fs, fs_file_t *file);
|
||||
int (*file_truncate)(struct filesystem *fs, fs_file_t *file, int32_t length);
|
||||
|
||||
int (*dir_open)(struct filesystem *fs, fs_dir_t *dir, const char *path);
|
||||
int (*dir_close)(struct filesystem *fs, fs_dir_t *dir);
|
||||
int (*dir_read)(struct filesystem *fs, fs_dir_t *dir, struct dirent *ent);
|
||||
} filesystem_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
148
sdk/lib/VFS/heap.c
Normal file
148
sdk/lib/VFS/heap.c
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright 2024, Hiroyuki OYAMA
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "heap.h"
|
||||
#include "vfs.h"
|
||||
|
||||
#if !defined(PICO_VFS_BLOCKDEVICE_HEAP_BLOCK_SIZE)
|
||||
#define PICO_VFS_BLOCKDEVICE_HEAP_BLOCK_SIZE 512
|
||||
#endif
|
||||
|
||||
#if !defined(PICO_VFS_BLOCKDEVICE_HEAP_ERASE_VALUE)
|
||||
#define PICO_VFS_BLOCKDEVICE_HEAP_ERASE_VALUE 0xFF
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
size_t size;
|
||||
uint8_t *heap;
|
||||
} blockdevice_heap_config_t;
|
||||
|
||||
static const char DEVICE_NAME[] = "heap";
|
||||
|
||||
uint8_t *g_heap;
|
||||
uint32_t g_heap_size;
|
||||
|
||||
static int init(blockdevice_t *device)
|
||||
{
|
||||
(void)device;
|
||||
blockdevice_heap_config_t *config = device->config;
|
||||
if (device->is_initialized)
|
||||
{
|
||||
return BD_ERROR_OK;
|
||||
}
|
||||
g_heap = config->heap;
|
||||
g_heap_size = config->size;
|
||||
|
||||
device->is_initialized = true;
|
||||
return BD_ERROR_OK;
|
||||
}
|
||||
|
||||
static int deinit(blockdevice_t *device)
|
||||
{
|
||||
blockdevice_heap_config_t *config = device->config;
|
||||
|
||||
if (!device->is_initialized)
|
||||
{
|
||||
return BD_ERROR_OK;
|
||||
}
|
||||
config->heap = NULL;
|
||||
device->is_initialized = false;
|
||||
return BD_ERROR_OK;
|
||||
}
|
||||
|
||||
static int sync(blockdevice_t *device)
|
||||
{
|
||||
(void)device;
|
||||
return BD_ERROR_OK;
|
||||
}
|
||||
|
||||
static int read(blockdevice_t *device, const void *buffer, bd_size_t addr, bd_size_t length)
|
||||
{
|
||||
blockdevice_heap_config_t *config = device->config;
|
||||
VFS_memcpy((uint8_t *)buffer, config->heap + (size_t)addr, (size_t)length);
|
||||
return BD_ERROR_OK;
|
||||
}
|
||||
|
||||
static int erase(blockdevice_t *device, bd_size_t addr, bd_size_t length)
|
||||
{
|
||||
blockdevice_heap_config_t *config = device->config;
|
||||
assert(config->heap != NULL);
|
||||
VFS_memset(config->heap + (size_t)addr, PICO_VFS_BLOCKDEVICE_HEAP_ERASE_VALUE, (size_t)length);
|
||||
|
||||
return BD_ERROR_OK;
|
||||
}
|
||||
|
||||
static int program(blockdevice_t *device, const void *buffer, bd_size_t addr, bd_size_t length)
|
||||
{
|
||||
blockdevice_heap_config_t *config = device->config;
|
||||
VFS_memcpy(config->heap + (size_t)addr, buffer, (size_t)length);
|
||||
return BD_ERROR_OK;
|
||||
}
|
||||
|
||||
static int trim(blockdevice_t *device, bd_size_t addr, bd_size_t length)
|
||||
{
|
||||
(void)device;
|
||||
(void)addr;
|
||||
(void)length;
|
||||
return BD_ERROR_OK;
|
||||
}
|
||||
|
||||
static bd_size_t size(blockdevice_t *device)
|
||||
{
|
||||
blockdevice_heap_config_t *config = device->config;
|
||||
return (bd_size_t)config->size;
|
||||
}
|
||||
|
||||
blockdevice_t *blockdevice_heap_create(uint8_t *heap_buf, size_t length)
|
||||
{
|
||||
if (heap_buf == NULL || length == 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
blockdevice_t *device = VFS_calloc(1, sizeof(blockdevice_t));
|
||||
if (device == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
blockdevice_heap_config_t *config = calloc(1, sizeof(blockdevice_heap_config_t));
|
||||
if (config == NULL)
|
||||
{
|
||||
VFS_free(device);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
device->init = init;
|
||||
device->deinit = deinit;
|
||||
device->read = read;
|
||||
device->erase = erase;
|
||||
device->program = program;
|
||||
device->trim = trim;
|
||||
device->sync = sync;
|
||||
device->size = size;
|
||||
device->read_size = PICO_VFS_BLOCKDEVICE_HEAP_BLOCK_SIZE;
|
||||
device->erase_size = PICO_VFS_BLOCKDEVICE_HEAP_BLOCK_SIZE;
|
||||
device->program_size = PICO_VFS_BLOCKDEVICE_HEAP_BLOCK_SIZE;
|
||||
device->name = DEVICE_NAME;
|
||||
device->is_initialized = false;
|
||||
|
||||
config->size = length;
|
||||
config->heap = heap_buf;
|
||||
device->config = config;
|
||||
device->init(device);
|
||||
return device;
|
||||
}
|
||||
|
||||
void blockdevice_heap_free(blockdevice_t *device)
|
||||
{
|
||||
device->deinit(device);
|
||||
VFS_free(device->config);
|
||||
VFS_free(device);
|
||||
}
|
||||
42
sdk/lib/VFS/heap.h
Normal file
42
sdk/lib/VFS/heap.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2024, Hiroyuki OYAMA
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
#ifndef HEAP_H
|
||||
#define HEAP_H
|
||||
|
||||
/** \defgroup blockdevice_heap blockdevice_heap
|
||||
* \ingroup blockdevice
|
||||
* \brief Heap memory block device
|
||||
*/
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include "blockdevice.h"
|
||||
|
||||
/*! \brief Create RAM heap memory block device
|
||||
* \ingroup blockdevice_heap
|
||||
*
|
||||
* Create a block device object that uses RAM heap memory. The size of heap memory allocated to the block device is specified by size.
|
||||
*
|
||||
* \param size Size in bytes to be allocated to the block device.
|
||||
* \return Block device object. Returnes NULL in case of failure.
|
||||
* \retval NULL Failed to create block device object.
|
||||
*/
|
||||
blockdevice_t *blockdevice_heap_create(uint8_t *heap_buf,size_t size);
|
||||
|
||||
/*! \brief Release the heap memory device.
|
||||
* \ingroup blockdevice_heap
|
||||
*
|
||||
* \param device Block device object.
|
||||
*/
|
||||
void blockdevice_heap_free(blockdevice_t *device);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
729
sdk/lib/VFS/vfs.c
Normal file
729
sdk/lib/VFS/vfs.c
Normal file
@@ -0,0 +1,729 @@
|
||||
/*
|
||||
* Copyright 2024, Hiroyuki OYAMA
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
// #include <sys/errno.h>
|
||||
// #include <sys/dirent.h>
|
||||
// #include <sys/unistd.h>
|
||||
// #include <pico/mutex.h>
|
||||
#include "vfs.h"
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char *dir;
|
||||
void *filesystem;
|
||||
void *device;
|
||||
} mountpoint_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
fs_file_t *file;
|
||||
filesystem_t *filesystem;
|
||||
char path[PATH_MAX1 + 1];
|
||||
} file_descriptor_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
fs_dir_t *dir;
|
||||
filesystem_t *filesystem;
|
||||
} dir_descriptor_t;
|
||||
|
||||
#if !defined(PICO_VFS_MAX_MOUNTPOINT)
|
||||
#define PICO_VFS_MAX_MOUNTPOINT 10
|
||||
#endif
|
||||
#define FS_MAX_MOUNTPOINT PICO_VFS_MAX_MOUNTPOINT
|
||||
#define STDIO_FILNO_MAX 2
|
||||
#define FILENO_VALUE(fd) (fd + STDIO_FILNO_MAX + 1) // Conversion to file descriptors for publication
|
||||
#define FILENO_INDEX(fd) (fd - STDIO_FILNO_MAX - 1) // Conversion to file descriptors for internal use
|
||||
|
||||
static mountpoint_t mountpoints[FS_MAX_MOUNTPOINT] = {0}; // Mount points and file system map
|
||||
static size_t max_file_descriptor = 0; // File descriptor current maximum value
|
||||
static file_descriptor_t *file_descriptor = NULL; // File descriptor and file system map
|
||||
static size_t max_dir_descriptor = 0; // Dir descriptor current maximum value
|
||||
static dir_descriptor_t *dir_descriptor = NULL; // Dir descriptor and file system map
|
||||
|
||||
static int _error_remap(int err)
|
||||
{
|
||||
if (err >= 0)
|
||||
{
|
||||
errno = 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
errno = -err;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static const char *remove_prefix(const char *str, const char *prefix)
|
||||
{
|
||||
size_t len_prefix = strlen(prefix);
|
||||
size_t len_str = strlen(str);
|
||||
|
||||
if (len_str < len_prefix || strncmp(str, prefix, len_prefix) != 0)
|
||||
{
|
||||
return str;
|
||||
}
|
||||
return str + len_prefix;
|
||||
}
|
||||
|
||||
static mountpoint_t *find_mountpoint(const char *path)
|
||||
{
|
||||
mountpoint_t *longest_match = NULL;
|
||||
size_t longest_length = 0;
|
||||
|
||||
for (size_t i = 0; i < FS_MAX_MOUNTPOINT; i++)
|
||||
{
|
||||
if (mountpoints[i].dir == NULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
size_t prefix_length = strlen(mountpoints[i].dir);
|
||||
if (prefix_length > longest_length && strncmp(path, mountpoints[i].dir, prefix_length) == 0)
|
||||
{
|
||||
longest_match = &mountpoints[i];
|
||||
longest_length = prefix_length;
|
||||
}
|
||||
}
|
||||
return longest_match;
|
||||
}
|
||||
|
||||
static bool is_valid_file_descriptor(int fildes)
|
||||
{
|
||||
if (fildes <= STDIO_FILNO_MAX || (int)max_file_descriptor <= FILENO_INDEX(fildes))
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
int fs_format(filesystem_t *fs, blockdevice_t *device)
|
||||
{
|
||||
if (!device->is_initialized)
|
||||
{
|
||||
int err = device->init(device);
|
||||
if (err != BD_ERROR_OK)
|
||||
{
|
||||
return _error_remap(err);
|
||||
}
|
||||
}
|
||||
return fs->format(fs, device);
|
||||
}
|
||||
|
||||
int fs_mount(const char *dir, filesystem_t *fs, blockdevice_t *device)
|
||||
{
|
||||
if (!device->is_initialized)
|
||||
{
|
||||
int err = device->init(device);
|
||||
if (err)
|
||||
return _error_remap(err);
|
||||
}
|
||||
int err = fs->mount(fs, device, false);
|
||||
if (err)
|
||||
{
|
||||
return _error_remap(err);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < FS_MAX_MOUNTPOINT; i++)
|
||||
{
|
||||
if (mountpoints[i].filesystem == NULL)
|
||||
{
|
||||
mountpoints[i].filesystem = fs;
|
||||
mountpoints[i].device = device;
|
||||
mountpoints[i].dir = VFS_strdup(dir);
|
||||
return _error_remap(0);
|
||||
}
|
||||
}
|
||||
return _error_remap(-EFAULT);
|
||||
}
|
||||
|
||||
int fs_unmount(const char *path)
|
||||
{
|
||||
|
||||
mountpoint_t *mp = find_mountpoint(path);
|
||||
if (mp == NULL)
|
||||
{
|
||||
return _error_remap(-ENOENT);
|
||||
}
|
||||
filesystem_t *fs = mp->filesystem;
|
||||
int err = fs->unmount(fs);
|
||||
if (err)
|
||||
{
|
||||
return _error_remap(err);
|
||||
}
|
||||
|
||||
mp->filesystem = NULL;
|
||||
mp->device = NULL;
|
||||
VFS_free(mp->dir);
|
||||
return _error_remap(0);
|
||||
}
|
||||
|
||||
int fs_reformat(const char *path)
|
||||
{
|
||||
|
||||
mountpoint_t *mp = find_mountpoint(path);
|
||||
if (mp == NULL)
|
||||
{
|
||||
return _error_remap(-ENOENT);
|
||||
}
|
||||
filesystem_t *fs = mp->filesystem;
|
||||
blockdevice_t *device = mp->device;
|
||||
|
||||
int err = fs->unmount(fs);
|
||||
if (err)
|
||||
{
|
||||
return _error_remap(err);
|
||||
}
|
||||
err = fs->format(fs, device);
|
||||
if (err)
|
||||
{
|
||||
return _error_remap(err);
|
||||
}
|
||||
err = fs->mount(fs, device, false);
|
||||
|
||||
return _error_remap(err);
|
||||
}
|
||||
|
||||
int fs_info(const char *path, filesystem_t **fs, blockdevice_t **device)
|
||||
{
|
||||
(void)fs;
|
||||
(void)device;
|
||||
|
||||
mountpoint_t *mp = find_mountpoint(path);
|
||||
if (mp == NULL)
|
||||
{
|
||||
return _error_remap(-ENOENT);
|
||||
}
|
||||
*fs = mp->filesystem;
|
||||
*device = mp->device;
|
||||
return _error_remap(0);
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
|
||||
|
||||
|
||||
int _fstat(int fildes, struct stat *st) {
|
||||
auto_init_recursive_mutex(_mutex);
|
||||
recursive_mutex_enter_blocking(&_mutex);
|
||||
|
||||
if (fildes == STDIN_FILENO || fildes == STDOUT_FILENO || fildes == STDERR_FILENO) {
|
||||
recursive_mutex_exit(&_mutex);
|
||||
st->st_size = 0;
|
||||
st->st_mode = S_IFCHR;
|
||||
return _error_remap(0);
|
||||
}
|
||||
if (!is_valid_file_descriptor(fildes)) {
|
||||
recursive_mutex_exit(&_mutex);
|
||||
return _error_remap(-EBADF);
|
||||
}
|
||||
|
||||
fs_file_t *file = file_descriptor[FILENO_INDEX(fildes)].file;
|
||||
filesystem_t *fs = file_descriptor[FILENO_INDEX(fildes)].filesystem;
|
||||
if (fs == NULL) {
|
||||
recursive_mutex_exit(&_mutex);
|
||||
return _error_remap(-EBADF);
|
||||
}
|
||||
|
||||
off_t size = 0;
|
||||
if (fs->type != FILESYSTEM_TYPE_FAT) {
|
||||
off_t current = fs->file_tell(fs, file);
|
||||
size = fs->file_seek(fs, file, 0, SEEK_END);
|
||||
off_t err = fs->file_seek(fs, file, current, SEEK_SET);
|
||||
if (current != err) {
|
||||
recursive_mutex_exit(&_mutex);
|
||||
return _error_remap(err);
|
||||
}
|
||||
} else {
|
||||
/* NOTE: Support for different behaviour of FatFs from POSIX
|
||||
*
|
||||
* FatFs has a problem where f_size() reports a larger than actual file size when
|
||||
* moved to a position beyond the f_lseek() file size; POSIX reports the actual size
|
||||
* written to the file, not the seek position.
|
||||
*/
|
||||
const char *path = file_descriptor[FILENO_INDEX(fildes)].path;
|
||||
mountpoint_t *mp = find_mountpoint(path);
|
||||
if (mp == NULL) {
|
||||
recursive_mutex_exit(&_mutex);
|
||||
return _error_remap(-ENOENT);
|
||||
}
|
||||
const char *entity_path = remove_prefix(path, mp->dir);
|
||||
filesystem_t *fs = mp->filesystem;
|
||||
|
||||
struct stat finfo = {0};
|
||||
int err = fs->stat(fs, entity_path, &finfo);
|
||||
if (err != 0) {
|
||||
recursive_mutex_exit(&_mutex);
|
||||
return _error_remap(err);
|
||||
}
|
||||
size = finfo.st_size;
|
||||
}
|
||||
recursive_mutex_exit(&_mutex);
|
||||
|
||||
st->st_size = size;
|
||||
st->st_mode = S_IFREG;
|
||||
return _error_remap(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
char *fs_strerror(int errnum)
|
||||
{
|
||||
if (errnum > 5000)
|
||||
{
|
||||
// SD blockdevice error
|
||||
const char *str = "";
|
||||
switch (errnum)
|
||||
{
|
||||
case 5001:
|
||||
str = "operation would block";
|
||||
break;
|
||||
case 5002:
|
||||
str = "unsupported operation";
|
||||
break;
|
||||
case 5003:
|
||||
str = "invalid parameter";
|
||||
break;
|
||||
case 5004:
|
||||
str = "uninitialized";
|
||||
break;
|
||||
case 5005:
|
||||
str = "device is missing or not connected";
|
||||
break;
|
||||
case 5006:
|
||||
str = "write protected";
|
||||
break;
|
||||
case 5007:
|
||||
str = "unusable card";
|
||||
break;
|
||||
case 5008:
|
||||
str = "No response from device";
|
||||
break;
|
||||
case 5009:
|
||||
str = "CRC error";
|
||||
break;
|
||||
case 5010:
|
||||
str = "Erase error: reset/sequence";
|
||||
break;
|
||||
case 5011:
|
||||
str = "Write error: !SPI_DATA_ACCEPTED";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return (char *)str;
|
||||
}
|
||||
else if (errnum > 4000)
|
||||
{
|
||||
// On-board flash blockdevice error
|
||||
const char *str = "";
|
||||
switch (errnum)
|
||||
{
|
||||
case 4001:
|
||||
str = "operation timeout";
|
||||
break;
|
||||
case 4002:
|
||||
str = "safe execution is not possible";
|
||||
break;
|
||||
case 4003:
|
||||
str = "method fails due to dynamic resource exhaustion";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return (char *)str;
|
||||
}
|
||||
else
|
||||
{
|
||||
return strerror(errnum);
|
||||
}
|
||||
}
|
||||
|
||||
static int _assign_file_descriptor()
|
||||
{
|
||||
int fd = -1;
|
||||
if (max_file_descriptor == 0)
|
||||
{
|
||||
max_file_descriptor = 2;
|
||||
file_descriptor = VFS_calloc(max_file_descriptor, sizeof(file_descriptor_t));
|
||||
if (file_descriptor == NULL)
|
||||
{
|
||||
printf("_open: Out of memory\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < max_file_descriptor; i++)
|
||||
{
|
||||
if (file_descriptor[i].filesystem == NULL)
|
||||
{
|
||||
fd = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (fd == -1)
|
||||
{
|
||||
// Extend the management array
|
||||
size_t last_max = max_file_descriptor;
|
||||
max_file_descriptor *= 2;
|
||||
file_descriptor = realloc(file_descriptor, sizeof(file_descriptor_t) * max_file_descriptor);
|
||||
if (file_descriptor == NULL)
|
||||
{
|
||||
printf("_open: Out of memory\n");
|
||||
return -1;
|
||||
}
|
||||
for (size_t i = last_max; i < max_file_descriptor; i++)
|
||||
{
|
||||
file_descriptor[i].filesystem = NULL;
|
||||
file_descriptor[i].file = NULL;
|
||||
}
|
||||
fd = last_max;
|
||||
}
|
||||
return FILENO_VALUE(fd);
|
||||
}
|
||||
|
||||
int VFS_open(const char *path, int oflags, ...)
|
||||
{
|
||||
|
||||
mountpoint_t *mp = find_mountpoint(path);
|
||||
if (mp == NULL)
|
||||
{
|
||||
return _error_remap(-ENOENT);
|
||||
}
|
||||
const char *entity_path = remove_prefix(path, mp->dir);
|
||||
// find file descriptor
|
||||
int fd = _assign_file_descriptor();
|
||||
if (fd == -1)
|
||||
{
|
||||
return _error_remap(-ENFILE);
|
||||
}
|
||||
|
||||
filesystem_t *fs = mp->filesystem;
|
||||
fs_file_t *file = file_descriptor[FILENO_INDEX(fd)].file = VFS_calloc(1, sizeof(fs_file_t));
|
||||
if (file == NULL)
|
||||
{
|
||||
return _error_remap(-ENOMEM);
|
||||
}
|
||||
int err = fs->file_open(fs, file, entity_path, oflags);
|
||||
if (err < 0)
|
||||
{
|
||||
VFS_free(file);
|
||||
file_descriptor[FILENO_INDEX(fd)].file = NULL;
|
||||
return _error_remap(err);
|
||||
}
|
||||
file_descriptor[FILENO_INDEX(fd)].filesystem = fs;
|
||||
strncpy(file_descriptor[FILENO_INDEX(fd)].path, path, PATH_MAX1);
|
||||
return _error_remap(fd);
|
||||
}
|
||||
|
||||
ssize_t VFS_read(int fildes, void *buf, size_t nbyte)
|
||||
{
|
||||
|
||||
if (!is_valid_file_descriptor(fildes))
|
||||
{
|
||||
return _error_remap(-EBADF);
|
||||
}
|
||||
fs_file_t *file = file_descriptor[FILENO_INDEX(fildes)].file;
|
||||
filesystem_t *fs = file_descriptor[FILENO_INDEX(fildes)].filesystem;
|
||||
if (fs == NULL)
|
||||
{
|
||||
return _error_remap(-EBADF);
|
||||
}
|
||||
ssize_t size = fs->file_read(fs, file, buf, nbyte);
|
||||
|
||||
return _error_remap(size);
|
||||
}
|
||||
|
||||
ssize_t VFS_write(int fildes, const void *buf, size_t nbyte)
|
||||
{
|
||||
if (!is_valid_file_descriptor(fildes))
|
||||
{
|
||||
return _error_remap(-EBADF);
|
||||
}
|
||||
fs_file_t *file = file_descriptor[FILENO_INDEX(fildes)].file;
|
||||
filesystem_t *fs = file_descriptor[FILENO_INDEX(fildes)].filesystem;
|
||||
if (fs == NULL)
|
||||
{
|
||||
return _error_remap(-EBADF);
|
||||
}
|
||||
ssize_t size = fs->file_write(fs, file, buf, nbyte);
|
||||
return _error_remap(size);
|
||||
}
|
||||
|
||||
int VFS_close(int fildes)
|
||||
{
|
||||
if (!is_valid_file_descriptor(fildes))
|
||||
{
|
||||
printf("_close error fildes=%d\n", fildes);
|
||||
return _error_remap(-EBADF);
|
||||
}
|
||||
fs_file_t *file = file_descriptor[FILENO_INDEX(fildes)].file;
|
||||
filesystem_t *fs = file_descriptor[FILENO_INDEX(fildes)].filesystem;
|
||||
if (fs == NULL)
|
||||
{
|
||||
return _error_remap(-EBADF);
|
||||
}
|
||||
int err = fs->file_close(fs, file);
|
||||
VFS_free(file);
|
||||
file_descriptor[FILENO_INDEX(fildes)].filesystem = NULL;
|
||||
file_descriptor[FILENO_INDEX(fildes)].file = NULL;
|
||||
file_descriptor[FILENO_INDEX(fildes)].path[0] = '\0';
|
||||
return _error_remap(err);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int32_t VFS_seek(int fildes, int32_t offset, int whence)
|
||||
{
|
||||
if (!is_valid_file_descriptor(fildes))
|
||||
{
|
||||
return _error_remap(-EBADF);
|
||||
}
|
||||
fs_file_t *file = file_descriptor[FILENO_INDEX(fildes)].file;
|
||||
filesystem_t *fs = file_descriptor[FILENO_INDEX(fildes)].filesystem;
|
||||
if (fs == NULL)
|
||||
{
|
||||
return _error_remap(-EBADF);
|
||||
}
|
||||
|
||||
int32_t pos = fs->file_seek(fs, file, offset, whence);
|
||||
return _error_remap(pos);
|
||||
}
|
||||
|
||||
|
||||
int32_t VFS_fsize(int fildes)
|
||||
{
|
||||
if (!is_valid_file_descriptor(fildes))
|
||||
{
|
||||
return _error_remap(-EBADF);
|
||||
}
|
||||
fs_file_t *file = file_descriptor[FILENO_INDEX(fildes)].file;
|
||||
filesystem_t *fs = file_descriptor[FILENO_INDEX(fildes)].filesystem;
|
||||
if (fs == NULL)
|
||||
{
|
||||
return _error_remap(-EBADF);
|
||||
}
|
||||
|
||||
int32_t filesize = fs->file_size(fs, file);
|
||||
return _error_remap(filesize);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int VFS_stat(const char *path, struct VFS_stat *st)
|
||||
{
|
||||
mountpoint_t *mp = find_mountpoint(path);
|
||||
if (mp == NULL)
|
||||
{
|
||||
return _error_remap(-ENOENT);
|
||||
}
|
||||
const char *entity_path = remove_prefix(path, mp->dir);
|
||||
filesystem_t *fs = mp->filesystem;
|
||||
int err = fs->stat(fs, entity_path, st);
|
||||
return _error_remap(err);
|
||||
}
|
||||
|
||||
int VFS_ftruncate(int fildes, int32_t length)
|
||||
{
|
||||
if (!is_valid_file_descriptor(fildes))
|
||||
{
|
||||
return _error_remap(-EBADF);
|
||||
}
|
||||
fs_file_t *file = file_descriptor[FILENO_INDEX(fildes)].file;
|
||||
filesystem_t *fs = file_descriptor[FILENO_INDEX(fildes)].filesystem;
|
||||
if (fs == NULL)
|
||||
{
|
||||
return _error_remap(-EBADF);
|
||||
}
|
||||
int err = fs->file_truncate(fs, file, length);
|
||||
return _error_remap(err);
|
||||
}
|
||||
|
||||
int VFS_mkdir(const char *path, mode_t mode)
|
||||
{
|
||||
mountpoint_t *mp = find_mountpoint(path);
|
||||
if (mp == NULL)
|
||||
{
|
||||
return _error_remap(-ENOENT);
|
||||
}
|
||||
const char *entity_path = remove_prefix(path, mp->dir);
|
||||
filesystem_t *fs = mp->filesystem;
|
||||
int err = fs->mkdir(fs, entity_path, mode);
|
||||
return _error_remap(err);
|
||||
}
|
||||
|
||||
static int _assign_dir_descriptor(void)
|
||||
{
|
||||
int fd = -1;
|
||||
if (max_dir_descriptor == 0)
|
||||
{
|
||||
max_dir_descriptor = 2;
|
||||
dir_descriptor = VFS_calloc(max_dir_descriptor, sizeof(dir_descriptor_t));
|
||||
if (dir_descriptor == NULL)
|
||||
{
|
||||
printf("_opendir: Out of memory\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < max_dir_descriptor; i++)
|
||||
{
|
||||
if (dir_descriptor[i].filesystem == NULL)
|
||||
{
|
||||
fd = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (fd == -1)
|
||||
{
|
||||
// Extend the management array
|
||||
size_t last_max = max_dir_descriptor;
|
||||
max_dir_descriptor *= 2;
|
||||
dir_descriptor = realloc(dir_descriptor, sizeof(dir_descriptor_t) * max_dir_descriptor);
|
||||
if (dir_descriptor == NULL)
|
||||
{
|
||||
printf("_opendir: Out of memory\n");
|
||||
return -1;
|
||||
}
|
||||
for (size_t i = last_max; i < max_dir_descriptor; i++)
|
||||
{
|
||||
dir_descriptor[i].filesystem = NULL;
|
||||
dir_descriptor[i].dir = NULL;
|
||||
}
|
||||
fd = (int)last_max;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
fs_dir_t *VFS_opendir(const char *path)
|
||||
{
|
||||
|
||||
mountpoint_t *mp = find_mountpoint(path);
|
||||
if (mp == NULL)
|
||||
{
|
||||
_error_remap(-ENOENT);
|
||||
return NULL;
|
||||
}
|
||||
const char *entity_path = remove_prefix(path, mp->dir);
|
||||
// find dir descriptor
|
||||
int fd = _assign_dir_descriptor();
|
||||
if (fd == -1)
|
||||
{
|
||||
_error_remap(-ENFILE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fs_dir_t *dir = dir_descriptor[fd].dir = VFS_calloc(1, sizeof(fs_dir_t));
|
||||
if (dir == NULL)
|
||||
{
|
||||
_error_remap(-ENOMEM);
|
||||
return NULL;
|
||||
}
|
||||
filesystem_t *fs = mp->filesystem;
|
||||
int err = fs->dir_open(fs, dir, entity_path);
|
||||
if (err != 0)
|
||||
{
|
||||
VFS_free(dir);
|
||||
dir_descriptor[fd].dir = NULL;
|
||||
_error_remap(err);
|
||||
return NULL;
|
||||
}
|
||||
dir_descriptor[fd].filesystem = fs;
|
||||
dir->fd = fd;
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
struct dirent *VFS_readdir(fs_dir_t *dir)
|
||||
{
|
||||
fs_dir_t *_dir = dir_descriptor[dir->fd].dir;
|
||||
filesystem_t *fs = dir_descriptor[dir->fd].filesystem;
|
||||
if (fs == NULL)
|
||||
{
|
||||
_error_remap(-EBADF);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
VFS_memset(&_dir->current, 0, sizeof(_dir->current));
|
||||
int err = fs->dir_read(fs, _dir, &_dir->current);
|
||||
if (err == 0)
|
||||
{
|
||||
|
||||
return &_dir->current;
|
||||
}
|
||||
else if (err == -ENOENT)
|
||||
{
|
||||
VFS_memset(&_dir->current, 0, sizeof(_dir->current));
|
||||
_error_remap(0);
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
VFS_memset(&_dir->current, 0, sizeof(_dir->current));
|
||||
_error_remap(err);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int VFS_closedir(fs_dir_t *dir)
|
||||
{
|
||||
int fd = dir->fd;
|
||||
fs_dir_t *_dir = dir_descriptor[dir->fd].dir;
|
||||
filesystem_t *fs = dir_descriptor[dir->fd].filesystem;
|
||||
if (fs == NULL)
|
||||
{
|
||||
return _error_remap(-EBADF);
|
||||
}
|
||||
int err = fs->dir_close(fs, _dir);
|
||||
dir_descriptor[fd].filesystem = NULL;
|
||||
VFS_free(dir_descriptor[fd].dir);
|
||||
dir_descriptor[fd].dir = NULL;
|
||||
return _error_remap(err);
|
||||
}
|
||||
|
||||
int VFS_rmdir(const char *path)
|
||||
{
|
||||
mountpoint_t *mp = find_mountpoint(path);
|
||||
if (mp == NULL)
|
||||
{
|
||||
return _error_remap(-ENOENT);
|
||||
}
|
||||
const char *entity_path = remove_prefix(path, mp->dir);
|
||||
printf("entity_path:%s\n", entity_path);
|
||||
filesystem_t *fs = mp->filesystem;
|
||||
int err = fs->rmdir(fs, entity_path);
|
||||
return _error_remap(err);
|
||||
}
|
||||
|
||||
int VFS_rename(const char *old, const char *new)
|
||||
{
|
||||
mountpoint_t *mp = find_mountpoint(old);
|
||||
if (mp == NULL)
|
||||
{
|
||||
return _error_remap(-ENOENT);
|
||||
}
|
||||
const char *old_entity_path = remove_prefix(old, mp->dir);
|
||||
const char *new_entity_path = remove_prefix(new, mp->dir);
|
||||
|
||||
filesystem_t *fs = mp->filesystem;
|
||||
int err = fs->rename(fs, old_entity_path, new_entity_path);
|
||||
return _error_remap(err);
|
||||
}
|
||||
|
||||
int VFS_unlink(const char *path)
|
||||
{
|
||||
|
||||
mountpoint_t *mp = find_mountpoint(path);
|
||||
if (mp == NULL)
|
||||
{
|
||||
return _error_remap(-ENOENT);
|
||||
}
|
||||
const char *entity_path = remove_prefix(path, mp->dir);
|
||||
filesystem_t *fs = mp->filesystem;
|
||||
int err = fs->remove(fs, entity_path);
|
||||
return _error_remap(err);
|
||||
}
|
||||
|
||||
128
sdk/lib/VFS/vfs.h
Normal file
128
sdk/lib/VFS/vfs.h
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright 2024, Hiroyuki OYAMA
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
#ifndef VFS_H
|
||||
#define VFS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//#include <fcntl.h>
|
||||
//#include <sys/types.h>
|
||||
#include "osal/string.h"
|
||||
#include "filesystem.h"
|
||||
#include "blockdevice.h"
|
||||
|
||||
#define VFS_malloc os_malloc
|
||||
#define VFS_calloc os_calloc
|
||||
#define VFS_free os_free
|
||||
#define VFS_memset hw_memset
|
||||
#define VFS_memcpy hw_memcpy
|
||||
#define VFS_strcpy os_strcpy
|
||||
#define VFS_strdup os_strdup
|
||||
#define VFS_strncpy os_strncpy
|
||||
|
||||
|
||||
|
||||
/*! \brief Enable predefined file systems
|
||||
* \ingroup filesystem
|
||||
*
|
||||
* This default function defines the block device and file system, formats it if necessary and then mounts it on `/` to make it available.
|
||||
* The `pico_enable_filesystem` function in CMakeLists.txt provides a default or user-defined fs_init function.
|
||||
*
|
||||
* \retval true Init succeeded.
|
||||
* \retval false Init failed.
|
||||
*/
|
||||
bool fs_init(void);
|
||||
|
||||
/*! \brief Format block device with specify file system
|
||||
* \ingroup filesystem
|
||||
*
|
||||
* Block devices can be formatted and made available as a file system.
|
||||
*
|
||||
* \param fs File system object. Format the block device according to the specified file system.
|
||||
* \param device Block device used in the file system.
|
||||
* \retval 0 Format succeeded.
|
||||
* \retval -1 Format failed. Error codes are indicated by errno.
|
||||
*/
|
||||
int fs_format(filesystem_t *fs, blockdevice_t *device);
|
||||
|
||||
/*! \brief Mount a file system
|
||||
* \ingroup filesystem
|
||||
*
|
||||
* Mounts a file system with block devices at the specified path.
|
||||
*
|
||||
* \param path Directory path of the mount point. Specify a string beginning with a slash.
|
||||
* \param fs File system object.
|
||||
* \param device Block device used in the file system. Block devices must be formatted with a file system.
|
||||
* \retval 0 Mount succeeded.
|
||||
* \retval -1 Mount failed. Error codes are indicated by errno.
|
||||
*/
|
||||
int fs_mount(const char *path, filesystem_t *fs, blockdevice_t *device);
|
||||
|
||||
/*! \brief Dismount a file system
|
||||
* \ingroup filesystem
|
||||
*
|
||||
* Dismount a file system.
|
||||
*
|
||||
* \param path Directory path of the mount point. Must be the same as the path specified for the mount.
|
||||
* \retval 0 Dismount succeeded.
|
||||
* \retval -1 Dismount failed. Error codes are indicated by errno.
|
||||
*/
|
||||
int fs_unmount(const char *path);
|
||||
|
||||
/*! \brief Reformat the mounted file system
|
||||
* \ingroup filesystem
|
||||
*
|
||||
* Reformat a file system mounted at the specified path.
|
||||
*
|
||||
* \param path Directory path of the mount point. Must be the same as the path specified for the mount.
|
||||
* \retval 0 Reformat suceeded.
|
||||
* \retval -1 Reformat failed. Error codes are indicated by errno.
|
||||
*/
|
||||
int fs_reformat(const char *path);
|
||||
|
||||
/*! \brief Lookup filesystem and blockdevice objects from a mount point
|
||||
* \ingroup filesystem
|
||||
*
|
||||
* \param path Directory path of the mount point. Must be the same as the path specified for the mount.
|
||||
* \param fs Pointer references to filesystem objects
|
||||
* \param device Pinter references to blockdevice objects
|
||||
* \retval 0 Lookup succeeded
|
||||
* \retval -1 Lookup failed. Error codes are indicated by errno.
|
||||
*/
|
||||
int fs_info(const char *path, filesystem_t **fs, blockdevice_t **device);
|
||||
|
||||
/*! \brief File system error message
|
||||
* \ingroup filesystem
|
||||
*
|
||||
* Convert the error code reported in the negative integer into a string.
|
||||
*
|
||||
* \param error Negative error code returned by the file system.
|
||||
* \return Pointer to the corresponding message string.
|
||||
*/
|
||||
char *fs_strerror(int error);
|
||||
|
||||
ssize_t VFS_read(int fildes, void *buf, size_t nbyte);
|
||||
ssize_t VFS_write(int fildes, const void *buf, size_t nbyte);
|
||||
int VFS_close(int fildes);
|
||||
int VFS_open(const char *path, int oflags, ...);
|
||||
int32_t VFS_seek(int fildes, int32_t offset, int whence);
|
||||
int32_t VFS_fsize(int fildes);
|
||||
int VFS_stat(const char *path, struct VFS_stat *st);
|
||||
int VFS_ftruncate(int fildes, int32_t length);
|
||||
int VFS_mkdir(const char *path, mode_t mode) ;
|
||||
fs_dir_t *VFS_opendir(const char *path);
|
||||
struct dirent *VFS_readdir(fs_dir_t *dir);
|
||||
int VFS_closedir(fs_dir_t *dir);
|
||||
int VFS_rmdir(const char *path);
|
||||
int VFS_rename(const char *old, const char *new);
|
||||
int VFS_unlink(const char *path);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
143
sdk/lib/VFS/vfs_sd.c
Normal file
143
sdk/lib/VFS/vfs_sd.c
Normal file
@@ -0,0 +1,143 @@
|
||||
#include "sys_config.h"
|
||||
|
||||
#include "integer.h"
|
||||
#include "diskio.h"
|
||||
#include "ff.h"
|
||||
#include <stdio.h>
|
||||
#include "osal/sleep.h"
|
||||
#include "typesdef.h"
|
||||
#include "osal/task.h"
|
||||
#include "osal/semaphore.h"
|
||||
#include "osal/mutex.h"
|
||||
#include "list.h"
|
||||
#include "dev.h"
|
||||
#include "sdhost.h"
|
||||
#include "devid.h"
|
||||
#include "vfs.h"
|
||||
typedef struct
|
||||
{
|
||||
void *dev;
|
||||
} sd_config_t;
|
||||
|
||||
static const char DEVICE_NAME[] = "sd";
|
||||
|
||||
|
||||
|
||||
static int init(blockdevice_t *device)
|
||||
{
|
||||
(void)device;
|
||||
int ret = BD_ERROR_OK;
|
||||
|
||||
if (device->is_initialized)
|
||||
{
|
||||
return BD_ERROR_OK;
|
||||
}
|
||||
uint32_t err = sdhost_init(48*1000*1000, 0);
|
||||
if (err != 0)
|
||||
{
|
||||
return BD_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
device->is_initialized = true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int deinit(blockdevice_t *device)
|
||||
{
|
||||
|
||||
|
||||
if (!device->is_initialized)
|
||||
{
|
||||
return BD_ERROR_OK;
|
||||
}
|
||||
device->is_initialized = false;
|
||||
return BD_ERROR_OK;
|
||||
}
|
||||
|
||||
static int sync(blockdevice_t *device)
|
||||
{
|
||||
(void)device;
|
||||
return BD_ERROR_OK;
|
||||
}
|
||||
|
||||
static int read(blockdevice_t *device, const void *buffer, bd_size_t addr, bd_size_t length)
|
||||
{
|
||||
sd_config_t *config = device->config;
|
||||
DWORD sector = (DWORD)(addr / device->erase_size);
|
||||
DWORD count = (DWORD)(length / device->erase_size);
|
||||
int err = sd_multiple_read((struct sdh_device*)config->dev,sector,count*512,(uint8_t*)buffer);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int erase(blockdevice_t *device, bd_size_t addr, bd_size_t length)
|
||||
{
|
||||
(void)device;
|
||||
(void)addr;
|
||||
(void)length;
|
||||
return BD_ERROR_OK;
|
||||
}
|
||||
|
||||
static int program(blockdevice_t *device, const void *buffer, bd_size_t addr, bd_size_t length)
|
||||
{
|
||||
sd_config_t *config = device->config;
|
||||
DWORD sector = (DWORD)(addr / device->erase_size);
|
||||
DWORD count = (DWORD)(length / device->erase_size);
|
||||
|
||||
int err = sd_multiple_write((struct sdh_device*)config->dev,sector,count*512,(uint8_t*)buffer);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int trim(blockdevice_t *device, bd_size_t addr, bd_size_t length)
|
||||
{
|
||||
(void)device;
|
||||
(void)addr;
|
||||
(void)length;
|
||||
return BD_ERROR_OK;
|
||||
}
|
||||
|
||||
static bd_size_t size(blockdevice_t *device)
|
||||
{
|
||||
//sd_config_t *config = device->config;
|
||||
extern unsigned int sd_dwCap;
|
||||
return (bd_size_t)sd_dwCap*1024;
|
||||
}
|
||||
|
||||
blockdevice_t *blockdevice_sd_create()
|
||||
{
|
||||
|
||||
blockdevice_t *device = VFS_calloc(1, sizeof(blockdevice_t));
|
||||
if (device == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
sd_config_t *config = calloc(1, sizeof(sd_config_t));
|
||||
if (config == NULL)
|
||||
{
|
||||
VFS_free(device);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
device->init = init;
|
||||
device->deinit = deinit;
|
||||
device->read = read;
|
||||
device->erase = erase;
|
||||
device->program = program;
|
||||
device->trim = trim;
|
||||
device->sync = sync;
|
||||
device->size = size;
|
||||
device->read_size = 512;
|
||||
device->erase_size = 512;
|
||||
device->program_size = 512;
|
||||
device->name = DEVICE_NAME;
|
||||
device->is_initialized = false;
|
||||
device->config = config;
|
||||
config->dev = (struct sdh_device *)dev_get(HG_SDIOHOST_DEVID);
|
||||
device->init(device);
|
||||
return device;
|
||||
}
|
||||
|
||||
void blockdevice_sd_free(blockdevice_t *device)
|
||||
{
|
||||
device->deinit(device);
|
||||
VFS_free(device->config);
|
||||
VFS_free(device);
|
||||
}
|
||||
Reference in New Issue
Block a user