nodemcu-firmware/app/spiffs/spiffs.c

548 lines
15 KiB
C
Raw Permalink Normal View History

#include <stdio.h>
#include "platform.h"
#include "spiffs.h"
/*
* With the intoduction of a unified FatFS and SPIFFS support (#1397), the SPIFFS
* interface is now abstracted through a uses a single SPIFFS entry point
* myspiffs_realm() which returns a vfs_fs_fns object (as does myfatfs_realm()).
* All other functions and data are static.
*
* Non-OS SDK V3.0 introduces a flash partition table (PT) and SPIFFS has now been
* updated to support this:
* - SPIFFS limits search to the specifed SPIFFS0 address and size.
* - Any headroom / offset from other partitions is reflected in the PT allocations.
* - Unforced mounts will attempt to mount any valid SPIFSS found in this range
* (NodeMCU uses the SPIFFS_USE_MAGIC setting to make existing FS discoverable).
* - Subject to the following, no offset or FS search is done. The FS is assumed
* to be at the first valid location at the start of the partition.
*/
#include "spiffs_nucleus.h"
static spiffs fs;
static void (*automounter)();
#define LOG_PAGE_SIZE 256
#define LOG_BLOCK_SIZE (INTERNAL_FLASH_SECTOR_SIZE * 2)
#define LOG_BLOCK_SIZE_SMALL_FS (INTERNAL_FLASH_SECTOR_SIZE)
#define MIN_BLOCKS_FS 4
#define MASK_1MB (0x100000-1)
#define ALIGN (0x2000)
2019-02-17 19:26:29 +01:00
static u8_t spiffs_work_buf[LOG_PAGE_SIZE*2];
static u8_t spiffs_fds[sizeof(spiffs_fd) * SPIFFS_MAX_OPEN_FILES];
#if SPIFFS_CACHE
static u8_t myspiffs_cache[20 + (LOG_PAGE_SIZE+20)*4];
#endif
static s32_t my_spiffs_read(u32_t addr, u32_t size, u8_t *dst) {
platform_flash_read(dst, addr, size);
return SPIFFS_OK;
}
static s32_t my_spiffs_write(u32_t addr, u32_t size, u8_t *src) {
platform_flash_write(src, addr, size);
return SPIFFS_OK;
}
static int erase_cnt = -1; // If set to >=0 then erasing gives a ... feedback
static s32_t my_spiffs_erase(u32_t addr, u32_t size) {
u32_t sect_first = platform_flash_get_sector_of_address(addr);
u32_t sect_last = sect_first;
while( sect_first <= sect_last ) {
if (erase_cnt >= 0 && (erase_cnt++ & 0xF) == 0) {
dbg_printf(".");
}
if( platform_flash_erase_sector( sect_first ++ ) == PLATFORM_ERR ) {
return SPIFFS_ERR_INTERNAL;
}
}
return SPIFFS_OK;
2019-02-17 19:26:29 +01:00
}
void myspiffs_check_callback(spiffs_check_type type, spiffs_check_report report, u32_t arg1, u32_t arg2){
// if(SPIFFS_CHECK_PROGRESS == report) return;
// NODE_ERR("type: %d, report: %d, arg1: %d, arg2: %d\n", type, report, arg1, arg2);
}
/*******************
* Note that the W25Q32BV array is organized into 16,384 programmable pages of 256-bytes 
* each. Up to 256 bytes can be programmed at a time. Pages can be erased in groups of 
* 16 (4KB sector erase), groups of 128 (32KB block erase), groups of 256 (64KB block 
* erase) or the entire chip (chip erase). The W25Q32BV has 1,024 erasable sectors and 
* 64 erasable blocks respectively. The small 4KB sectors allow for greater flexibility 
* in applications that require data and parameter storage. 
*
* Returns TRUE if FS was found.
*/
static bool myspiffs_set_cfg(spiffs_config *cfg, bool force_create) {
uint32 pt_start, pt_size, pt_end;
pt_size = platform_flash_get_partition (NODEMCU_SPIFFS0_PARTITION, &pt_start);
if (pt_size == 0) {
return FALSE;
}
pt_end = pt_start + pt_size;
cfg->hal_read_f = my_spiffs_read;
cfg->hal_write_f = my_spiffs_write;
cfg->hal_erase_f = my_spiffs_erase;
cfg->phys_erase_block = INTERNAL_FLASH_SECTOR_SIZE;
cfg->log_page_size = LOG_PAGE_SIZE;
cfg->phys_addr = (pt_start + ALIGN - 1) & ~(ALIGN - 1);
cfg->phys_size = (pt_end & ~(ALIGN - 1)) - cfg->phys_addr;
if (cfg->phys_size < MIN_BLOCKS_FS * LOG_BLOCK_SIZE_SMALL_FS) {
2017-07-07 05:18:06 +02:00
return FALSE;
} else if (cfg->phys_size < MIN_BLOCKS_FS * LOG_BLOCK_SIZE) {
cfg->log_block_size = LOG_BLOCK_SIZE_SMALL_FS;
} else {
cfg->log_block_size = LOG_BLOCK_SIZE;
}
#ifdef SPIFFS_USE_MAGIC_LENGTH
if (!force_create) {
int size = SPIFFS_probe_fs(cfg);
if (size > 0 && size < cfg->phys_size) {
NODE_DBG("Overriding size:%x\n",size);
cfg->phys_size = size;
}
if (size <= 0) {
return FALSE;
}
}
#endif
2019-02-17 19:26:29 +01:00
NODE_DBG("myspiffs set cfg block: %x %x %x %x %x %x\n", pt_start, pt_end,
cfg->phys_size, cfg->phys_addr, cfg->phys_size, cfg->log_block_size);
return TRUE;
}
static bool myspiffs_mount(bool force_mount) {
STARTUP_COUNT;
spiffs_config cfg;
if (!myspiffs_set_cfg(&cfg, force_mount) && !force_mount) {
return FALSE;
}
fs.err_code = 0;
int res = SPIFFS_mount(&fs,
&cfg,
spiffs_work_buf,
spiffs_fds,
sizeof(spiffs_fds),
#if SPIFFS_CACHE
myspiffs_cache,
sizeof(myspiffs_cache),
#else
0, 0,
#endif
// myspiffs_check_callback);
0);
NODE_DBG("mount res: %d, %d\n", res, fs.err_code);
STARTUP_COUNT;
return res == SPIFFS_OK;
}
void myspiffs_unmount() {
SPIFFS_unmount(&fs);
}
// FS formatting function
// Returns 1 if OK, 0 for error
int myspiffs_format( void )
{
SPIFFS_unmount(&fs);
myspiffs_mount(TRUE);
SPIFFS_unmount(&fs);
NODE_DBG("Formatting: size 0x%x, addr 0x%x\n", fs.cfg.phys_size, fs.cfg.phys_addr);
erase_cnt = 0;
int status = SPIFFS_format(&fs);
erase_cnt = -1;
return status < 0 ? 0 : myspiffs_mount(FALSE);
}
// ***************************************************************************
// vfs API
// ***************************************************************************
#include <stdlib.h>
#include "vfs_int.h"
#define MY_LDRV_ID "FLASH"
// default current drive
static int is_current_drive = TRUE;
// forward declarations
static sint32_t myspiffs_vfs_close( const struct vfs_file *fd );
static sint32_t myspiffs_vfs_read( const struct vfs_file *fd, void *ptr, size_t len );
static sint32_t myspiffs_vfs_write( const struct vfs_file *fd, const void *ptr, size_t len );
static sint32_t myspiffs_vfs_lseek( const struct vfs_file *fd, sint32_t off, int whence );
static sint32_t myspiffs_vfs_eof( const struct vfs_file *fd );
static sint32_t myspiffs_vfs_tell( const struct vfs_file *fd );
static sint32_t myspiffs_vfs_flush( const struct vfs_file *fd );
static uint32_t myspiffs_vfs_size( const struct vfs_file *fd );
static sint32_t myspiffs_vfs_ferrno( const struct vfs_file *fd );
static sint32_t myspiffs_vfs_closedir( const struct vfs_dir *dd );
static sint32_t myspiffs_vfs_readdir( const struct vfs_dir *dd, struct vfs_stat *buf );
static vfs_vol *myspiffs_vfs_mount( const char *name, int num );
static vfs_file *myspiffs_vfs_open( const char *name, const char *mode );
static vfs_dir *myspiffs_vfs_opendir( const char *name );
static sint32_t myspiffs_vfs_stat( const char *name, struct vfs_stat *buf );
static sint32_t myspiffs_vfs_remove( const char *name );
static sint32_t myspiffs_vfs_rename( const char *oldname, const char *newname );
static sint32_t myspiffs_vfs_fsinfo( uint32_t *total, uint32_t *used );
static sint32_t myspiffs_vfs_fscfg( uint32_t *phys_addr, uint32_t *phys_size );
static sint32_t myspiffs_vfs_format( void );
static sint32_t myspiffs_vfs_errno( void );
static void myspiffs_vfs_clearerr( void );
static sint32_t myspiffs_vfs_umount( const struct vfs_vol *vol );
// ---------------------------------------------------------------------------
// function tables
//
static vfs_fs_fns myspiffs_fs_fns = {
.mount = myspiffs_vfs_mount,
.open = myspiffs_vfs_open,
.opendir = myspiffs_vfs_opendir,
.stat = myspiffs_vfs_stat,
.remove = myspiffs_vfs_remove,
.rename = myspiffs_vfs_rename,
.mkdir = NULL,
.fsinfo = myspiffs_vfs_fsinfo,
.fscfg = myspiffs_vfs_fscfg,
.format = myspiffs_vfs_format,
.chdrive = NULL,
.chdir = NULL,
.ferrno = myspiffs_vfs_errno,
.clearerr = myspiffs_vfs_clearerr
};
static vfs_file_fns myspiffs_file_fns = {
.close = myspiffs_vfs_close,
.read = myspiffs_vfs_read,
.write = myspiffs_vfs_write,
.lseek = myspiffs_vfs_lseek,
.eof = myspiffs_vfs_eof,
.tell = myspiffs_vfs_tell,
.flush = myspiffs_vfs_flush,
.size = myspiffs_vfs_size,
.ferrno = myspiffs_vfs_ferrno
};
static vfs_dir_fns myspiffs_dd_fns = {
.close = myspiffs_vfs_closedir,
.readdir = myspiffs_vfs_readdir
};
// ---------------------------------------------------------------------------
// specific struct extensions
//
struct myvfs_file {
struct vfs_file vfs_file;
spiffs_file fh;
};
struct myvfs_dir {
struct vfs_dir vfs_dir;
spiffs_DIR d;
};
// ---------------------------------------------------------------------------
// volume functions
//
static sint32_t myspiffs_vfs_umount( const struct vfs_vol *vol ) {
// not implemented
return VFS_RES_ERR;
}
// ---------------------------------------------------------------------------
// dir functions
//
#define GET_DIR_D(descr) \
const struct myvfs_dir *mydd = (const struct myvfs_dir *)descr; \
spiffs_DIR *d = (spiffs_DIR *)&(mydd->d);
static sint32_t myspiffs_vfs_closedir( const struct vfs_dir *dd ) {
GET_DIR_D(dd);
sint32_t res = SPIFFS_closedir( d );
// free descriptor memory
free( (void *)dd );
}
static sint32_t myspiffs_vfs_readdir( const struct vfs_dir *dd, struct vfs_stat *buf ) {
GET_DIR_D(dd);
struct spiffs_dirent dirent;
if (SPIFFS_readdir( d, &dirent )) {
memset( buf, 0, sizeof( struct vfs_stat ) );
// copy entries to item
// fill in supported stat entries
strncpy( buf->name, dirent.name, FS_OBJ_NAME_LEN+1 );
buf->name[FS_OBJ_NAME_LEN] = '\0';
buf->size = dirent.size;
return VFS_RES_OK;
}
return VFS_RES_ERR;
}
// ---------------------------------------------------------------------------
// file functions
//
#define GET_FILE_FH(descr) \
const struct myvfs_file *myfd = (const struct myvfs_file *)descr; \
spiffs_file fh = myfd->fh;
static sint32_t myspiffs_vfs_close( const struct vfs_file *fd ) {
GET_FILE_FH(fd);
sint32_t res = SPIFFS_close( &fs, fh );
// free descriptor memory
free( (void *)fd );
return res;
}
static sint32_t myspiffs_vfs_read( const struct vfs_file *fd, void *ptr, size_t len ) {
GET_FILE_FH(fd);
sint32_t n = SPIFFS_read( &fs, fh, ptr, len );
return n >= 0 ? n : VFS_RES_ERR;
}
static sint32_t myspiffs_vfs_write( const struct vfs_file *fd, const void *ptr, size_t len ) {
GET_FILE_FH(fd);
sint32_t n = SPIFFS_write( &fs, fh, (void *)ptr, len );
return n >= 0 ? n : VFS_RES_ERR;
}
static sint32_t myspiffs_vfs_lseek( const struct vfs_file *fd, sint32_t off, int whence ) {
GET_FILE_FH(fd);
int spiffs_whence;
switch (whence) {
default:
case VFS_SEEK_SET:
spiffs_whence = SPIFFS_SEEK_SET;
break;
case VFS_SEEK_CUR:
spiffs_whence = SPIFFS_SEEK_CUR;
break;
case VFS_SEEK_END:
spiffs_whence = SPIFFS_SEEK_END;
break;
}
sint32_t res = SPIFFS_lseek( &fs, fh, off, spiffs_whence );
return res >= 0 ? res : VFS_RES_ERR;
}
static sint32_t myspiffs_vfs_eof( const struct vfs_file *fd ) {
GET_FILE_FH(fd);
return SPIFFS_eof( &fs, fh );
}
static sint32_t myspiffs_vfs_tell( const struct vfs_file *fd ) {
GET_FILE_FH(fd);
return SPIFFS_tell( &fs, fh );
}
static sint32_t myspiffs_vfs_flush( const struct vfs_file *fd ) {
GET_FILE_FH(fd);
return SPIFFS_fflush( &fs, fh ) >= 0 ? VFS_RES_OK : VFS_RES_ERR;
}
static uint32_t myspiffs_vfs_size( const struct vfs_file *fd ) {
GET_FILE_FH(fd);
int32_t curpos = SPIFFS_tell( &fs, fh );
int32_t size = SPIFFS_lseek( &fs, fh, 0, SPIFFS_SEEK_END );
(void) SPIFFS_lseek( &fs, fh, curpos, SPIFFS_SEEK_SET );
return size;
}
static sint32_t myspiffs_vfs_ferrno( const struct vfs_file *fd ) {
return SPIFFS_errno( &fs );
}
static int fs_mode2flag(const char *mode){
if(strlen(mode)==1){
if(strcmp(mode,"w")==0)
return SPIFFS_WRONLY|SPIFFS_CREAT|SPIFFS_TRUNC;
else if(strcmp(mode, "r")==0)
return SPIFFS_RDONLY;
else if(strcmp(mode, "a")==0)
return SPIFFS_WRONLY|SPIFFS_CREAT|SPIFFS_APPEND;
else
return SPIFFS_RDONLY;
} else if (strlen(mode)==2){
if(strcmp(mode,"r+")==0)
return SPIFFS_RDWR;
else if(strcmp(mode, "w+")==0)
return SPIFFS_RDWR|SPIFFS_CREAT|SPIFFS_TRUNC;
else if(strcmp(mode, "a+")==0)
return SPIFFS_RDWR|SPIFFS_CREAT|SPIFFS_APPEND;
else
return SPIFFS_RDONLY;
} else {
return SPIFFS_RDONLY;
}
}
// ---------------------------------------------------------------------------
// filesystem functions
//
static vfs_file *myspiffs_vfs_open( const char *name, const char *mode ) {
struct myvfs_file *fd;
int flags = fs_mode2flag( mode );
if (fd = (struct myvfs_file *)malloc( sizeof( struct myvfs_file ) )) {
if (0 < (fd->fh = SPIFFS_open( &fs, name, flags, 0 ))) {
fd->vfs_file.fs_type = VFS_FS_SPIFFS;
fd->vfs_file.fns = &myspiffs_file_fns;
return (vfs_file *)fd;
} else {
free( fd );
}
}
return NULL;
}
static vfs_dir *myspiffs_vfs_opendir( const char *name ){
struct myvfs_dir *dd;
if (dd = (struct myvfs_dir *)malloc( sizeof( struct myvfs_dir ) )) {
if (SPIFFS_opendir( &fs, name, &(dd->d) )) {
dd->vfs_dir.fs_type = VFS_FS_SPIFFS;
dd->vfs_dir.fns = &myspiffs_dd_fns;
return (vfs_dir *)dd;
} else {
free( dd );
}
}
return NULL;
}
static sint32_t myspiffs_vfs_stat( const char *name, struct vfs_stat *buf ) {
spiffs_stat stat;
if (0 <= SPIFFS_stat( &fs, name, &stat )) {
memset( buf, 0, sizeof( struct vfs_stat ) );
// fill in supported stat entries
strncpy( buf->name, stat.name, FS_OBJ_NAME_LEN+1 );
buf->name[FS_OBJ_NAME_LEN] = '\0';
buf->size = stat.size;
return VFS_RES_OK;
} else {
return VFS_RES_ERR;
}
}
static sint32_t myspiffs_vfs_remove( const char *name ) {
return SPIFFS_remove( &fs, name );
}
static sint32_t myspiffs_vfs_rename( const char *oldname, const char *newname ) {
return SPIFFS_rename( &fs, oldname, newname );
}
static sint32_t myspiffs_vfs_fsinfo( uint32_t *total, uint32_t *used ) {
return SPIFFS_info( &fs, total, used );
}
static sint32_t myspiffs_vfs_fscfg( uint32_t *phys_addr, uint32_t *phys_size ) {
*phys_addr = fs.cfg.phys_addr;
*phys_size = fs.cfg.phys_size;
return VFS_RES_OK;
}
static vfs_vol *myspiffs_vfs_mount( const char *name, int num ) {
// volume descriptor not supported, just return TRUE / FALSE
return myspiffs_mount(FALSE) ? (vfs_vol *)1 : NULL;
}
static sint32_t myspiffs_vfs_format( void ) {
return myspiffs_format();
}
static sint32_t myspiffs_vfs_errno( void ) {
return SPIFFS_errno( &fs );
}
static void myspiffs_vfs_clearerr( void ) {
SPIFFS_clearerr( &fs );
}
// The callback will be called on the first file operation
void myspiffs_set_automount(void (*mounter)()) {
automounter = mounter;
}
// ---------------------------------------------------------------------------
// VFS interface functions
//
vfs_fs_fns *myspiffs_realm( const char *inname, char **outname, int set_current_drive ) {
if (automounter) {
void (*mounter)() = automounter;
automounter = NULL;
mounter();
}
if (inname[0] == '/') {
// logical drive is specified, check if it's our id
if (0 == strncmp(inname + 1, MY_LDRV_ID, sizeof(MY_LDRV_ID)-1)) {
*outname = (char *)(inname + sizeof(MY_LDRV_ID));
if (*outname[0] == '/') {
// skip leading /
(*outname)++;
}
if (set_current_drive) is_current_drive = TRUE;
return &myspiffs_fs_fns;
}
} else {
// no logical drive in patchspec, are we current drive?
if (is_current_drive) {
*outname = (char *)inname;
return &myspiffs_fs_fns;
}
}
if (set_current_drive) is_current_drive = FALSE;
return NULL;
}