#include #include #include #include #include "sdkconfig.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 // inline void *ff_memalloc( UINT size ) { return malloc( size ); } inline 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); (void)fs; 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, CONFIG_NODEMCU_FS_OBJ_NAME_LEN+1 ); buf->name[CONFIG_NODEMCU_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; // num argument specifies the physical driver if (num >= 0) { for (int i = 0; i < FF_VOLUMES; 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; }