mirror of
https://github.com/nodemcu/nodemcu-firmware.git
synced 2025-01-16 20:52:57 +08:00
526d21dab4
The PR removed the bulk of non-newlib headers from the NodeMCU source base. app/libc has now been cut down to the bare minimum overrides to shadow the corresponding functions in the SDK's libc. The old c_xyz.h headerfiles have been nuked in favour of the standard <xyz.h> headers, with a few exceptions over in sdk-overrides. Again, shipping a libc.a without headers is a terrible thing to do. We're still living on a prayer that libc was configured the same was as a default-configured xtensa gcc toolchain assumes it is. That part I cannot do anything about, unfortunately, but it's no worse than it has been before. This enables our source files to compile successfully using the standard header files, and use the typical malloc()/calloc()/realloc()/free(), the strwhatever()s and memwhatever()s. These end up, through macro and linker magic, mapped to the appropriate SDK or ROM functions.
541 lines
13 KiB
C
541 lines
13 KiB
C
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdbool.h>
|
|
|
|
#include "vfs_int.h"
|
|
|
|
#include "fatfs_prefix_lib.h"
|
|
#include "ff.h"
|
|
#include "fatfs_config.h"
|
|
|
|
|
|
static FRESULT last_result = FR_OK;
|
|
|
|
static const char* const volstr[FF_VOLUMES] = {FF_VOLUME_STRS};
|
|
|
|
static int is_current_drive = false;
|
|
|
|
|
|
// forward declarations
|
|
static int32_t myfatfs_close( const struct vfs_file *fd );
|
|
static int32_t myfatfs_read( const struct vfs_file *fd, void *ptr, size_t len );
|
|
static int32_t myfatfs_write( const struct vfs_file *fd, const void *ptr, size_t len );
|
|
static int32_t myfatfs_lseek( const struct vfs_file *fd, int32_t off, int whence );
|
|
static int32_t myfatfs_eof( const struct vfs_file *fd );
|
|
static int32_t myfatfs_tell( const struct vfs_file *fd );
|
|
static int32_t myfatfs_flush( const struct vfs_file *fd );
|
|
static uint32_t myfatfs_fsize( const struct vfs_file *fd );
|
|
static int32_t myfatfs_ferrno( const struct vfs_file *fd );
|
|
|
|
static int32_t myfatfs_closedir( const struct vfs_dir *dd );
|
|
static int32_t myfatfs_readdir( const struct vfs_dir *dd, struct vfs_stat *buf );
|
|
|
|
static vfs_vol *myfatfs_mount( const char *name, int num );
|
|
static vfs_file *myfatfs_open( const char *name, const char *mode );
|
|
static vfs_dir *myfatfs_opendir( const char *name );
|
|
static int32_t myfatfs_stat( const char *name, struct vfs_stat *buf );
|
|
static int32_t myfatfs_remove( const char *name );
|
|
static int32_t myfatfs_rename( const char *oldname, const char *newname );
|
|
static int32_t myfatfs_mkdir( const char *name );
|
|
static int32_t myfatfs_fsinfo( uint32_t *total, uint32_t *used );
|
|
static int32_t myfatfs_chdrive( const char *name );
|
|
static int32_t myfatfs_chdir( const char *name );
|
|
static int32_t myfatfs_errno( void );
|
|
static void myfatfs_clearerr( void );
|
|
|
|
static int32_t myfatfs_umount( const struct vfs_vol *vol );
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// function tables
|
|
//
|
|
static vfs_fs_fns myfatfs_fs_fns = {
|
|
.mount = myfatfs_mount,
|
|
.open = myfatfs_open,
|
|
.opendir = myfatfs_opendir,
|
|
.stat = myfatfs_stat,
|
|
.remove = myfatfs_remove,
|
|
.rename = myfatfs_rename,
|
|
.mkdir = myfatfs_mkdir,
|
|
.fsinfo = myfatfs_fsinfo,
|
|
.fscfg = NULL,
|
|
.format = NULL,
|
|
.chdrive = myfatfs_chdrive,
|
|
.chdir = myfatfs_chdir,
|
|
.ferrno = myfatfs_errno,
|
|
.clearerr = myfatfs_clearerr
|
|
};
|
|
|
|
static vfs_file_fns myfatfs_file_fns = {
|
|
.close = myfatfs_close,
|
|
.read = myfatfs_read,
|
|
.write = myfatfs_write,
|
|
.lseek = myfatfs_lseek,
|
|
.eof = myfatfs_eof,
|
|
.tell = myfatfs_tell,
|
|
.flush = myfatfs_flush,
|
|
.size = myfatfs_fsize,
|
|
.ferrno = myfatfs_ferrno
|
|
};
|
|
|
|
static vfs_dir_fns myfatfs_dir_fns = {
|
|
.close = myfatfs_closedir,
|
|
.readdir = myfatfs_readdir
|
|
};
|
|
|
|
static vfs_vol_fns myfatfs_vol_fns = {
|
|
.umount = myfatfs_umount
|
|
};
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// specific struct extensions
|
|
//
|
|
struct myvfs_vol {
|
|
struct vfs_vol vfs_vol;
|
|
char *ldrname;
|
|
FATFS fs;
|
|
};
|
|
|
|
struct myvfs_file {
|
|
struct vfs_file vfs_file;
|
|
FIL fp;
|
|
};
|
|
|
|
struct myvfs_dir {
|
|
struct vfs_dir vfs_dir;
|
|
DIR dp;
|
|
};
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// exported helper functions for FatFS
|
|
//
|
|
void *ff_memalloc( UINT size )
|
|
{
|
|
return malloc( size );
|
|
}
|
|
|
|
void ff_memfree( void *mblock )
|
|
{
|
|
free( mblock );
|
|
}
|
|
|
|
// TODO
|
|
DWORD get_fattime( void )
|
|
{
|
|
DWORD stamp;
|
|
vfs_time tm;
|
|
|
|
if (VFS_RES_OK == vfs_get_rtc( &tm )) {
|
|
// sanity checks
|
|
tm.year = (tm.year >= 1980) && (tm.year < 2108) ? tm.year : 1980;
|
|
tm.mon = (tm.mon >= 1) && (tm.mon <= 12) ? tm.mon : 1;
|
|
tm.day = (tm.day >= 1) && (tm.day <= 31) ? tm.day : 1;
|
|
tm.hour = (tm.hour >= 0) && (tm.hour <= 23) ? tm.hour : 0;
|
|
tm.min = (tm.min >= 0) && (tm.min <= 59) ? tm.min : 0;
|
|
tm.sec = (tm.sec >= 0) && (tm.sec <= 59) ? tm.sec : 0;
|
|
|
|
stamp = (tm.year-1980) << 25 | tm.mon << 21 | tm.day << 16 |
|
|
tm.hour << 11 | tm.min << 5 | tm.sec;
|
|
} else {
|
|
// default time stamp derived from ffconf.h
|
|
stamp = ((DWORD)(FF_NORTC_YEAR - 1980) << 25 | (DWORD)FF_NORTC_MON << 21 | (DWORD)FF_NORTC_MDAY << 16);
|
|
}
|
|
|
|
return stamp;
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// volume functions
|
|
//
|
|
#define GET_FATFS_FS(descr) \
|
|
const struct myvfs_vol *myvol = (const struct myvfs_vol *)descr; \
|
|
FATFS *fs = (FATFS *)&(myvol->fs);
|
|
|
|
static int32_t myfatfs_umount( const struct vfs_vol *vol )
|
|
{
|
|
GET_FATFS_FS(vol);
|
|
|
|
last_result = f_mount( NULL, myvol->ldrname, 0 );
|
|
|
|
free( myvol->ldrname );
|
|
free( (void *)vol );
|
|
|
|
return last_result == FR_OK ? VFS_RES_OK : VFS_RES_ERR;
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// file functions
|
|
//
|
|
#define GET_FIL_FP(descr) \
|
|
const struct myvfs_file *myfd = (const struct myvfs_file *)descr; \
|
|
FIL *fp = (FIL *)&(myfd->fp);
|
|
|
|
static int32_t myfatfs_close( const struct vfs_file *fd )
|
|
{
|
|
GET_FIL_FP(fd)
|
|
|
|
last_result = f_close( fp );
|
|
|
|
// free descriptor memory
|
|
free( (void *)fd );
|
|
|
|
return last_result == FR_OK ? VFS_RES_OK : VFS_RES_ERR;
|
|
}
|
|
|
|
static int32_t myfatfs_read( const struct vfs_file *fd, void *ptr, size_t len )
|
|
{
|
|
GET_FIL_FP(fd);
|
|
UINT act_read;
|
|
|
|
last_result = f_read( fp, ptr, len, &act_read );
|
|
|
|
return last_result == FR_OK ? act_read : VFS_RES_ERR;
|
|
}
|
|
|
|
static int32_t myfatfs_write( const struct vfs_file *fd, const void *ptr, size_t len )
|
|
{
|
|
GET_FIL_FP(fd);
|
|
UINT act_written;
|
|
|
|
last_result = f_write( fp, ptr, len, &act_written );
|
|
|
|
return last_result == FR_OK ? act_written : VFS_RES_ERR;
|
|
}
|
|
|
|
static int32_t myfatfs_lseek( const struct vfs_file *fd, int32_t off, int whence )
|
|
{
|
|
GET_FIL_FP(fd);
|
|
FSIZE_t new_pos;
|
|
|
|
switch (whence) {
|
|
default:
|
|
case VFS_SEEK_SET:
|
|
new_pos = off > 0 ? off : 0;
|
|
break;
|
|
case VFS_SEEK_CUR:
|
|
new_pos = f_tell( fp );
|
|
new_pos += off;
|
|
break;
|
|
case VFS_SEEK_END:
|
|
new_pos = f_size( fp );
|
|
new_pos += off < 0 ? off : 0;
|
|
break;
|
|
};
|
|
|
|
last_result = f_lseek( fp, new_pos );
|
|
new_pos = f_tell( fp );
|
|
|
|
return last_result == FR_OK ? new_pos : VFS_RES_ERR;
|
|
}
|
|
|
|
static int32_t myfatfs_eof( const struct vfs_file *fd )
|
|
{
|
|
GET_FIL_FP(fd);
|
|
|
|
last_result = FR_OK;
|
|
|
|
return f_eof( fp );
|
|
}
|
|
|
|
static int32_t myfatfs_tell( const struct vfs_file *fd )
|
|
{
|
|
GET_FIL_FP(fd);
|
|
|
|
last_result = FR_OK;
|
|
|
|
return f_tell( fp );
|
|
}
|
|
|
|
static int32_t myfatfs_flush( const struct vfs_file *fd )
|
|
{
|
|
GET_FIL_FP(fd);
|
|
|
|
last_result = f_sync( fp );
|
|
|
|
return last_result == FR_OK ? VFS_RES_OK : VFS_RES_ERR;
|
|
}
|
|
|
|
static uint32_t myfatfs_fsize( const struct vfs_file *fd )
|
|
{
|
|
GET_FIL_FP(fd);
|
|
|
|
last_result = FR_OK;
|
|
|
|
return f_size( fp );
|
|
}
|
|
|
|
static int32_t myfatfs_ferrno( const struct vfs_file *fd )
|
|
{
|
|
return -last_result;
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// dir functions
|
|
//
|
|
#define GET_DIR_DP(descr) \
|
|
const struct myvfs_dir *mydd = (const struct myvfs_dir *)descr; \
|
|
DIR *dp = (DIR *)&(mydd->dp);
|
|
|
|
static int32_t myfatfs_closedir( const struct vfs_dir *dd )
|
|
{
|
|
GET_DIR_DP(dd);
|
|
|
|
last_result = f_closedir( dp );
|
|
|
|
// free descriptor memory
|
|
free( (void *)dd );
|
|
|
|
return last_result == FR_OK ? VFS_RES_OK : VFS_RES_ERR;
|
|
}
|
|
|
|
static void myfatfs_fill_stat( const FILINFO *fno, struct vfs_stat *buf )
|
|
{
|
|
memset( buf, 0, sizeof( struct vfs_stat ) );
|
|
|
|
// fill in supported stat entries
|
|
strncpy( buf->name, fno->fname, FS_OBJ_NAME_LEN+1 );
|
|
buf->name[FS_OBJ_NAME_LEN] = '\0';
|
|
buf->size = fno->fsize;
|
|
buf->is_dir = fno->fattrib & AM_DIR ? 1 : 0;
|
|
buf->is_rdonly = fno->fattrib & AM_RDO ? 1 : 0;
|
|
buf->is_hidden = fno->fattrib & AM_HID ? 1 : 0;
|
|
buf->is_sys = fno->fattrib & AM_SYS ? 1 : 0;
|
|
buf->is_arch = fno->fattrib & AM_ARC ? 1 : 0;
|
|
|
|
struct vfs_time *tm = &(buf->tm);
|
|
tm->year = (fno->fdate >> 9) + 1980;
|
|
tm->mon = (fno->fdate >> 5) & 0x0f;
|
|
tm->day = fno->fdate & 0x1f;
|
|
tm->hour = (fno->ftime >> 11);
|
|
tm->min = (fno->ftime >> 5) & 0x3f;
|
|
tm->sec = fno->ftime & 0x3f;
|
|
buf->tm_valid = 1;
|
|
}
|
|
|
|
static int32_t myfatfs_readdir( const struct vfs_dir *dd, struct vfs_stat *buf )
|
|
{
|
|
GET_DIR_DP(dd);
|
|
FILINFO fno;
|
|
|
|
if (FR_OK == (last_result = f_readdir( dp, &fno ))) {
|
|
// condition "no further item" is signalled with empty name
|
|
if (fno.fname[0] != '\0') {
|
|
myfatfs_fill_stat( &fno, buf );
|
|
|
|
return VFS_RES_OK;
|
|
}
|
|
}
|
|
|
|
return VFS_RES_ERR;
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// filesystem functions
|
|
//
|
|
static vfs_vol *myfatfs_mount( const char *name, int num )
|
|
{
|
|
struct myvfs_vol *vol;
|
|
const size_t len = strlen( name );
|
|
|
|
// num argument specifies the physical driver = SS/CS pin number for this sd card
|
|
if (num >= 0) {
|
|
for (int i = 0; i < NUM_LOGICAL_DRIVES; i++) {
|
|
if (0 == strncmp( name, volstr[i], strlen( volstr[i] ) )) {
|
|
VolToPart[i].pd = num;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (vol = malloc( sizeof( struct myvfs_vol ) )) {
|
|
if (vol->ldrname = strdup( name )) {
|
|
if (FR_OK == (last_result = f_mount( &(vol->fs), name, 1 ))) {
|
|
vol->vfs_vol.fs_type = VFS_FS_FATFS;
|
|
vol->vfs_vol.fns = &myfatfs_vol_fns;
|
|
return (vfs_vol *)vol;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (vol) {
|
|
if (vol->ldrname) free( vol->ldrname );
|
|
free( vol );
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static BYTE myfatfs_mode2flag( const char *mode )
|
|
{
|
|
if (strlen( mode ) == 1) {
|
|
if(strcmp( mode, "w" ) == 0)
|
|
return FA_WRITE | FA_CREATE_ALWAYS;
|
|
else if (strcmp( mode, "r" ) == 0)
|
|
return FA_READ | FA_OPEN_EXISTING;
|
|
else if (strcmp( mode, "a" ) == 0)
|
|
return FA_WRITE | FA_OPEN_ALWAYS;
|
|
else
|
|
return FA_READ | FA_OPEN_EXISTING;
|
|
} else if (strlen( mode ) == 2) {
|
|
if (strcmp( mode, "r+" ) == 0)
|
|
return FA_READ | FA_WRITE | FA_OPEN_EXISTING;
|
|
else if (strcmp( mode, "w+" ) == 0)
|
|
return FA_READ | FA_WRITE | FA_CREATE_ALWAYS;
|
|
else if (strcmp( mode, "a+" ) ==0 )
|
|
return FA_READ | FA_WRITE | FA_OPEN_ALWAYS;
|
|
else
|
|
return FA_READ | FA_OPEN_EXISTING;
|
|
} else {
|
|
return FA_READ | FA_OPEN_EXISTING;
|
|
}
|
|
}
|
|
|
|
static vfs_file *myfatfs_open( const char *name, const char *mode )
|
|
{
|
|
struct myvfs_file *fd;
|
|
const BYTE flags = myfatfs_mode2flag( mode );
|
|
|
|
if (fd = malloc( sizeof( struct myvfs_file ) )) {
|
|
if (FR_OK == (last_result = f_open( &(fd->fp), name, flags ))) {
|
|
// skip to end of file for append mode
|
|
if (flags & FA_OPEN_ALWAYS)
|
|
f_lseek( &(fd->fp), f_size( &(fd->fp) ) );
|
|
|
|
fd->vfs_file.fs_type = VFS_FS_FATFS;
|
|
fd->vfs_file.fns = &myfatfs_file_fns;
|
|
return (vfs_file *)fd;
|
|
} else {
|
|
free( fd );
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static vfs_dir *myfatfs_opendir( const char *name )
|
|
{
|
|
struct myvfs_dir *dd;
|
|
|
|
if (dd = malloc( sizeof( struct myvfs_dir ) )) {
|
|
if (FR_OK == (last_result = f_opendir( &(dd->dp), name ))) {
|
|
dd->vfs_dir.fs_type = VFS_FS_FATFS;
|
|
dd->vfs_dir.fns = &myfatfs_dir_fns;
|
|
return (vfs_dir *)dd;
|
|
} else {
|
|
free( dd );
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int32_t myfatfs_stat( const char *name, struct vfs_stat *buf )
|
|
{
|
|
FILINFO fno;
|
|
|
|
if (FR_OK == (last_result = f_stat( name, &fno ))) {
|
|
myfatfs_fill_stat( &fno, buf );
|
|
|
|
return VFS_RES_OK;
|
|
} else {
|
|
return VFS_RES_ERR;
|
|
}
|
|
}
|
|
|
|
static int32_t myfatfs_remove( const char *name )
|
|
{
|
|
last_result = f_unlink( name );
|
|
|
|
return last_result == FR_OK ? VFS_RES_OK : VFS_RES_ERR;
|
|
}
|
|
|
|
static int32_t myfatfs_rename( const char *oldname, const char *newname )
|
|
{
|
|
last_result = f_rename( oldname, newname );
|
|
|
|
return last_result == FR_OK ? VFS_RES_OK : VFS_RES_ERR;
|
|
}
|
|
|
|
static int32_t myfatfs_mkdir( const char *name )
|
|
{
|
|
last_result = f_mkdir( name );
|
|
|
|
return last_result == FR_OK ? VFS_RES_OK : VFS_RES_ERR;
|
|
}
|
|
|
|
static int32_t myfatfs_fsinfo( uint32_t *total, uint32_t *used )
|
|
{
|
|
DWORD free_clusters;
|
|
FATFS *fatfs;
|
|
|
|
if ((last_result = f_getfree( "", &free_clusters, &fatfs )) == FR_OK) {
|
|
// provide information in kByte since uint32_t would clip to 4 GByte
|
|
*total = (fatfs->n_fatent * fatfs->csize) / (1024 / FF_MAX_SS);
|
|
*used = *total - (free_clusters * fatfs->csize) / (1024 / FF_MAX_SS);
|
|
}
|
|
|
|
return last_result == FR_OK ? VFS_RES_OK : VFS_RES_ERR;
|
|
}
|
|
|
|
static int32_t myfatfs_chdrive( const char *name )
|
|
{
|
|
last_result = f_chdrive( name );
|
|
|
|
return last_result == FR_OK ? VFS_RES_OK : VFS_RES_ERR;
|
|
}
|
|
|
|
static int32_t myfatfs_chdir( const char *name )
|
|
{
|
|
last_result = f_chdir( name );
|
|
|
|
return last_result == FR_OK ? VFS_RES_OK : VFS_RES_ERR;
|
|
}
|
|
|
|
static int32_t myfatfs_errno( void )
|
|
{
|
|
return -last_result;
|
|
}
|
|
|
|
static void myfatfs_clearerr( void )
|
|
{
|
|
last_result = FR_OK;
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// VFS interface functions
|
|
//
|
|
vfs_fs_fns *myfatfs_realm( const char *inname, char **outname, int set_current_drive )
|
|
{
|
|
if (inname[0] == '/') {
|
|
char *oname;
|
|
|
|
// logical drive is specified, check if it's one of ours
|
|
for (int i = 0; i < FF_VOLUMES; i++) {
|
|
size_t volstr_len = strlen( volstr[i] );
|
|
if (0 == strncmp( &(inname[1]), volstr[i], volstr_len )) {
|
|
oname = strdup( inname );
|
|
strcpy( oname, volstr[i] );
|
|
oname[volstr_len] = ':';
|
|
*outname = oname;
|
|
|
|
if (set_current_drive) is_current_drive = TRUE;
|
|
return &myfatfs_fs_fns;
|
|
}
|
|
}
|
|
} else {
|
|
// no logical drive in patchspec, are we current drive?
|
|
if (is_current_drive) {
|
|
*outname = strdup( inname );
|
|
return &myfatfs_fs_fns;
|
|
}
|
|
}
|
|
|
|
if (set_current_drive) is_current_drive = false;
|
|
return NULL;
|
|
}
|