Initial commit: TXW82x FPV v2.7.0.7-42229 SDK + project sources

This commit is contained in:
2026-07-06 11:30:13 +08:00
commit e76462eeb7
3451 changed files with 1415300 additions and 0 deletions

55
sdk/lib/VFS/blockdevice.h Normal file
View 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

View 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;
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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);
}