Commit a2ba49e3 authored by Johny Mattsson's avatar Johny Mattsson
Browse files

Switch to IDF-provided VFS and standard `io` module.

The IDF-provided VFS resolves several issues:

 - The IDF components having a different view of the (virtual) file system
   compared to the Lua environment.

 - RTOS task/thread safety. Our legacy VFS was only ever safe to use
   from the LVM thread, which limited its usability. Upgrading it
   would have effectively required a reimplementation of the IDF VFS,
   which would have been a bigger task with larger on-going maintenance
   issues.

 - We're no longer needing to maintain our own SPIFFS component.

 - We're no longer needing to maintain our own FATFS component.

 - The legacy of the 8266's lack of standard C interface to the file system
   is no longer holding us back, meaning that we can use the standard
   Lua `io` module rather than the cobbled-together swiss army knife
   also known as the file module.

Of course, the downside is that we'll either have to declare a backwards
breakage in regard to the file module, or provide a Lua shim for the old
functions, where applicable.

Also included is some necessary integer type fixups in unrelated code,
which apparently had depended on some non-standard types in either the
SPIFFS or FATFS headers.

A memory leak issue in the sdmmc module was also found and fixed while
said module got switched over to the Espressif VFS.

Module documentation has been updated to match the new reality (and I
discovered in some places it wasn't even matching the old reality).
parent 32f66e3d
......@@ -11,8 +11,10 @@
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#ifndef LUA_CROSS_COMPILER
#ifdef LUA_USE_ESP
#include "esp_system.h"
#endif
#ifdef LUA_USE_ESP8266
#include "vfs.h"
#endif
......@@ -716,29 +718,31 @@ LUALIB_API void (luaL_reref) (lua_State *L, int t, int *ref) {
typedef struct LoadF {
int extraline;
#ifdef LUA_CROSS_COMPILER
FILE *f;
#else
#ifdef LUA_USE_ESP8266
int f;
#else
FILE *f;
#endif
char buff[LUAL_BUFFERSIZE];
} LoadF;
#ifdef LUA_CROSS_COMPILER
# define freopen_bin(f,fn) freopen(f,"rb",fn)
# define read_buff(b,f) fread(b, 1, sizeof (b), f)
#else
#ifdef LUA_USE_ESP8266
# define strerror(n) ""
#undef feof
# define feof(f) vfs_eof(f)
#undef fopen
# define fopen(f, m) vfs_open(f, m)
# define freopen_bin(fn,f) ((void) vfs_close(f), vfs_open(fn, "r"))
#undef fclose
# define fclose(f) vfs_close(f)
#undef getc
# define getc(f) vfs_getc(f)
#undef ungetc
# define ungetc(c,f) vfs_ungetc(c, f)
# define read_buff(b,f) vfs_read(f, b, sizeof (b))
#else
# define freopen_bin(f,fn) freopen(f,"rb",fn)
# define read_buff(b,f) fread(b, 1, sizeof (b), f)
#endif
......@@ -810,7 +814,7 @@ LUALIB_API int luaL_loadfile (lua_State *L, const char *filename) {
}
#else
(void) readstatus;
if (filename) vfs_close(lf.f); /* close file (even in case of errors) */
if (filename) fclose(lf.f); /* close file (even in case of errors) */
#endif
lua_remove(L, fnameindex);
return status;
......
......@@ -13,7 +13,6 @@
#include "lfunc.h"
#include "lflash.h"
#include "platform.h"
#include "vfs.h"
#include "uzlib.h"
#include "platform_wdt.h"
......@@ -25,6 +24,17 @@
#include "lfs.h"
#ifdef LUA_USE_ESP8266
#include "vfs.h"
#define fopen(f, m) vfs_open(f, m)
#define fclose(f) vfs_close(f)
#define fseek(f,n.w) vfs_lseek(f, n, w)
#define SEEK_SET VFS_SEEK_SET
#define SEEK_END VFS_SEEK_END
#define ftell(f) vfs_tell(f)
#define fread(o, s, n, f) vfs_read(f, o, (s)*(n))
#endif
/*
* Flash memory is a fixed memory addressable block that is serially allocated by the
* luac build process and the out image can be downloaded into SPIFSS and loaded into
......@@ -52,7 +62,11 @@ static uint32_t curOffset;
#define WRITE_BLOCK_WORDS (WRITE_BLOCKSIZE/WORDSIZE)
struct INPUT {
int fd;
#ifdef LUA_USE_ESP8266
int f;
#else
FILE * f;
#endif
int len;
uint8_t block[READ_BLOCKSIZE];
uint8_t *inPtr;
......@@ -330,7 +344,7 @@ static uint8_t get_byte (void) {
int remaining = in->len - in->bytesRead;
int wanted = remaining >= READ_BLOCKSIZE ? READ_BLOCKSIZE : remaining;
if (vfs_read(in->fd, in->block, wanted) != wanted)
if (fread(in->block, 1, wanted, in->f) != wanted)
flash_error("read error on LFS image file");
platform_wdt_feed();
......@@ -477,14 +491,16 @@ static int loadLFS (lua_State *L) {
out->crc = ~0;
/* Open LFS image/ file, read unpacked length from last 4 byte and rewind */
if (!(in->fd = vfs_open(fn, "r")))
if (!(in->f = fopen(fn, "r")))
flash_error("LFS image file not found");
in->len = vfs_size(in->fd);
fseek(in->f, 0, SEEK_END);
in->len = ftell(in->f);
fseek(in->f, in->len - 4, SEEK_SET);
if (in->len <= 200 || /* size of an empty luac output */
vfs_lseek(in->fd, in->len-4, VFS_SEEK_SET) != in->len-4 ||
vfs_read(in->fd, &out->len, sizeof(uint32_t)) != sizeof(uint32_t))
ftell(in->f) != in->len-4 ||
fread(&out->len, 1, sizeof(uint32_t), in->f) != sizeof(uint32_t))
flash_error("read error on LFS image file");
vfs_lseek(in->fd, 0, VFS_SEEK_SET);
fseek(in->f, 0, SEEK_SET);
/* Allocate the out buffers */
for(i = 0; i < WRITE_BLOCKS; i++)
......@@ -507,7 +523,7 @@ static int loadLFS (lua_State *L) {
* basic signature, crc and length checks, so now we can reset the counts
* to do the actual write to flash on the second pass.
*/
vfs_lseek(in->fd, 0, VFS_SEEK_SET);
fseek(in->f, 0, SEEK_SET);
flashErase(0,(out->flashLen - 1)/FLASH_PAGE_SIZE);
flashSetPosition(0);
......@@ -535,8 +551,8 @@ static int loadLFSgc (lua_State *L) {
luaM_free(L, out);
}
if (in) {
if (in->fd)
vfs_close(in->fd);
if (in->f)
fclose(in->f);
luaM_free(L, in);
}
return 0;
......
......@@ -17,7 +17,7 @@
#include <string.h>
#include <fcntl.h>
#ifndef LUA_CROSS_COMPILER
#ifdef LUA_USE_ESP8266
#include "vfs.h"
#endif
......@@ -329,7 +329,7 @@ static int ll_loadlib (lua_State *L) {
** 'require' function
** =======================================================
*/
#ifdef LUA_CROSS_COMPILER
#if !defined(LUA_USE_ESP8266)
static int readable (const char *filename) {
FILE *f = fopen(filename, "r"); /* try to open file */
if (f == NULL) return 0; /* open failed */
......
......@@ -317,7 +317,7 @@ void output_redirect(const char *str, size_t l);
@@ lua_writestringerror defines how to print error messages.
** (A format string with one argument is enough for Lua...)
*/
#ifdef LUA_USE_ESP
#ifdef LUA_USE_ESP8266
#define lua_writestringerror(s,p) dbg_printf((s), (p))
#else
#define lua_writestringerror(s,p) fprintf(stderr, (s), (p))
......
......@@ -19,6 +19,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
/*
** This file uses only the official API of Lua.
......@@ -31,10 +32,9 @@
#include "lpanic.h"
#define LUA_LIB
#ifdef LUA_USE_ESP
#ifdef LUA_USE_ESP8266
#include "platform.h"
#include "vfs.h"
#include <fcntl.h>
#endif
......@@ -677,11 +677,7 @@ LUALIB_API void (luaL_reref) (lua_State *L, int t, int *ref) {
** =======================================================
*/
#ifdef LUA_CROSS_COMPILER
# define file(f) FILE *f
# define freopen_bin(f,fn) freopen(f,"rb",fn)
# define read_buff(b,f) fread(b, 1, sizeof (b), f)
#else /* map stdio API and macros to vfs */
#if defined(LUA_USE_ESP8266) /* map stdio API and macros to vfs */
# undef feof
# undef fopen
# undef getc
......@@ -690,10 +686,15 @@ LUALIB_API void (luaL_reref) (lua_State *L, int t, int *ref) {
# define strerror(n) ""
# define feof(f) vfs_eof(f)
# define fopen(f, m) vfs_open(f, m)
# define fclose(f) vfs_close(f)
# define freopen_bin(fn,f) ((void) vfs_close(f), vfs_open(fn, "r"))
# define getc(f) vfs_getc(f)
# define ungetc(c,f) vfs_ungetc(c, f)
# define read_buff(b,f) vfs_read(f, b, sizeof (b))
#else
# define file(f) FILE *f
# define freopen_bin(f,fn) freopen(f,"rb",fn)
# define read_buff(b,f) fread(b, 1, sizeof (b), f)
#endif
#define LAUXLIB_TYPE 0
typedef struct LoadF {
......@@ -803,7 +804,7 @@ LUALIB_API int luaL_loadfilex (lua_State *L, const char *filename,
}
#else
(void) readstatus; /* avoid compile error */
if (filename) vfs_close(lf.f); /* close file (even in case of errors) */
if (filename) fclose(lf.f); /* close file (even in case of errors) */
#endif
lua_remove(L, fnameindex);
return status;
......
......@@ -20,13 +20,16 @@
#ifdef LUA_USE_ESP
#include "platform.h"
#include "vfs.h"
#include "task/task.h"
#else
// On the cross-compiler we do need the LFS reload mechanism, regardless
#undef CONFIG_NODEMCU_EMBEDDED_LFS_SIZE
#endif
#ifdef LUA_USE_ESP8266
#include "vfs.h"
#endif
/*
** This is a mixed bag of NodeMCU additions broken into the following sections:
** * POSIX vs VFS file API abstraction
......@@ -47,7 +50,8 @@
//====================== Wrap POSIX and VFS file API =========================//
#ifdef LUA_USE_ESP
#ifdef LUA_USE_ESP8266
int luaopen_file(lua_State *L);
# define l_file(f) int f
# define l_open(f) vfs_open(f, "r")
......@@ -64,11 +68,13 @@ int luaopen_file(lua_State *L);
# define l_rewind(f) rewind(f)
#endif
#ifdef LUA_USE_ESP
#ifdef LUA_USE_ESP8266
extern void dbg_printf(const char *fmt, ...); // DEBUG
#undef printf
#define printf(...) dbg_printf(__VA_ARGS__) // DEBUG
#endif
#ifdef LUA_USE_ESP
#define FLASH_PAGE_SIZE INTERNAL_FLASH_SECTOR_SIZE
/* Erasing the LFS invalidates ESP instruction cache, so doing a block 64Kb */
......@@ -520,7 +526,7 @@ LUAI_FUNC void *luaN_writeFlash(void *vF, const void *rec, size_t n) {
LUAI_FUNC int luaN_init (lua_State *L) {
static LFSflashState *F = NULL;
static LFSHeader *fh;
char fname[CONFIG_NODEMCU_FS_OBJ_NAME_LEN];
char fname[CONFIG_SPIFFS_OBJ_NAME_LEN-1];
bool have_load_file = lfs_get_load_filename(fname, sizeof(fname));
/*
......@@ -582,7 +588,7 @@ LUAI_FUNC int luaN_init (lua_State *L) {
}
#endif
lfs_clear_load_filename();
#ifdef LUA_USE_ESP
#ifdef LUA_USE_ESP8266
luaopen_file(L);
#endif
if (!(F->f = l_open(fname))) {
......
......@@ -23,7 +23,7 @@
#include "lauxlib.h"
#include "lualib.h"
#ifndef LUA_USE_HOST
#ifdef LUA_USE_ESP8266
#include <fcntl.h>
#include "vfs.h"
#endif
......@@ -421,7 +421,7 @@ static int ll_loadlib (lua_State *L) {
** =======================================================
*/
#ifdef LUA_USE_ESP
#ifdef LUA_USE_ESP8266
#define file_t int
#undef fopen
#undef fclose
......
......@@ -73,6 +73,7 @@ idf_component_register(
"bt"
"driver_can"
"esp_http_client"
"fatfs"
"libsodium"
"lua"
"mbedtls"
......
......@@ -2,156 +2,26 @@
#include "module.h"
#include "lauxlib.h"
#include "lnodeaux.h"
#include "lmem.h"
#include "platform.h"
#include "esp_spiffs.h"
#include "vfs.h"
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#define FILE_READ_CHUNK 1024
static const char *default_fs_label =
((CONFIG_NODEMCU_DEFAULT_SPIFFS_LABEL &&
(CONFIG_NODEMCU_DEFAULT_SPIFFS_LABEL)[0]) ?
CONFIG_NODEMCU_DEFAULT_SPIFFS_LABEL : NULL);
// use this time/date in absence of a timestamp
#define FILE_TIMEDEF_YEAR 1970
#define FILE_TIMEDEF_MON 01
#define FILE_TIMEDEF_DAY 01
#define FILE_TIMEDEF_HOUR 00
#define FILE_TIMEDEF_MIN 00
#define FILE_TIMEDEF_SEC 00
static int file_fd = 0;
static int file_fd_ref = LUA_NOREF;
static int rtc_cb_ref = LUA_NOREF;
typedef struct _file_fd_ud {
int fd;
} file_fd_ud;
static void table2tm( lua_State *L, vfs_time *tm )
{
int idx = lua_gettop( L );
// extract items from table
lua_getfield( L, idx, "year" );
lua_getfield( L, idx, "mon" );
lua_getfield( L, idx, "day" );
lua_getfield( L, idx, "hour" );
lua_getfield( L, idx, "min" );
lua_getfield( L, idx, "sec" );
tm->year = luaL_optint( L, ++idx, FILE_TIMEDEF_YEAR );
tm->mon = luaL_optint( L, ++idx, FILE_TIMEDEF_MON );
tm->day = luaL_optint( L, ++idx, FILE_TIMEDEF_DAY );
tm->hour = luaL_optint( L, ++idx, FILE_TIMEDEF_HOUR );
tm->min = luaL_optint( L, ++idx, FILE_TIMEDEF_MIN );
tm->sec = luaL_optint( L, ++idx, FILE_TIMEDEF_SEC );
// remove items from stack
lua_pop( L, 6 );
}
static int32_t file_rtc_cb( vfs_time *tm )
{
int32_t res = VFS_RES_ERR;
if (rtc_cb_ref != LUA_NOREF) {
lua_State *L = lua_getstate();
lua_rawgeti( L, LUA_REGISTRYINDEX, rtc_cb_ref );
luaL_pcallx( L, 0, 1 );
if (lua_type( L, lua_gettop( L ) ) == LUA_TTABLE) {
table2tm( L, tm );
res = VFS_RES_OK;
}
// pop item returned by callback
lua_pop( L, 1 );
}
return res;
}
// Lua: on()
static int file_on(lua_State *L)
{
enum events{
ON_RTC = 0
};
const char *const eventnames[] = {"rtc", NULL};
int event = luaL_checkoption(L, 1, "rtc", eventnames);
switch (event) {
case ON_RTC:
luaL_unref(L, LUA_REGISTRYINDEX, rtc_cb_ref);
if (lua_isfunction(L, 2)) {
lua_pushvalue(L, 2); // copy argument (func) to the top of stack
rtc_cb_ref = luaL_ref(L, LUA_REGISTRYINDEX);
vfs_register_rtc_cb(file_rtc_cb);
} else {
rtc_cb_ref = LUA_NOREF;
vfs_register_rtc_cb(NULL);
}
break;
default:
break;
}
return 0;
}
// Lua: close()
static int file_close( lua_State* L )
{
file_fd_ud *ud;
if (lua_type( L, 1 ) != LUA_TUSERDATA) {
// fall back to last opened file
if (file_fd_ref != LUA_NOREF) {
lua_rawgeti( L, LUA_REGISTRYINDEX, file_fd_ref );
// top of stack is now default file descriptor
ud = (file_fd_ud *)luaL_checkudata(L, -1, "file.obj");
lua_pop( L, 1 );
} else {
// no default file currently opened
return 0;
}
} else {
ud = (file_fd_ud *)luaL_checkudata(L, 1, "file.obj");
}
if(ud->fd){
vfs_close(ud->fd);
// mark as closed
ud->fd = 0;
}
// unref default file descriptor
luaL_unref( L, LUA_REGISTRYINDEX, file_fd_ref );
file_fd_ref = LUA_NOREF;
return 0;
}
static int file_obj_free( lua_State *L )
{
file_fd_ud *ud = (file_fd_ud *)luaL_checkudata(L, 1, "file.obj");
if (ud->fd) {
// close file if it's still open
vfs_close(ud->fd);
}
return 0;
}
#ifdef CONFIG_NODEMCU_BUILD_SPIFFS
// Lua: format()
static int file_format( lua_State* L )
{
file_close(L);
if( !vfs_format() )
if(esp_spiffs_format(default_fs_label) != ESP_OK)
{
NODE_ERR( "\n*** ERROR ***: unable to format. FS might be compromised.\n" );
NODE_ERR( "It is advised to re-flash the NodeMCU image.\n" );
......@@ -163,154 +33,58 @@ static int file_format( lua_State* L )
return 0;
}
static int file_fscfg (lua_State *L)
{
uint32_t phys_addr, phys_size;
vfs_fscfg("/FLASH", &phys_addr, &phys_size);
lua_pushinteger (L, phys_addr);
lua_pushinteger (L, phys_size);
return 2;
}
#endif
// Lua: open(filename, mode)
static int file_open( lua_State* L )
{
size_t len;
// unref last file descriptor to allow gc'ing if not kept by user script
luaL_unref( L, LUA_REGISTRYINDEX, file_fd_ref );
file_fd_ref = LUA_NOREF;
const char *fname = luaL_checklstring( L, 1, &len );
const char *basename = vfs_basename( fname );
luaL_argcheck(L, strlen(basename) <= CONFIG_NODEMCU_FS_OBJ_NAME_LEN && strlen(fname) == len, 1, "filename invalid");
const char *mode = luaL_optstring(L, 2, "r");
file_fd = vfs_open(fname, mode);
if(!file_fd){
lua_pushnil(L);
} else {
file_fd_ud *ud = (file_fd_ud *) lua_newuserdata( L, sizeof( file_fd_ud ) );
ud->fd = file_fd;
luaL_getmetatable( L, "file.obj" );
lua_setmetatable( L, -2 );
// store reference to opened file
lua_pushvalue( L, -1 );
file_fd_ref = luaL_ref( L, LUA_REGISTRYINDEX );
}
return 1;
}
// Lua: list()
static int file_list( lua_State* L )
{
vfs_dir *dir;
if ((dir = vfs_opendir(""))) {
const char *dirname = luaL_optstring(L, 1, ".");
DIR *dir;
if ((dir = opendir(dirname))) {
lua_newtable( L );
struct vfs_stat stat;
while (vfs_readdir(dir, &stat) == VFS_RES_OK) {
lua_pushinteger(L, stat.size);
lua_setfield(L, -2, stat.name);
struct dirent *e;
while ((e = readdir(dir))) {
struct stat st = { 0, };
stat(e->d_name, &st);
lua_pushinteger(L, st.st_size);
lua_setfield(L, -2, e->d_name);
}
vfs_closedir(dir);
closedir(dir);
return 1;
}
return 0;
}
static int get_file_obj( lua_State *L, int *argpos )
{
if (lua_type( L, 1 ) == LUA_TUSERDATA) {
file_fd_ud *ud = (file_fd_ud *)luaL_checkudata(L, 1, "file.obj");
*argpos = 2;
return ud->fd;
} else {
*argpos = 1;
return file_fd;
}
}
#define GET_FILE_OBJ int argpos; \
int fd = get_file_obj( L, &argpos );
static int file_seek (lua_State *L)
{
GET_FILE_OBJ;
static const int mode[] = {VFS_SEEK_SET, VFS_SEEK_CUR, VFS_SEEK_END};
static const char *const modenames[] = {"set", "cur", "end", NULL};
if(!fd)
return luaL_error(L, "open a file first");
int op = luaL_checkoption(L, argpos, "cur", modenames);
long offset = luaL_optlong(L, ++argpos, 0);
op = vfs_lseek(fd, offset, mode[op]);
if (op < 0)
lua_pushnil(L); /* error */
else
lua_pushinteger(L, vfs_tell(fd));
return 1;
}
// Lua: exists(filename)
static int file_exists( lua_State* L )
{
size_t len;
const char *fname = luaL_checklstring( L, 1, &len );
const char *basename = vfs_basename( fname );
luaL_argcheck(L, strlen(basename) <= CONFIG_NODEMCU_FS_OBJ_NAME_LEN && strlen(fname) == len, 1, "filename invalid");
struct vfs_stat stat;
lua_pushboolean(L, vfs_stat((char *)fname, &stat) == VFS_RES_OK ? 1 : 0);
struct stat st;
lua_pushboolean(L, stat(fname, &st) == 0 ? 1 : 0);
return 1;
}
// Lua: remove(filename)
static int file_remove( lua_State* L )
{
size_t len;
const char *fname = luaL_checklstring( L, 1, &len );
const char *basename = vfs_basename( fname );
luaL_argcheck(L, strlen(basename) <= CONFIG_NODEMCU_FS_OBJ_NAME_LEN && strlen(fname) == len, 1, "filename invalid");
vfs_remove((char *)fname);
unlink(fname);
return 0;
}
// Lua: flush()
static int file_flush( lua_State* L )
{
GET_FILE_OBJ;
if(!fd)
return luaL_error(L, "open a file first");
if(vfs_flush(fd) == 0)
lua_pushboolean(L, 1);
else
lua_pushnil(L);
return 1;
}
// Lua: rename("oldname", "newname")
static int file_rename( lua_State* L )
{
size_t len;
const char *oldname = luaL_checklstring( L, 1, &len );
const char *basename = vfs_basename( oldname );
luaL_argcheck(L, strlen(basename) <= CONFIG_NODEMCU_FS_OBJ_NAME_LEN && strlen(oldname) == len, 1, "filename invalid");
const char *newname = luaL_checklstring( L, 2, &len );
basename = vfs_basename( newname );
luaL_argcheck(L, strlen(basename) <= CONFIG_NODEMCU_FS_OBJ_NAME_LEN && strlen(newname) == len, 2, "filename invalid");
if(0 <= vfs_rename( oldname, newname )){
const char *oldname = luaL_checkstring( L, 1);
const char *newname = luaL_checkstring( L, 2);
if (rename(oldname, newname) == 0) {
lua_pushboolean(L, 1);
} else {
lua_pushboolean(L, 0);
......@@ -318,212 +92,14 @@ static int file_rename( lua_State* L )
return 1;
}
// Lua: stat(filename)
static int file_stat( lua_State* L )
{
size_t len;
const char *fname = luaL_checklstring( L, 1, &len );
luaL_argcheck( L, strlen(fname) <= CONFIG_NODEMCU_FS_OBJ_NAME_LEN && strlen(fname) == len, 1, "filename invalid" );
struct vfs_stat stat;
if (vfs_stat( (char *)fname, &stat ) != VFS_RES_OK) {
lua_pushnil( L );
return 1;
}
lua_createtable( L, 0, 7 );
lua_pushinteger( L, stat.size );
lua_setfield( L, -2, "size" );
lua_pushstring( L, stat.name );
lua_setfield( L, -2, "name" );
lua_pushboolean( L, stat.is_dir );
lua_setfield( L, -2, "is_dir" );
lua_pushboolean( L, stat.is_rdonly );
lua_setfield( L, -2, "is_rdonly" );
lua_pushboolean( L, stat.is_hidden );
lua_setfield( L, -2, "is_hidden" );
lua_pushboolean( L, stat.is_sys );
lua_setfield( L, -2, "is_sys" );
lua_pushboolean( L, stat.is_arch );
lua_setfield( L, -2, "is_arch" );
// time stamp as sub-table
lua_createtable( L, 0, 6 );
lua_pushinteger( L, stat.tm_valid ? stat.tm.year : FILE_TIMEDEF_YEAR );
lua_setfield( L, -2, "year" );
lua_pushinteger( L, stat.tm_valid ? stat.tm.mon : FILE_TIMEDEF_MON );
lua_setfield( L, -2, "mon" );
lua_pushinteger( L, stat.tm_valid ? stat.tm.day : FILE_TIMEDEF_DAY );
lua_setfield( L, -2, "day" );
lua_pushinteger( L, stat.tm_valid ? stat.tm.hour : FILE_TIMEDEF_HOUR );
lua_setfield( L, -2, "hour" );
lua_pushinteger( L, stat.tm_valid ? stat.tm.min : FILE_TIMEDEF_MIN );
lua_setfield( L, -2, "min" );
lua_pushinteger( L, stat.tm_valid ? stat.tm.sec : FILE_TIMEDEF_SEC );
lua_setfield( L, -2, "sec" );
lua_setfield( L, -2, "time" );
return 1;
}
// g_read()
static int file_g_read( lua_State* L, int n, int16_t end_char, int fd )
{
char *heap_mem = NULL;
if(n <= 0)
n = FILE_READ_CHUNK;
if(end_char < 0 || end_char >255)
end_char = EOF;
if(!fd)
return luaL_error(L, "open a file first");
char *p;
int i;
size_t bufsize = n;
if (n > LUAL_BUFFERSIZE) {
// get buffer from heap
p = heap_mem = luaM_malloc(L, bufsize);
} else {
// small chunks go onto the stack
p = alloca(bufsize);
}
n = vfs_read(fd, p, n);
// bypass search if no end character provided
if (n > 0 && end_char != EOF) {
for (i = 0; i < n; ++i)
if (p[i] == end_char)
{
++i;
break;
}
} else {
i = n;
}
int err = 0;
if (i == 0 || n == VFS_RES_ERR) {
lua_pushnil(L);
} else {
vfs_lseek(fd, -(n - i), VFS_SEEK_CUR);
err = luaX_pushlstring(L, p, i); // On error it will return nonzero and leave a message on top of the stack.
}
if (heap_mem) {
luaN_freearray(L, heap_mem, bufsize);
}
if (err){
lua_error(L); // luaX_pushlstring failed and the error message is on top of the stack. Throw it.
// never returns
}
return 1;
}
// Lua: read()
// file.read() will read all byte in file
// file.read(10) will read 10 byte from file, or EOF is reached.
// file.read('q') will read until 'q' or EOF is reached.
static int file_read( lua_State* L )
{
unsigned need_len = FILE_READ_CHUNK;
int16_t end_char = EOF;
size_t el;
GET_FILE_OBJ;
if( lua_type( L, argpos ) == LUA_TNUMBER )
{
need_len = ( unsigned )luaL_checkinteger( L, argpos );
}
else if(lua_isstring(L, argpos))
{
const char *end = luaL_checklstring( L, argpos, &el );
if(el!=1){
return luaL_error( L, "wrong arg range" );
}
end_char = (int16_t)end[0];
}
return file_g_read(L, need_len, end_char, fd);
}
// Lua: readline()
static int file_readline( lua_State* L )
{
GET_FILE_OBJ;
return file_g_read(L, FILE_READ_CHUNK, '\n', fd);
}
// Lua: write("string")
static int file_write( lua_State* L )
{
GET_FILE_OBJ;
if(!fd)
return luaL_error(L, "open a file first");
size_t l, rl;
const char *s = luaL_checklstring(L, argpos, &l);
rl = vfs_write(fd, s, l);
if(rl==l)
lua_pushboolean(L, 1);
else
lua_pushnil(L);
return 1;
}
// Lua: writeline("string")
static int file_writeline( lua_State* L )
{
GET_FILE_OBJ;
if(!fd)
return luaL_error(L, "open a file first");
size_t l, rl;
const char *s = luaL_checklstring(L, argpos, &l);
rl = vfs_write(fd, s, l);
if(rl==l){
rl = vfs_write(fd, "\n", 1);
if(rl==1)
lua_pushboolean(L, 1);
else
lua_pushnil(L);
}
else{
lua_pushnil(L);
}
return 1;
}
// Lua: fsinfo()
static int file_fsinfo( lua_State* L )
{
u32_t total, used;
if (vfs_fsinfo("", &total, &used)) {
return luaL_error(L, "file system failed");
}
NODE_DBG("total: %d, used:%d\n", total, used);
size_t total, used;
if (esp_spiffs_info(default_fs_label, &total, &used) != ESP_OK)
return luaL_error(L, "spiffs file system not mounted");
if(total>0x7FFFFFFF || used>0x7FFFFFFF || used > total)
{
return luaL_error(L, "file system error");
......@@ -534,62 +110,16 @@ static int file_fsinfo( lua_State* L )
return 3;
}
typedef struct {
vfs_vol *vol;
} volume_type;
#ifdef CONFIG_NODEMCU_BUILD_FATFS
// Lua: success = file.chdir("/SD0/")
static int file_chdir( lua_State *L )
{
const char *path = luaL_checkstring( L, 1 );
lua_pushboolean( L, 0 <= vfs_chdir( path ) );
return 1;
}
#endif
LROT_BEGIN(file_obj, NULL, LROT_MASK_GC_INDEX)
LROT_FUNCENTRY( __gc, file_obj_free )
LROT_TABENTRY ( __index, file_obj )
LROT_FUNCENTRY( close, file_close )
LROT_FUNCENTRY( read, file_read )
LROT_FUNCENTRY( readline, file_readline )
LROT_FUNCENTRY( write, file_write )
LROT_FUNCENTRY( writeline, file_writeline )
LROT_FUNCENTRY( seek, file_seek )
LROT_FUNCENTRY( flush, file_flush )
LROT_END(file_obj, NULL, LROT_MASK_GC_INDEX)
// Module function map
LROT_BEGIN(file, NULL, 0)
LROT_FUNCENTRY( list, file_list )
LROT_FUNCENTRY( open, file_open )
LROT_FUNCENTRY( close, file_close )
LROT_FUNCENTRY( write, file_write )
LROT_FUNCENTRY( writeline, file_writeline )
LROT_FUNCENTRY( read, file_read )
LROT_FUNCENTRY( readline, file_readline )
#ifdef CONFIG_NODEMCU_BUILD_SPIFFS
LROT_FUNCENTRY( format, file_format )
LROT_FUNCENTRY( fscfg, file_fscfg )
#endif
LROT_FUNCENTRY( remove, file_remove )
LROT_FUNCENTRY( seek, file_seek )
LROT_FUNCENTRY( flush, file_flush )
LROT_FUNCENTRY( rename, file_rename )
LROT_FUNCENTRY( exists, file_exists )
LROT_FUNCENTRY( fsinfo, file_fsinfo )
LROT_FUNCENTRY( on, file_on )
LROT_FUNCENTRY( stat, file_stat )
#ifdef CONFIG_NODEMCU_BUILD_FATFS
LROT_FUNCENTRY( chdir, file_chdir )
#endif
LROT_END(file, NULL, 0)
int luaopen_file( lua_State *L ) {
luaL_rometatable( L, "file.obj", LROT_TABLEREF(file_obj));
return 0;
}
NODEMCU_MODULE(FILE, "file", file, luaopen_file);
NODEMCU_MODULE(FILE, "file", file, NULL);
......@@ -4,7 +4,6 @@
#include "lundump.h"
#include "platform.h"
#include "task/task.h"
#include "vfs.h"
#include "esp_system.h"
#include "esp_log.h"
#include "esp_sleep.h"
......@@ -572,14 +571,13 @@ static int node_egc_setmode(lua_State* L) {
static int writer(lua_State* L, const void* p, size_t size, void* u)
{
UNUSED(L);
int file_fd = *( (int *)u );
if (!file_fd)
FILE *file = (FILE *)u;
if (!file)
return 1;
NODE_DBG("get fd:%d,size:%d\n", file_fd, size);
if (size != 0 && (size != vfs_write(file_fd, (const char *)p, size)) )
if (size != 0 && (size != fwrite((const char *)p, size, 1, file)) )
return 1;
NODE_DBG("write fd:%d,size:%d\n", file_fd, size);
return 0;
}
......@@ -589,11 +587,9 @@ static int writer(lua_State* L, const void* p, size_t size, void* u)
static int node_compile( lua_State* L )
{
Proto* f;
int file_fd = 0;
FILE *file = 0;
size_t len;
const char *fname = luaL_checklstring( L, 1, &len );
const char *basename = vfs_basename( fname );
luaL_argcheck(L, strlen(basename) <= CONFIG_NODEMCU_FS_OBJ_NAME_LEN && strlen(fname) == len, 1, "filename invalid");
char *output = luaM_malloc( L, len+1 );
strcpy(output, fname);
......@@ -616,23 +612,22 @@ static int node_compile( lua_State* L )
int stripping = 1; /* strip debug information? */
file_fd = vfs_open(output, "w+");
if (!file_fd)
file = fopen(output, "w+");
if (!file)
{
luaM_free( L, output );
return luaL_error(L, "cannot open/write to file");
}
lua_lock(L);
int result = luaU_dump(L, f, writer, &file_fd, stripping);
int result = luaU_dump(L, f, writer, file, stripping);
lua_unlock(L);
if (vfs_flush(file_fd) != VFS_RES_OK) {
if (fflush(file) != 0) {
// overwrite Lua error, like writer() does in case of a file io error
result = 1;
}
vfs_close(file_fd);
file_fd = 0;
fclose(file);
luaM_free( L, output );
if (result == LUA_ERR_CC_INTOVERFLOW) {
......
......@@ -4,7 +4,9 @@
#include "lmem.h"
#include "vfs.h"
#include "esp_vfs_fat.h"
#include "diskio_impl.h" // for ff_diskio_get_drive
#include "diskio_sdmmc.h"
#include "driver/sdmmc_host.h"
#include "sdmmc_cmd.h"
......@@ -12,9 +14,8 @@
#include "common.h"
// the index of cards here must be aligned with the pdrv for fatfs
// see FF_VOLUMES in ffconf.h
#define NUM_CARDS 4
// We're limiting ourselves to the number of FAT volumes configured.
#define NUM_CARDS FF_VOLUMES
sdmmc_card_t *lsdmmc_card[NUM_CARDS];
// local definition for SDSPI host
......@@ -24,10 +25,74 @@ sdmmc_card_t *lsdmmc_card[NUM_CARDS];
typedef struct {
sdmmc_card_t *card;
vfs_vol *vol;
FATFS *fs;
int base_path_ref;
BYTE pdrv;
} lsdmmc_ud_t;
static void choose_partition(uint8_t pdrv, uint8_t part)
{
// Update the volume->partition mapping in FatFS
for (unsigned i = 0; i < FF_VOLUMES; ++i)
{
if (VolToPart[i].pd == pdrv)
VolToPart[i].pt = part;
}
}
static esp_err_t sdmmc_mount_fat(lsdmmc_ud_t *ud, const char *base_path, uint8_t partition)
{
esp_err_t err = ff_diskio_get_drive(&ud->pdrv);
if (err != ESP_OK)
return err;
choose_partition(ud->pdrv, partition);
ff_diskio_register_sdmmc(ud->pdrv, ud->card);
char drv[3] = { (char)('0' + ud->pdrv), ':', 0 };
err = esp_vfs_fat_register(
base_path, drv, CONFIG_NODEMCU_MAX_OPEN_FILES, &ud->fs);
if (err != ESP_OK)
goto fail;
if (f_mount(ud->fs, drv, 1) != FR_OK)
{
err = ESP_FAIL;
goto fail;
}
return ESP_OK;
fail:
if (ud->fs)
{
f_mount(NULL, drv, 0);
ud->fs = NULL;
}
esp_vfs_fat_unregister_path(base_path);
choose_partition(ud->pdrv, 0);
ff_diskio_unregister(ud->pdrv);
return err;
}
static esp_err_t sdmmc_unmount_fat(lsdmmc_ud_t *ud, const char *base_path)
{
char drv[3] = { (char)('0' + ud->pdrv), ':', 0 };
f_mount(NULL, drv, 0);
ud->fs = NULL;
esp_err_t err = esp_vfs_fat_unregister_path(base_path);
ff_diskio_unregister(ud->pdrv);
return err;
}
static int lsdmmc_init( lua_State *L )
{
const char *err_msg = "";
......@@ -135,8 +200,9 @@ static int lsdmmc_init( lua_State *L )
luaL_getmetatable(L, "sdmmc.card");
lua_setmetatable(L, -2);
ud->card = lsdmmc_card[card_idx];
ud->vol = NULL;
ud->base_path_ref = LUA_NOREF;
ud->fs = NULL;
ud->pdrv = 0;
// all done
return 1;
}
......@@ -155,6 +221,9 @@ static int lsdmmc_init( lua_State *L )
} else
err_msg = "failed to init sdmmc host";
luaM_free(L, lsdmmc_card[card_idx]);
lsdmmc_card[card_idx] = NULL;
return luaL_error( L, err_msg );
}
......@@ -279,54 +348,53 @@ static int lsdmmc_get_info( lua_State *L )
// Lua: card:mount("/SD0"[, partition])
static int lsdmmc_mount( lua_State *L )
{
const char *err_msg = "";
GET_CARD_UD;
(void)card;
int stack = 1;
const char *ldrv = luaL_checkstring( L, ++stack );
int num = luaL_optint( L, ++stack, -1 );
const char *ldrv = luaL_checkstring(L, 2);
int part = luaL_optint(L, 3, 0);
if (ud->vol == NULL) {
lua_settop(L, 2);
if ((ud->vol = vfs_mount( ldrv, num )))
lua_pushboolean( L, true );
if (ud->fs == NULL)
{
esp_err_t err = sdmmc_mount_fat(ud, ldrv, part);
if (err == ESP_OK)
{
// We need this when we unmount
ud->base_path_ref = luaL_ref(L, LUA_REGISTRYINDEX);
lua_pushboolean(L, true);
}
else
lua_pushboolean( L, false );
lua_pushboolean(L, false);
return 1;
} else
err_msg = "already mounted";
return luaL_error( L, err_msg );
}
else
return luaL_error(L, "already mounted");
}
// Lua: card:umount()
static int lsdmmc_umount( lua_State *L )
{
const char *err_msg = "";
GET_CARD_UD;
(void)card;
if (ud->vol) {
if (vfs_umount( ud->vol ) == VFS_RES_OK) {
ud->vol = NULL;
// all ok
return 0;
} else
err_msg = "umount failed";
} else
err_msg = "not mounted";
return luaL_error( L, err_msg );
if (ud->fs)
{
lua_rawgeti(L, LUA_REGISTRYINDEX, ud->base_path_ref);
const char *base_path = lua_tostring(L, -1);
luaL_unref2(L, LUA_REGISTRYINDEX, ud->base_path_ref);
esp_err_t err = sdmmc_unmount_fat(ud, base_path);
if (err != ESP_OK)
return luaL_error(L, "unmount reported error code %d", err);
return 0;
}
else
return luaL_error(L, "not mounted");
}
LROT_BEGIN(sdmmc_card, NULL, LROT_MASK_INDEX)
LROT_TABENTRY( __index, sdmmc_card )
LROT_FUNCENTRY( read, lsdmmc_read )
......
......@@ -19,7 +19,7 @@
// assuming system_timer_reinit() has *not* been called
#define MAX_TIMEOUT_DEF 6870947 //SDK 1.5.3 limit (0x68D7A3)
static const uint32 MAX_TIMEOUT=MAX_TIMEOUT_DEF;
static const uint32_t MAX_TIMEOUT=MAX_TIMEOUT_DEF;
static const char* MAX_TIMEOUT_ERR_STR = "Range: 1-"STRINGIFY(MAX_TIMEOUT_DEF);
typedef struct{
......
idf_component_register(
SRCS "dht.c" "flash_api.c" "flash_fs.c" "onewire.c" "platform.c"
SRCS "dht.c" "flash_api.c" "onewire.c" "platform.c"
"platform_flash.c" "platform_partition.c" "platform_rmt.c"
"u8x8_nodemcu_hal.c" "ucg_nodemcu_hal.c" "vfs.c" "wdt.c" "ws2812.c"
"u8x8_nodemcu_hal.c" "ucg_nodemcu_hal.c" "wdt.c" "ws2812.c"
INCLUDE_DIRS "include"
REQUIRES "spiffs" "u8g2" "ucg" "driver_i2c" "task"
PRIV_REQUIRES "bootloader_support" "lua" "esp32"
......
......@@ -17,29 +17,25 @@ menu "NodeMCU platform config"
output, something has gone seriously wrong and you probably want
to know about it.
config NODEMCU_FS_OBJ_NAME_LEN
int "Make filesystem object name length"
default 31
config NODEMCU_DEFAULT_SPIFFS_LABEL
string "Partition label of SPIFFS to mount as default file system"
default ""
help
Maximum name of filesystem objects (files, directories).
If there are multiple SPIFFS partitions, this setting can
be used to control which partition gets mounted as the
default file system. Leaving it empty defaults to using
the first SPIFFS partition found.
config NODEMCU_SPIFFS_MAX_OPEN_FILES
int "Maximum number of open files for SPIFFS"
config NODEMCU_MAX_OPEN_FILES
int "Maximum number of open files"
default 4
help
Maximum number of open files for SPIFFS
config NODEMCU_BUILD_SPIFFS
bool
default "y"
# I don't think we can deal without SPIFFS at this point, so always on for now
config NODEMCU_BUILD_FATFS
bool "Support for FAT filesystems"
default "n"
select NODEMCU_CMODULE_SDMMC
help
Include support for accessing FAT filesystems on SD cards.
This sets the maximum number of open files at the same time.
Technically, it's per file system, but the most common use
case only has a single file system (internal SPIFFS) so in
that case it is the number of concurrent files that can be
opened. Raising this limit will incur some extra memory
overhead.
config NODEMCU_EMBED_LFS
bool "Embed LFS as part of the NodeMCU firmware"
......
......@@ -241,7 +241,6 @@ int platform_flash_erase_sector( uint32_t sector_id );
#define PLATFORM_PARTITION_SUBTYPE_DATA_RF 0x01
#define PLATFORM_PARTITION_SUBTYPE_DATA_WIFI 0x02
#define PLATFORM_PARTITION_SUBTYPE_NODEMCU_SPIFFS 0x00
#define PLATFORM_PARTITION_SUBTYPE_NODEMCU_LFS 0x01
typedef struct {
......
#ifndef __VFS_H__
#define __VFS_H__
#include <stdint.h>
#include <stdbool.h>
#include "sdkconfig.h"
#include "vfs_int.h"
// DEPRECATED, DON'T USE
// Check for fd != 0 instead
#define FS_OPEN_OK 1
// ---------------------------------------------------------------------------
// file functions
//
// vfs_close - close file descriptor and free memory
// fd: file descriptor
// Returns: VFS_RES_OK or negative value in case of error
static inline int32_t vfs_close( int fd ) {
vfs_file *f = (vfs_file *)fd;
return f ? f->fns->close( f ) : VFS_RES_ERR;
}
// vfs_read - read data from file
// fd: file descriptor
// ptr: destination data buffer
// len: requested length
// Returns: Number of bytes read, or VFS_RES_ERR in case of error
static inline int32_t vfs_read( int fd, void *ptr, size_t len ) {
vfs_file *f = (vfs_file *)fd;
return f ? f->fns->read( f, ptr, len ) : VFS_RES_ERR;
}
// vfs_write - write data to file
// fd: file descriptor
// ptr: source data buffer
// len: requested length
// Returns: Number of bytes written, or VFS_RES_ERR in case of error
static inline int32_t vfs_write( int fd, const void *ptr, size_t len ) {
vfs_file *f = (vfs_file *)fd;
return f ? f->fns->write( f, ptr, len ) : VFS_RES_ERR;
}
int vfs_getc( int fd );
int vfs_ungetc( int c, int fd );
// vfs_lseek - move read/write pointer
// fd: file descriptor
// off: offset
// whence: VFS_SEEK_SET - set pointer to off
// VFS_SEEK_CUR - set pointer to current position + off
// VFS_SEEK_END - set pointer to end of file + off
// Returns: New position, or VFS_RES_ERR in case of error
static inline int32_t vfs_lseek( int fd, int32_t off, int whence ) {
vfs_file *f = (vfs_file *)fd;
return f ? f->fns->lseek( f, off, whence ) : VFS_RES_ERR;
}
// vfs_eof - test for end-of-file
// fd: file descriptor
// Returns: 0 if not at end, != 0 if end of file
static inline int32_t vfs_eof( int fd ) {
vfs_file *f = (vfs_file *)fd;
return f ? f->fns->eof( f ) : VFS_RES_ERR;
}
// vfs_tell - get read/write position
// fd: file descriptor
// Returns: Current position
static inline int32_t vfs_tell( int fd ) {
vfs_file *f = (vfs_file *)fd;
return f ? f->fns->tell( f ) : VFS_RES_ERR;
}
// vfs_flush - flush write cache to file
// fd: file descriptor
// Returns: VFS_RES_OK, or VFS_RES_ERR in case of error
static inline int32_t vfs_flush( int fd ) {
vfs_file *f = (vfs_file *)fd;
return f ? f->fns->flush( f ) : VFS_RES_ERR;
}
// vfs_size - get current file size
// fd: file descriptor
// Returns: File size
static inline uint32_t vfs_size( int fd ) {
vfs_file *f = (vfs_file *)fd;
return f ? f->fns->size( f ) : 0;
}
// vfs_ferrno - get file system specific errno
// fd: file descriptor
// Returns: errno
int32_t vfs_ferrno( int fd );
// ---------------------------------------------------------------------------
// dir functions
//
// vfs_closedir - close directory descriptor and free memory
// dd: dir descriptor
// Returns: VFS_RES_OK, or VFS_RES_ERR in case of error
static inline int32_t vfs_closedir( vfs_dir *dd ) { return dd->fns->close( dd ); }
// vfs_readdir - read next directory item
// dd: dir descriptor
// buf: pre-allocated stat structure to be filled in
// Returns: VFS_RES_OK if next item found, otherwise VFS_RES_ERR
static inline int32_t vfs_readdir( vfs_dir *dd, struct vfs_stat *buf ) { return dd->fns->readdir( dd, buf ); }
// ---------------------------------------------------------------------------
// volume functions
//
// vfs_umount - unmount logical drive and free memory
// vol: volume object
// Returns: VFS_RES_OK, or VFS_RES_ERR in case of error
static inline int32_t vfs_umount( vfs_vol *vol ) { return vol->fns->umount( vol ); }
// ---------------------------------------------------------------------------
// file system functions
//
// vfs_mount - unmount logical drive
// name: name of logical drive
// num: drive's physical number (eg. SS/CS pin), negative values are ignored
// Returns: Volume object, or NULL in case of error
vfs_vol *vfs_mount( const char *name, int num );
// vfs_open - open file
// name: file name
// mode: open mode
// Returns: File descriptor, or NULL in case of error
int vfs_open( const char *name, const char *mode );
// vfs_opendir - open directory
// name: dir name
// Returns: Directory descriptor, or NULL in case of error
vfs_dir *vfs_opendir( const char *name );
// vfs_stat - stat file or directory
// name: file or directory name
// buf: pre-allocated structure to be filled in
// Returns: VFS_RES_OK, or VFS_RES_ERR in case of error
int32_t vfs_stat( const char *name, struct vfs_stat *buf );
// vfs_remove - remove file or directory
// name: file or directory name
// Returns: VFS_RES_OK, or VFS_RES_ERR in case of error
int32_t vfs_remove( const char *name );
// vfs_rename - rename file or directory
// name: file or directory name
// Returns: VFS_RES_OK, or VFS_RES_ERR in case of error
int32_t vfs_rename( const char *oldname, const char *newname );
// vfs_mkdir - create directory
// name: directory name
// Returns: VFS_RES_OK, or VFS_RES_ERR in case of error
int32_t vfs_mkdir( const char *name );
// vfs_fsinfo - get file system info
// name: logical drive identifier
// total: receives total amount
// used: received used amount
// Returns: VFS_RES_OK, or VFS_RES_ERR in case of error
int32_t vfs_fsinfo( const char *name, uint32_t *total, uint32_t *used );
// vfs_format - format file system
// Returns: 1, or 0 in case of error
int32_t vfs_format( void );
// vfs_chdir - change default directory
// path: new default directory
// Returns: VFS_RES_OK, or VFS_RES_ERR in case of error
int32_t vfs_chdir( const char *path );
// vfs_fscfg - query configuration settings of file system
// phys_addr: pointer to store physical address information
// phys_size: pointer to store physical size information
// Returns: VFS_RES_OK, or VFS_RES_ERR in case of error
int32_t vfs_fscfg( const char *name, uint32_t *phys_addr, uint32_t *phys_size);
// vfs_errno - get file system specific errno
// name: logical drive identifier
// Returns: errno
int32_t vfs_errno( const char *name );
// vfs_clearerr - cleaer file system specific errno
void vfs_clearerr( const char *name );
// vfs_register_rtc_cb - register callback function for RTC query
// cb: pointer to callback function
void vfs_register_rtc_cb( int32_t (*cb)( vfs_time *tm ) );
// vfs_basename - identify basename (incl. extension)
// path: full file system path
// Returns: pointer to basename within path string
const char *vfs_basename( const char *path );
#endif
// internal definitions for vfs
#ifndef __VFS_INT_H__
#define __VFS_INT_H__
#include <string.h>
#include <stdint.h>
#if 0
#include "spiffs.h"
#include "fatfs_prefix_lib.h"
#include "ff.h"
#endif
#define VFS_EOF -1
enum vfs_filesystems {
VFS_FS_NONE = 0,
VFS_FS_SPIFFS,
VFS_FS_FATFS
};
enum vfs_seek {
VFS_SEEK_SET = 0,
VFS_SEEK_CUR,
VFS_SEEK_END
};
enum vfs_result {
VFS_RES_OK = 0,
VFS_RES_ERR = -1
};
struct vfs_time {
int year, mon, day;
int hour, min, sec;
};
typedef struct vfs_time vfs_time;
// generic file descriptor
struct vfs_file {
int fs_type;
const struct vfs_file_fns *fns;
};
typedef const struct vfs_file vfs_file;
// stat data
struct vfs_stat {
uint32_t size;
char name[CONFIG_NODEMCU_FS_OBJ_NAME_LEN+1];
struct vfs_time tm;
uint8_t tm_valid;
uint8_t is_dir;
uint8_t is_rdonly;
uint8_t is_hidden;
uint8_t is_sys;
uint8_t is_arch;
};
// file descriptor functions
struct vfs_file_fns {
int32_t (*close)( const struct vfs_file *fd );
int32_t (*read)( const struct vfs_file *fd, void *ptr, size_t len );
int32_t (*write)( const struct vfs_file *fd, const void *ptr, size_t len );
int32_t (*lseek)( const struct vfs_file *fd, int32_t off, int whence );
int32_t (*eof)( const struct vfs_file *fd );
int32_t (*tell)( const struct vfs_file *fd );
int32_t (*flush)( const struct vfs_file *fd );
uint32_t (*size)( const struct vfs_file *fd );
int32_t (*ferrno)( const struct vfs_file *fd );
};
typedef const struct vfs_file_fns vfs_file_fns;
// generic dir descriptor
struct vfs_dir {
int fs_type;
const struct vfs_dir_fns *fns;
};
typedef const struct vfs_dir vfs_dir;
// dir descriptor functions
struct vfs_dir_fns {
int32_t (*close)( const struct vfs_dir *dd );
int32_t (*readdir)( const struct vfs_dir *dd, struct vfs_stat *buf );
};
typedef const struct vfs_dir_fns vfs_dir_fns;
// generic volume descriptor
struct vfs_vol {
int fs_type;
const struct vfs_vol_fns *fns;
};
typedef const struct vfs_vol vfs_vol;
// volume functions
struct vfs_vol_fns {
int32_t (*umount)( const struct vfs_vol *vol );
};
typedef const struct vfs_vol_fns vfs_vol_fns;
struct vfs_fs_fns {
vfs_vol *(*mount)( const char *name, int num );
vfs_file *(*open)( const char *name, const char *mode );
vfs_dir *(*opendir)( const char *name );
int32_t (*stat)( const char *name, struct vfs_stat *buf );
int32_t (*remove)( const char *name );
int32_t (*rename)( const char *oldname, const char *newname );
int32_t (*mkdir)( const char *name );
int32_t (*fsinfo)( uint32_t *total, uint32_t *used );
int32_t (*fscfg)( uint32_t *phys_addr, uint32_t *phys_size );
int32_t (*format)( void );
int32_t (*chdrive)( const char * );
int32_t (*chdir)( const char * );
int32_t (*ferrno)( void );
void (*clearerr)( void );
};
typedef const struct vfs_fs_fns vfs_fs_fns;
vfs_fs_fns *myspiffs_realm( const char *inname, char **outname, int set_current_drive );
vfs_fs_fns *myfatfs_realm( const char *inname, char **outname, int set_current_drive );
int32_t vfs_get_rtc( vfs_time *tm );
#endif
......@@ -3,6 +3,6 @@
nvs, data, nvs, 0x9000, 0x6000
phy_init, data, phy, 0xf000, 0x1000
factory, app, factory, 0x10000, 0x180000
# Type 0xC2 => NodeMCU. SubTypes: 0x00 = SPIFFS, 0x01 = LFS
lfs, 0xC2, 0x01, , 0x10000
nodemcuspiffs, 0xC2, 0x00, , 0x60000
# Type 0xC2 => NodeMCU. 0x01 = LFS
lfs, 0xC2, 0x01, , 0x10000
storage, data, spiffs, , 0x60000
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment