Commit b62fae91 authored by Terry Ellison's avatar Terry Ellison Committed by GitHub
Browse files

Merge pull request #2055 from nodemcu/dev

2.1.0-follow-up master drop
parents c8ac5cfb fee5608c
......@@ -428,7 +428,7 @@ static int tmr_create( lua_State *L ) {
}
#if defined(SWTMR_DEBUG)
#if defined(ENABLE_TIMER_SUSPEND) && defined(SWTMR_DEBUG)
static void tmr_printRegistry(lua_State* L){
swtmr_print_registry();
}
......@@ -463,7 +463,7 @@ static const LUA_REG_TYPE tmr_dyn_map[] = {
{ LNILKEY, LNILVAL }
};
#if defined(SWTMR_DEBUG)
#if defined(ENABLE_TIMER_SUSPEND) && defined(SWTMR_DEBUG)
static const LUA_REG_TYPE tmr_dbg_map[] = {
{ LSTRKEY( "printRegistry" ), LFUNCVAL( tmr_printRegistry ) },
{ LSTRKEY( "printSuspended" ), LFUNCVAL( tmr_printSuspended ) },
......@@ -492,7 +492,7 @@ static const LUA_REG_TYPE tmr_map[] = {
{ LSTRKEY( "state" ), LFUNCVAL( tmr_state ) },
{ LSTRKEY( "interval" ), LFUNCVAL( tmr_interval ) },
{ LSTRKEY( "create" ), LFUNCVAL( tmr_create ) },
#if defined(SWTMR_DEBUG)
#if defined(ENABLE_TIMER_SUSPEND) && defined(SWTMR_DEBUG)
{ LSTRKEY( "debug" ), LROVAL( tmr_dbg_map ) },
#endif
{ LSTRKEY( "ALARM_SINGLE" ), LNUMVAL( TIMER_MODE_SINGLE ) },
......
......@@ -666,13 +666,14 @@ static int wifi_station_getconfig( lua_State* L, bool get_flash_cfg)
lua_setfield(L, -2, "pwd");
}
if(sta_conf.bssid_set==1)
{
memset(temp, 0, sizeof(temp));
c_sprintf(temp, MACSTR, MAC2STR(sta_conf.bssid));
lua_pushstring( L, temp);
lua_setfield(L, -2, "bssid");
}
lua_pushboolean(L, sta_conf.bssid_set);
lua_setfield(L, -2, "bssid_set");
memset(temp, 0, sizeof(temp));
c_sprintf(temp, MACSTR, MAC2STR(sta_conf.bssid));
lua_pushstring( L, temp);
lua_setfield(L, -2, "bssid");
return 1;
}
else
......@@ -829,66 +830,110 @@ static int wifi_station_config( lua_State* L )
}
else
{
save_to_flash=false;
save_to_flash=true;
}
lua_pop(L, 1);
}
else //to be deprecated
{
platform_print_deprecation_note("Argument style station configuration is replaced by table style station configuration", "in the next version");
const char *ssid = luaL_checklstring( L, 1, &sl );
luaL_argcheck(L, (sl>=0 && sl<sizeof(sta_conf.ssid)), 1, "length:0-32"); /* Zero-length SSID is valid as a way to clear config */
memcpy(sta_conf.ssid, ssid, sl);
#ifdef WIFI_SDK_EVENT_MONITOR_ENABLE
const char *password = luaL_checklstring( L, 2, &pl );
luaL_argcheck(L, (pl>=0 && pl<=sizeof(sta_conf.password)), 2, "length:0-64"); /* WPA = min 8, WEP = min 5 ASCII characters for a 40-bit key */
lua_State* L_temp = NULL;
memcpy(sta_conf.password, password, pl);
if(lua_isnumber(L, 3))
lua_getfield(L, 1, "connect_cb");
if (!lua_isnil(L, -1))
{
lua_Integer lint=luaL_checkinteger( L, 3 );
if ( lint != 0 && lint != 1)
return luaL_error( L, "wrong arg type" );
auto_connect=(bool)lint;
if (lua_isfunction(L, -1))
{
L_temp = lua_newthread(L);
lua_pushnumber(L, EVENT_STAMODE_CONNECTED);
lua_pushvalue(L, -3);
lua_xmove(L, L_temp, 2);
wifi_event_monitor_register(L_temp);
}
else
{
return luaL_argerror(L, 1, "connect_cb:not function");
}
}
else if (lua_isstring(L, 3)&& !(lua_isnumber(L, 3)))
{
lua_pushnil(L);
lua_insert(L, 3);
lua_pop(L, 1);
lua_getfield(L, 1, "disconnect_cb");
if (!lua_isnil(L, -1))
{
if (lua_isfunction(L, -1))
{
L_temp = lua_newthread(L);
lua_pushnumber(L, EVENT_STAMODE_DISCONNECTED);
lua_pushvalue(L, -3);
lua_xmove(L, L_temp, 2);
wifi_event_monitor_register(L_temp);
}
else
{
return luaL_argerror(L, 1, "disconnect_cb:not function");
}
}
else
lua_pop(L, 1);
lua_getfield(L, 1, "authmode_change_cb");
if (!lua_isnil(L, -1))
{
if(lua_isnil(L, 3))
return luaL_error( L, "wrong arg type" );
auto_connect=1;
if (lua_isfunction(L, -1))
{
L_temp = lua_newthread(L);
lua_pushnumber(L, EVENT_STAMODE_AUTHMODE_CHANGE);
lua_pushvalue(L, -3);
lua_xmove(L, L_temp, 2);
wifi_event_monitor_register(L_temp);
}
else
{
return luaL_argerror(L, 1, "authmode_change_cb:not function");
}
}
lua_pop(L, 1);
if(lua_isnumber(L, 4))
lua_getfield(L, 1, "got_ip_cb");
if (!lua_isnil(L, -1))
{
sta_conf.bssid_set = 0;
memset(sta_conf.bssid, 0, sizeof(sta_conf.bssid));
if (lua_isfunction(L, -1))
{
L_temp = lua_newthread(L);
lua_pushnumber(L, EVENT_STAMODE_GOT_IP);
lua_pushvalue(L, -3);
lua_xmove(L, L_temp, 2);
wifi_event_monitor_register(L_temp);
}
else
{
return luaL_argerror(L, 1, "gotip_cb:not function");
}
}
else
lua_pop(L, 1);
lua_getfield(L, 1, "dhcp_timeout_cb");
if (!lua_isnil(L, -1))
{
if (lua_isstring(L, 4))
if (lua_isfunction(L, -1))
{
const char *macaddr = luaL_checklstring( L, 4, &ml );
luaL_argcheck(L, ml==sizeof("AA:BB:CC:DD:EE:FF")-1, 1, INVALID_MAC_STR);
memset(sta_conf.bssid, 0, sizeof(sta_conf.bssid));
ets_str2macaddr(sta_conf.bssid, macaddr);
sta_conf.bssid_set = 1;
L_temp = lua_newthread(L);
lua_pushnumber(L, EVENT_STAMODE_DHCP_TIMEOUT);
lua_pushvalue(L, -3);
lua_xmove(L, L_temp, 2);
wifi_event_monitor_register(L_temp);
}
else
{
sta_conf.bssid_set = 0;
memset(sta_conf.bssid, 0, sizeof(sta_conf.bssid));
return luaL_argerror(L, 1, "dhcp_timeout_cb:not function");
}
}
lua_pop(L, 1);
#endif
}
else
{
return luaL_argerror(L, 1, "config table not found!");
}
#if defined(WIFI_DEBUG)
......@@ -930,6 +975,13 @@ static int wifi_station_config( lua_State* L )
// Lua: wifi.sta.connect()
static int wifi_station_connect4lua( lua_State* L )
{
#ifdef WIFI_SDK_EVENT_MONITOR_ENABLE
if(lua_isfunction(L, 1)){
lua_pushnumber(L, EVENT_STAMODE_CONNECTED);
lua_pushvalue(L, 1);
wifi_event_monitor_register(L);
}
#endif
wifi_station_connect();
return 0;
}
......@@ -937,6 +989,13 @@ static int wifi_station_connect4lua( lua_State* L )
// Lua: wifi.sta.disconnect()
static int wifi_station_disconnect4lua( lua_State* L )
{
#ifdef WIFI_SDK_EVENT_MONITOR_ENABLE
if(lua_isfunction(L, 1)){
lua_pushnumber(L, EVENT_STAMODE_DISCONNECTED);
lua_pushvalue(L, 1);
wifi_event_monitor_register(L);
}
#endif
wifi_station_disconnect();
return 0;
}
......@@ -1509,6 +1568,65 @@ static int wifi_ap_config( lua_State* L )
}
lua_pop(L, 1);
#ifdef WIFI_SDK_EVENT_MONITOR_ENABLE
lua_State* L_temp = NULL;
lua_getfield(L, 1, "staconnected_cb");
if (!lua_isnil(L, -1))
{
if (lua_isfunction(L, -1))
{
L_temp = lua_newthread(L);
lua_pushnumber(L, EVENT_SOFTAPMODE_STACONNECTED);
lua_pushvalue(L, -3);
lua_xmove(L, L_temp, 2);
wifi_event_monitor_register(L_temp);
}
else
{
return luaL_argerror(L, 1, "staconnected_cb:not function");
}
}
lua_pop(L, 1);
lua_getfield(L, 1, "stadisconnected_cb");
if (!lua_isnil(L, -1))
{
if (lua_isfunction(L, -1))
{
L_temp = lua_newthread(L);
lua_pushnumber(L, EVENT_SOFTAPMODE_STADISCONNECTED);
lua_pushvalue(L, -3);
lua_xmove(L, L_temp, 2);
wifi_event_monitor_register(L_temp);
}
else
{
return luaL_argerror(L, 1, "stadisconnected_cb:not function");
}
}
lua_pop(L, 1);
lua_getfield(L, 1, "probereq_cb");
if (!lua_isnil(L, -1))
{
if (lua_isfunction(L, -1))
{
L_temp = lua_newthread(L);
lua_pushnumber(L, EVENT_SOFTAPMODE_PROBEREQRECVED);
lua_pushvalue(L, -3);
lua_xmove(L, L_temp, 2);
wifi_event_monitor_register(L_temp);
}
else
{
return luaL_argerror(L, 1, "probereq_cb:not function");
}
}
lua_pop(L, 1);
#endif
#if defined(WIFI_DEBUG)
char debug_temp[sizeof(config.password)+1];
......@@ -1629,11 +1747,6 @@ static const LUA_REG_TYPE wifi_station_map[] = {
{ LSTRKEY( "config" ), LFUNCVAL( wifi_station_config ) },
{ LSTRKEY( "connect" ), LFUNCVAL( wifi_station_connect4lua ) },
{ LSTRKEY( "disconnect" ), LFUNCVAL( wifi_station_disconnect4lua ) },
#if defined(WIFI_STATION_STATUS_MONITOR_ENABLE)
{ LSTRKEY( "eventMonReg" ), LFUNCVAL( wifi_station_event_mon_reg ) }, //defined in wifi_eventmon.c
{ LSTRKEY( "eventMonStart" ), LFUNCVAL( wifi_station_event_mon_start ) }, //defined in wifi_eventmon.c
{ LSTRKEY( "eventMonStop" ), LFUNCVAL( wifi_station_event_mon_stop ) }, //defined in wifi_eventmon.c
#endif
{ LSTRKEY( "getap" ), LFUNCVAL( wifi_station_listap ) },
{ LSTRKEY( "getapindex" ), LFUNCVAL( wifi_station_get_ap_index ) },
{ LSTRKEY( "getapinfo" ), LFUNCVAL( wifi_station_get_ap_info4lua ) },
......
......@@ -64,11 +64,7 @@ enum wifi_suspension_state
#ifdef WIFI_SDK_EVENT_MONITOR_ENABLE
extern const LUA_REG_TYPE wifi_event_monitor_map[];
void wifi_eventmon_init();
#endif
#ifdef WIFI_STATION_STATUS_MONITOR_ENABLE
int wifi_station_event_mon_start(lua_State* L);
int wifi_station_event_mon_reg(lua_State* L);
void wifi_station_event_mon_stop(lua_State* L);
int wifi_event_monitor_register(lua_State* L);
#endif
#endif /* APP_MODULES_WIFI_COMMON_H_ */
......@@ -17,100 +17,6 @@
#if defined(LUA_USE_MODULES_WIFI)
#ifdef WIFI_STATION_STATUS_MONITOR_ENABLE
//variables for wifi event monitor
static int wifi_station_status_cb_ref[6] = {[0 ... 6-1] = LUA_NOREF};
static os_timer_t wifi_sta_status_timer;
static uint8 prev_wifi_status=0;
// wifi.sta.eventMonStop()
void wifi_station_event_mon_stop(lua_State* L)
{
os_timer_disarm(&wifi_sta_status_timer);
if(lua_isstring(L,1))
{
int i;
for (i=0; i<6; i++)
{
unregister_lua_cb(L, &wifi_station_status_cb_ref[i]);
}
}
return;
}
static void wifi_status_cb(int arg)
{
lua_State* L = lua_getstate();
if (wifi_get_opmode() == SOFTAP_MODE)
{
os_timer_disarm(&wifi_sta_status_timer);
return;
}
int wifi_status = wifi_station_get_connect_status();
if (wifi_status != prev_wifi_status)
{
if(wifi_station_status_cb_ref[wifi_status] != LUA_NOREF)
{
lua_rawgeti(L, LUA_REGISTRYINDEX, wifi_station_status_cb_ref[wifi_status]);
lua_pushnumber(L, prev_wifi_status);
lua_call(L, 1, 0);
}
}
prev_wifi_status = wifi_status;
}
// wifi.sta.eventMonReg()
int wifi_station_event_mon_reg(lua_State* L)
{
platform_print_deprecation_note("wifi.sta.eventmonreg() is replaced by wifi.eventmon.register()", "in the next version");
uint8 id=(uint8)luaL_checknumber(L, 1);
if ((id > 5)) // verify user specified a valid wifi status
{
return luaL_error( L, "valid wifi status:0-5" );
}
if (lua_type(L, 2) == LUA_TFUNCTION || lua_type(L, 2) == LUA_TLIGHTFUNCTION) //check if 2nd item on stack is a function
{
lua_pushvalue(L, 2); //push function to top of stack
register_lua_cb(L, &wifi_station_status_cb_ref[id]);//pop function from top of the stack, register it in the LUA_REGISTRY, then assign returned lua_ref to wifi_station_status_cb_ref[id]
}
else
{
unregister_lua_cb(L, &wifi_station_status_cb_ref[id]); // unregister user's callback
}
return 0;
}
// wifi.sta.eventMonStart()
int wifi_station_event_mon_start(lua_State* L)
{
if(wifi_get_opmode() == SOFTAP_MODE) //Verify ESP is in either Station mode or StationAP mode
{
return luaL_error( L, "Can't monitor in SOFTAP mode" );
}
if (wifi_station_status_cb_ref[0] == LUA_NOREF && wifi_station_status_cb_ref[1] == LUA_NOREF &&
wifi_station_status_cb_ref[2] == LUA_NOREF && wifi_station_status_cb_ref[3] == LUA_NOREF &&
wifi_station_status_cb_ref[4] == LUA_NOREF && wifi_station_status_cb_ref[5] == LUA_NOREF )
{ //verify user has registered callbacks
return luaL_error( L, "No callbacks defined" );
}
uint32 ms = 150; //set default timer interval
if(lua_isnumber(L, 1)) // check if user has specified a different timer interval
{
ms=luaL_checknumber(L, 1); // retrieve user-defined interval
}
os_timer_disarm(&wifi_sta_status_timer);
os_timer_setfn(&wifi_sta_status_timer, (os_timer_func_t *)wifi_status_cb, NULL);
os_timer_arm(&wifi_sta_status_timer, ms, 1);
return 0;
}
#endif
#ifdef WIFI_SDK_EVENT_MONITOR_ENABLE
//variables for wifi event monitor
......@@ -126,7 +32,7 @@ static evt_queue_t *wifi_event_queue_tail; //pointer to end of queue
static int wifi_event_cb_ref[EVENT_MAX+1] = { [0 ... EVENT_MAX] = LUA_NOREF}; //holds references to registered Lua callbacks
// wifi.eventmon.register()
static int wifi_event_monitor_register(lua_State* L)
int wifi_event_monitor_register(lua_State* L)
{
uint8 id = (uint8)luaL_checknumber(L, 1);
if ( id > EVENT_MAX ) //Check if user is trying to register a callback for a valid event.
......
#############################################################
# Required variables for each makefile
# Discard this section from all parent makefiles
# Expected variables (with automatic defaults):
# CSRCS (all "C" files in the dir)
# SUBDIRS (all subdirs with a Makefile)
# GEN_LIBS - list of libs to be generated ()
# GEN_IMAGES - list of images to be generated ()
# COMPONENTS_xxx - a list of libs/objs in the form
# subdir/lib to be extracted and rolled up into
# a generated lib/image xxx.a ()
#
ifndef PDIR
GEN_LIBS = libsjson.a
endif
STD_CFLAGS=-std=gnu11 -Wimplicit
#############################################################
# Configuration i.e. compile options etc.
# Target specific stuff (defines etc.) goes in here!
# Generally values applying to a tree are captured in the
# makefile at its root level - these are then overridden
# for a subtree within the makefile rooted therein
#
DEFINES += -include memcompat.h -include json_config.h
#############################################################
# Recursion Magic - Don't touch this!!
#
# Each subtree potentially has an include directory
# corresponding to the common APIs applicable to modules
# rooted at that subtree. Accordingly, the INCLUDE PATH
# of a module can only contain the include directories up
# its parent path, and not its siblings
#
# Required for each makefile to inherit from the parent
#
INCLUDES := $(INCLUDES) -I $(PDIR)include
INCLUDES += -I ./
INCLUDES += -I ./include
INCLUDES += -I ../include
INCLUDES += -I ../../include
PDIR := ../$(PDIR)
sinclude $(PDIR)Makefile
#ifndef __JSON_CONFIG_H__
#define __JSON_CONFIG_H__
#define JSONSL_STATE_USER_FIELDS int lua_object_ref; int used_count;
#define JSONSL_NO_JPR
#endif
......@@ -1484,7 +1484,7 @@ static const char Special_Endings[0x100] = {
/* 0xfe */ 0 /* 0xfe */
};
static const uint32_t Special_Endings_bits[0x80 / 32] = {
0b00000000110010000000000000000000,
0b00000000011001000000000000000000,
0b10100000000010000000000000100000,
0b00000000000000000000000000011100,
0b00000000000000000000000000010100
......
#ifndef __MEMCOMPAT_H__
#define __MEMCOMPAT_H__
#include "c_types.h"
#include "mem.h"
static inline void *malloc(size_t sz) { return os_malloc(sz); }
static inline void free(void *p) { return os_free(p); }
static inline void *calloc(size_t n, size_t sz) { return os_zalloc(n*sz); }
#endif
......@@ -73,7 +73,7 @@ static bool myspiffs_set_location(spiffs_config *cfg, int align, int offset, int
* Returns TRUE if FS was found
* align must be a power of two
*/
static bool myspiffs_set_cfg(spiffs_config *cfg, int align, int offset, bool force_create) {
static bool myspiffs_set_cfg_block(spiffs_config *cfg, int align, int offset, int block_size, bool force_create) {
cfg->phys_erase_block = INTERNAL_FLASH_SECTOR_SIZE; // according to datasheet
cfg->log_page_size = LOG_PAGE_SIZE; // as we said
......@@ -81,10 +81,8 @@ static bool myspiffs_set_cfg(spiffs_config *cfg, int align, int offset, bool for
cfg->hal_write_f = my_spiffs_write;
cfg->hal_erase_f = my_spiffs_erase;
if (!myspiffs_set_location(cfg, align, offset, LOG_BLOCK_SIZE)) {
if (!myspiffs_set_location(cfg, align, offset, LOG_BLOCK_SIZE_SMALL_FS)) {
return FALSE;
}
if (!myspiffs_set_location(cfg, align, offset, block_size)) {
return FALSE;
}
NODE_DBG("fs.start:%x,max:%x\n",cfg->phys_addr,cfg->phys_size);
......@@ -109,6 +107,16 @@ static bool myspiffs_set_cfg(spiffs_config *cfg, int align, int offset, bool for
#endif
}
static bool myspiffs_set_cfg(spiffs_config *cfg, int align, int offset, bool force_create) {
if (force_create) {
return myspiffs_set_cfg_block(cfg, align, offset, LOG_BLOCK_SIZE , TRUE) ||
myspiffs_set_cfg_block(cfg, align, offset, LOG_BLOCK_SIZE_SMALL_FS, TRUE);
}
return myspiffs_set_cfg_block(cfg, align, offset, LOG_BLOCK_SIZE_SMALL_FS, FALSE) ||
myspiffs_set_cfg_block(cfg, align, offset, LOG_BLOCK_SIZE , FALSE);
}
static bool myspiffs_find_cfg(spiffs_config *cfg, bool force_create) {
int i;
......
......@@ -56,6 +56,10 @@ extern "C" {
#define SPIFFS_ERR_PROBE_NOT_A_FS -10035
#define SPIFFS_ERR_NAME_TOO_LONG -10036
#define SPIFFS_ERR_IX_MAP_UNMAPPED -10037
#define SPIFFS_ERR_IX_MAP_MAPPED -10038
#define SPIFFS_ERR_IX_MAP_BAD_RANGE -10039
#define SPIFFS_ERR_INTERNAL -10050
#define SPIFFS_ERR_TEST -10100
......@@ -133,7 +137,7 @@ typedef void (*spiffs_file_callback)(struct spiffs_t *fs, spiffs_fileop_type op,
#ifndef SPIFFS_DBG
#define SPIFFS_DBG(...) \
print(__VA_ARGS__)
printf(__VA_ARGS__)
#endif
#ifndef SPIFFS_GC_DBG
#define SPIFFS_GC_DBG(...) printf(__VA_ARGS__)
......@@ -293,6 +297,9 @@ typedef struct {
spiffs_obj_type type;
spiffs_page_ix pix;
u8_t name[SPIFFS_OBJ_NAME_LEN];
#if SPIFFS_OBJ_META_LEN
u8_t meta[SPIFFS_OBJ_META_LEN];
#endif
} spiffs_stat;
struct spiffs_dirent {
......@@ -301,6 +308,9 @@ struct spiffs_dirent {
spiffs_obj_type type;
u32_t size;
spiffs_page_ix pix;
#if SPIFFS_OBJ_META_LEN
u8_t meta[SPIFFS_OBJ_META_LEN];
#endif
};
typedef struct {
......@@ -309,6 +319,21 @@ typedef struct {
int entry;
} spiffs_DIR;
#if SPIFFS_IX_MAP
typedef struct {
// buffer with looked up data pixes
spiffs_page_ix *map_buf;
// precise file byte offset
u32_t offset;
// start data span index of lookup buffer
spiffs_span_ix start_spix;
// end data span index of lookup buffer
spiffs_span_ix end_spix;
} spiffs_ix_map;
#endif
// functions
#if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0
......@@ -506,6 +531,24 @@ s32_t SPIFFS_close(spiffs *fs, spiffs_file fh);
*/
s32_t SPIFFS_rename(spiffs *fs, const char *old, const char *newPath);
#if SPIFFS_OBJ_META_LEN
/**
* Updates file's metadata
* @param fs the file system struct
* @param path path to the file
* @param meta new metadata. must be SPIFFS_OBJ_META_LEN bytes long.
*/
s32_t SPIFFS_update_meta(spiffs *fs, const char *name, const void *meta);
/**
* Updates file's metadata
* @param fs the file system struct
* @param fh file handle of the file
* @param meta new metadata. must be SPIFFS_OBJ_META_LEN bytes long.
*/
s32_t SPIFFS_fupdate_meta(spiffs *fs, spiffs_file fh, const void *meta);
#endif
/**
* Returns last error of last file operation.
* @param fs the file system struct
......@@ -658,6 +701,85 @@ s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh);
*/
s32_t SPIFFS_set_file_callback_func(spiffs *fs, spiffs_file_callback cb_func);
#if SPIFFS_IX_MAP
/**
* Maps the first level index lookup to a given memory map.
* This will make reading big files faster, as the memory map will be used for
* looking up data pages instead of searching for the indices on the physical
* medium. When mapping, all affected indicies are found and the information is
* copied to the array.
* Whole file or only parts of it may be mapped. The index map will cover file
* contents from argument offset until and including arguments (offset+len).
* It is valid to map a longer range than the current file size. The map will
* then be populated when the file grows.
* On garbage collections and file data page movements, the map array will be
* automatically updated. Do not tamper with the map array, as this contains
* the references to the data pages. Modifying it from outside will corrupt any
* future readings using this file descriptor.
* The map will no longer be used when the file descriptor closed or the file
* is unmapped.
* This can be useful to get faster and more deterministic timing when reading
* large files, or when seeking and reading a lot within a file.
* @param fs the file system struct
* @param fh the file handle of the file to map
* @param map a spiffs_ix_map struct, describing the index map
* @param offset absolute file offset where to start the index map
* @param len length of the mapping in actual file bytes
* @param map_buf the array buffer for the look up data - number of required
* elements in the array can be derived from function
* SPIFFS_bytes_to_ix_map_entries given the length
*/
s32_t SPIFFS_ix_map(spiffs *fs, spiffs_file fh, spiffs_ix_map *map,
u32_t offset, u32_t len, spiffs_page_ix *map_buf);
/**
* Unmaps the index lookup from this filehandle. All future readings will
* proceed as normal, requiring reading of the first level indices from
* physical media.
* The map and map buffer given in function SPIFFS_ix_map will no longer be
* referenced by spiffs.
* It is not strictly necessary to unmap a file before closing it, as closing
* a file will automatically unmap it.
* @param fs the file system struct
* @param fh the file handle of the file to unmap
*/
s32_t SPIFFS_ix_unmap(spiffs *fs, spiffs_file fh);
/**
* Moves the offset for the index map given in function SPIFFS_ix_map. Parts or
* all of the map buffer will repopulated.
* @param fs the file system struct
* @param fh the mapped file handle of the file to remap
* @param offset new absolute file offset where to start the index map
*/
s32_t SPIFFS_ix_remap(spiffs *fs, spiffs_file fh, u32_t offs);
/**
* Utility function to get number of spiffs_page_ix entries a map buffer must
* contain on order to map given amount of file data in bytes.
* See function SPIFFS_ix_map and SPIFFS_ix_map_entries_to_bytes.
* @param fs the file system struct
* @param bytes number of file data bytes to map
* @return needed number of elements in a spiffs_page_ix array needed to
* map given amount of bytes in a file
*/
s32_t SPIFFS_bytes_to_ix_map_entries(spiffs *fs, u32_t bytes);
/**
* Utility function to amount of file data bytes that can be mapped when
* mapping a file with buffer having given number of spiffs_page_ix entries.
* See function SPIFFS_ix_map and SPIFFS_bytes_to_ix_map_entries.
* @param fs the file system struct
* @param map_page_ix_entries number of entries in a spiffs_page_ix array
* @return amount of file data in bytes that can be mapped given a map
* buffer having given amount of spiffs_page_ix entries
*/
s32_t SPIFFS_ix_map_entries_to_bytes(spiffs *fs, u32_t map_page_ix_entries);
#endif // SPIFFS_IX_MAP
#if SPIFFS_TEST_VISUALISATION
/**
* Prints out a visualization of the filesystem.
......
......@@ -20,12 +20,12 @@ static spiffs_cache_page *spiffs_cache_page_get(spiffs *fs, spiffs_page_ix pix)
if ((cache->cpage_use_map & (1<<i)) &&
(cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 &&
cp->pix == pix ) {
SPIFFS_CACHE_DBG("CACHE_GET: have cache page %i for %04x\n", i, pix);
SPIFFS_CACHE_DBG("CACHE_GET: have cache page "_SPIPRIi" for "_SPIPRIpg"\n", i, pix);
cp->last_access = cache->last_access;
return cp;
}
}
//SPIFFS_CACHE_DBG("CACHE_GET: no cache for %04x\n", pix);
//SPIFFS_CACHE_DBG("CACHE_GET: no cache for "_SPIPRIpg"\n", pix);
return 0;
}
......@@ -46,9 +46,9 @@ static s32_t spiffs_cache_page_free(spiffs *fs, int ix, u8_t write_back) {
cache->cpage_use_map &= ~(1 << ix);
if (cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) {
SPIFFS_CACHE_DBG("CACHE_FREE: free cache page %i objid %04x\n", ix, cp->obj_id);
SPIFFS_CACHE_DBG("CACHE_FREE: free cache page "_SPIPRIi" objid "_SPIPRIid"\n", ix, cp->obj_id);
} else {
SPIFFS_CACHE_DBG("CACHE_FREE: free cache page %i pix %04x\n", ix, cp->pix);
SPIFFS_CACHE_DBG("CACHE_FREE: free cache page "_SPIPRIi" pix "_SPIPRIpg"\n", ix, cp->pix);
}
}
......@@ -98,7 +98,7 @@ static spiffs_cache_page *spiffs_cache_page_allocate(spiffs *fs) {
spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i);
cache->cpage_use_map |= (1<<i);
cp->last_access = cache->last_access;
SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page %i\n", i);
SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page "_SPIPRIi"\n", i);
return cp;
}
}
......@@ -130,10 +130,13 @@ s32_t spiffs_phys_rd(
spiffs_cache_page *cp = spiffs_cache_page_get(fs, SPIFFS_PADDR_TO_PAGE(fs, addr));
cache->last_access++;
if (cp) {
// we've already got one, you see
#if SPIFFS_CACHE_STATS
fs->cache_hits++;
#endif
cp->last_access = cache->last_access;
u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix);
memcpy(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len);
} else {
if ((op & SPIFFS_OP_TYPE_MASK) == SPIFFS_OP_T_OBJ_LU2) {
// for second layer lookup functions, we do not cache in order to prevent shredding
......@@ -142,22 +145,34 @@ s32_t spiffs_phys_rd(
#if SPIFFS_CACHE_STATS
fs->cache_misses++;
#endif
// this operation will always free one cache page (unless all already free),
// the result code stems from the write operation of the possibly freed cache page
res = spiffs_cache_page_remove_oldest(fs, SPIFFS_CACHE_FLAG_TYPE_WR, 0);
cp = spiffs_cache_page_allocate(fs);
if (cp) {
cp->flags = SPIFFS_CACHE_FLAG_WRTHRU;
cp->pix = SPIFFS_PADDR_TO_PAGE(fs, addr);
}
s32_t res2 = SPIFFS_HAL_READ(fs,
addr - SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr),
SPIFFS_CFG_LOG_PAGE_SZ(fs),
spiffs_get_cache_page(fs, cache, cp->ix));
if (res2 != SPIFFS_OK) {
res = res2;
s32_t res2 = SPIFFS_HAL_READ(fs,
addr - SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr),
SPIFFS_CFG_LOG_PAGE_SZ(fs),
spiffs_get_cache_page(fs, cache, cp->ix));
if (res2 != SPIFFS_OK) {
// honor read failure before possible write failure (bad idea?)
res = res2;
}
u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix);
memcpy(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len);
} else {
// this will never happen, last resort for sake of symmetry
s32_t res2 = SPIFFS_HAL_READ(fs, addr, len, dst);
if (res2 != SPIFFS_OK) {
// honor read failure before possible write failure (bad idea?)
res = res2;
}
}
}
u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix);
memcpy(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len);
return res;
}
......
This diff is collapsed.
......@@ -35,6 +35,40 @@
#define SPIFFS_CHECK_DBG(...) //dbg_printf(__VA_ARGS__)
#endif
// Defines spiffs debug print formatters
// some general signed number
#ifndef _SPIPRIi
#define _SPIPRIi "%d"
#endif
// address
#ifndef _SPIPRIad
#define _SPIPRIad "%08x"
#endif
// block
#ifndef _SPIPRIbl
#define _SPIPRIbl "%04x"
#endif
// page
#ifndef _SPIPRIpg
#define _SPIPRIpg "%04x"
#endif
// span index
#ifndef _SPIPRIsp
#define _SPIPRIsp "%04x"
#endif
// file descriptor
#ifndef _SPIPRIfd
#define _SPIPRIfd "%d"
#endif
// file object id
#ifndef _SPIPRIid
#define _SPIPRIid "%04x"
#endif
// file flags
#ifndef _SPIPRIfl
#define _SPIPRIfl "%02x"
#endif
// Enable/disable API functions to determine exact number of bytes
// for filedescriptor and cache buffers. Once decided for a configuration,
// this can be disabled to reduce flash.
......@@ -57,6 +91,11 @@
#ifndef SPIFFS_CACHE_STATS
#define SPIFFS_CACHE_STATS 1
#endif
#else
// No SPIFFS_CACHE, also disable SPIFFS_CACHE_WR
#ifndef SPIFFS_CACHE_WR
#define SPIFFS_CACHE_WR 0
#endif
#endif
// Always check header of each accessed page to ensure consistent state.
......@@ -104,6 +143,20 @@
#define SPIFFS_OBJ_NAME_LEN (FS_OBJ_NAME_LEN+1)
#endif
// Maximum length of the metadata associated with an object.
// Setting to non-zero value enables metadata-related API but also
// changes the on-disk format, so the change is not backward-compatible.
//
// Do note: the meta length must never exceed
// logical_page_size - (SPIFFS_OBJ_NAME_LEN + 64)
//
// This is derived from following:
// logical_page_size - (SPIFFS_OBJ_NAME_LEN + sizeof(spiffs_page_header) +
// spiffs_object_ix_header fields + at least some LUT entries)
#ifndef SPIFFS_OBJ_META_LEN
#define SPIFFS_OBJ_META_LEN (0)
#endif
// Size of buffer allocated on stack used when copying data.
// Lower value generates more read/writes. No meaning having it bigger
// than logical page size.
......@@ -203,6 +256,53 @@
#define SPIFFS_READ_ONLY 0
#endif
// Enable this to add a temporal file cache using the fd buffer.
// The effects of the cache is that SPIFFS_open will find the file faster in
// certain cases. It will make it a lot easier for spiffs to find files
// opened frequently, reducing number of readings from the spi flash for
// finding those files.
// This will grow each fd by 6 bytes. If your files are opened in patterns
// with a degree of temporal locality, the system is optimized.
// Examples can be letting spiffs serve web content, where one file is the css.
// The css is accessed for each html file that is opened, meaning it is
// accessed almost every second time a file is opened. Another example could be
// a log file that is often opened, written, and closed.
// The size of the cache is number of given file descriptors, as it piggybacks
// on the fd update mechanism. The cache lives in the closed file descriptors.
// When closed, the fd know the whereabouts of the file. Instead of forgetting
// this, the temporal cache will keep handling updates to that file even if the
// fd is closed. If the file is opened again, the location of the file is found
// directly. If all available descriptors become opened, all cache memory is
// lost.
#ifndef SPIFFS_TEMPORAL_FD_CACHE
#define SPIFFS_TEMPORAL_FD_CACHE 1
#endif
// Temporal file cache hit score. Each time a file is opened, all cached files
// will lose one point. If the opened file is found in cache, that entry will
// gain SPIFFS_TEMPORAL_CACHE_HIT_SCORE points. One can experiment with this
// value for the specific access patterns of the application. However, it must
// be between 1 (no gain for hitting a cached entry often) and 255.
#ifndef SPIFFS_TEMPORAL_CACHE_HIT_SCORE
#define SPIFFS_TEMPORAL_CACHE_HIT_SCORE 4
#endif
// Enable to be able to map object indices to memory.
// This allows for faster and more deterministic reading if cases of reading
// large files and when changing file offset by seeking around a lot.
// When mapping a file's index, the file system will be scanned for index pages
// and the info will be put in memory provided by user. When reading, the
// memory map can be looked up instead of searching for index pages on the
// medium. This way, user can trade memory against performance.
// Whole, parts of, or future parts not being written yet can be mapped. The
// memory array will be owned by spiffs and updated accordingly during garbage
// collecting or when modifying the indices. The latter is invoked by when the
// file is modified in some way. The index buffer is tied to the file
// descriptor.
#ifndef SPIFFS_IX_MAP
#define SPIFFS_IX_MAP 0
#endif
// Set SPIFFS_TEST_VISUALISATION to non-zero to enable SPIFFS_vis function
// in the api. This function will visualize all filesystem using given printf
// function.
......
......@@ -11,7 +11,7 @@ static s32_t spiffs_gc_erase_block(
spiffs_block_ix bix) {
s32_t res;
SPIFFS_GC_DBG("gc: erase block %i\n", bix);
SPIFFS_GC_DBG("gc: erase block "_SPIPRIbl"\n", bix);
res = spiffs_erase_block(fs, bix);
SPIFFS_CHECK_RES(res);
......@@ -122,19 +122,19 @@ s32_t spiffs_gc_check(
u32_t needed_pages = (len + SPIFFS_DATA_PAGE_SIZE(fs) - 1) / SPIFFS_DATA_PAGE_SIZE(fs);
// if (fs->free_blocks <= 2 && (s32_t)needed_pages > free_pages) {
// SPIFFS_GC_DBG("gc: full freeblk:%i needed:%i free:%i dele:%i\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted);
// SPIFFS_GC_DBG("gc: full freeblk:"_SPIPRIi" needed:"_SPIPRIi" free:"_SPIPRIi" dele:"_SPIPRIi"\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted);
// return SPIFFS_ERR_FULL;
// }
if ((s32_t)needed_pages > (s32_t)(free_pages + fs->stats_p_deleted)) {
SPIFFS_GC_DBG("gc_check: full freeblk:%i needed:%i free:%i dele:%i\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted);
SPIFFS_GC_DBG("gc_check: full freeblk:"_SPIPRIi" needed:"_SPIPRIi" free:"_SPIPRIi" dele:"_SPIPRIi"\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted);
return SPIFFS_ERR_FULL;
}
do {
SPIFFS_GC_DBG("\ngc_check #%i: run gc free_blocks:%i pfree:%i pallo:%i pdele:%i [%i] len:%i of %i\n",
SPIFFS_GC_DBG("\ngc_check #"_SPIPRIi": run gc free_blocks:"_SPIPRIi" pfree:"_SPIPRIi" pallo:"_SPIPRIi" pdele:"_SPIPRIi" ["_SPIPRIi"] len:"_SPIPRIi" of "_SPIPRIi"\n",
tries,
fs->free_blocks, free_pages, fs->stats_p_allocated, fs->stats_p_deleted, (free_pages+fs->stats_p_allocated+fs->stats_p_deleted),
len, free_pages*SPIFFS_DATA_PAGE_SIZE(fs));
len, (u32_t)(free_pages*SPIFFS_DATA_PAGE_SIZE(fs)));
spiffs_block_ix *cands;
int count;
......@@ -152,13 +152,13 @@ s32_t spiffs_gc_check(
#endif
cand = cands[0];
fs->cleaning = 1;
//printf("gcing: cleaning block %i\n", cand);
//SPIFFS_GC_DBG("gcing: cleaning block "_SPIPRIi"\n", cand);
res = spiffs_gc_clean(fs, cand);
fs->cleaning = 0;
if (res < 0) {
SPIFFS_GC_DBG("gc_check: cleaning block %i, result %i\n", cand, res);
SPIFFS_GC_DBG("gc_check: cleaning block "_SPIPRIi", result "_SPIPRIi"\n", cand, res);
} else {
SPIFFS_GC_DBG("gc_check: cleaning block %i, result %i\n", cand, res);
SPIFFS_GC_DBG("gc_check: cleaning block "_SPIPRIi", result "_SPIPRIi"\n", cand, res);
}
SPIFFS_CHECK_RES(res);
......@@ -188,7 +188,7 @@ s32_t spiffs_gc_check(
res = SPIFFS_ERR_FULL;
}
SPIFFS_GC_DBG("gc_check: finished, %i dirty, blocks %i free, %i pages free, %i tries, res %i\n",
SPIFFS_GC_DBG("gc_check: finished, "_SPIPRIi" dirty, blocks "_SPIPRIi" free, "_SPIPRIi" pages free, "_SPIPRIi" tries, res "_SPIPRIi"\n",
fs->stats_p_allocated + fs->stats_p_deleted,
fs->free_blocks, free_pages, tries, res);
......@@ -226,7 +226,7 @@ s32_t spiffs_gc_erase_page_stats(
} // per entry
obj_lookup_page++;
} // per object lookup page
SPIFFS_GC_DBG("gc_check: wipe pallo:%i pdele:%i\n", allo, dele);
SPIFFS_GC_DBG("gc_check: wipe pallo:"_SPIPRIi" pdele:"_SPIPRIi"\n", allo, dele);
fs->stats_p_allocated -= allo;
fs->stats_p_deleted -= dele;
return res;
......@@ -255,7 +255,7 @@ s32_t spiffs_gc_find_candidate(
s32_t *cand_scores = (s32_t *)(fs->work + max_candidates * sizeof(spiffs_block_ix));
// align cand_scores on s32_t boundary
cand_scores = (s32_t*)(((ptrdiff_t)cand_scores + sizeof(ptrdiff_t) - 1) & ~(sizeof(ptrdiff_t) - 1));
cand_scores = (s32_t*)(((intptr_t)cand_scores + sizeof(intptr_t) - 1) & ~(sizeof(intptr_t) - 1));
*block_candidates = cand_blocks;
......@@ -314,7 +314,7 @@ s32_t spiffs_gc_find_candidate(
used_pages_in_block * SPIFFS_GC_HEUR_W_USED +
erase_age * (fs_crammed ? 0 : SPIFFS_GC_HEUR_W_ERASE_AGE);
int cand_ix = 0;
SPIFFS_GC_DBG("gc_check: bix:%i del:%i use:%i score:%i\n", cur_block, deleted_pages_in_block, used_pages_in_block, score);
SPIFFS_GC_DBG("gc_check: bix:"_SPIPRIbl" del:"_SPIPRIi" use:"_SPIPRIi" score:"_SPIPRIi"\n", cur_block, deleted_pages_in_block, used_pages_in_block, score);
while (cand_ix < max_candidates) {
if (cand_blocks[cand_ix] == (spiffs_block_ix)-1) {
cand_blocks[cand_ix] = cur_block;
......@@ -356,6 +356,7 @@ typedef struct {
spiffs_obj_id cur_obj_id;
spiffs_span_ix cur_objix_spix;
spiffs_page_ix cur_objix_pix;
spiffs_page_ix cur_data_pix;
int stored_scan_entry_index;
u8_t obj_id_found;
} spiffs_gc;
......@@ -375,15 +376,16 @@ typedef struct {
//
s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
s32_t res = SPIFFS_OK;
int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
const int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
// this is the global localizer being pushed and popped
int cur_entry = 0;
spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
spiffs_gc gc;
spiffs_gc gc; // our stack frame/state
spiffs_page_ix cur_pix = 0;
spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work;
spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work;
SPIFFS_GC_DBG("gc_clean: cleaning block %i\n", bix);
SPIFFS_GC_DBG("gc_clean: cleaning block "_SPIPRIbl"\n", bix);
memset(&gc, 0, sizeof(spiffs_gc));
gc.state = FIND_OBJ_DATA;
......@@ -392,12 +394,12 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
// move free cursor to next block, cannot use free pages from the block we want to clean
fs->free_cursor_block_ix = (bix+1)%fs->block_count;
fs->free_cursor_obj_lu_entry = 0;
SPIFFS_GC_DBG("gc_clean: move free cursor to block %i\n", fs->free_cursor_block_ix);
SPIFFS_GC_DBG("gc_clean: move free cursor to block "_SPIPRIbl"\n", fs->free_cursor_block_ix);
}
while (res == SPIFFS_OK && gc.state != FINISHED) {
SPIFFS_GC_DBG("gc_clean: state = %i entry:%i\n", gc.state, cur_entry);
gc.obj_id_found = 0;
SPIFFS_GC_DBG("gc_clean: state = "_SPIPRIi" entry:"_SPIPRIi"\n", gc.state, cur_entry);
gc.obj_id_found = 0; // reset (to no found data page)
// scan through lookup pages
int obj_lookup_page = cur_entry / entries_per_page;
......@@ -408,7 +410,7 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
// check each entry
// check each object lookup entry
while (scan && res == SPIFFS_OK &&
cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) {
spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
......@@ -417,21 +419,26 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
// act upon object id depending on gc state
switch (gc.state) {
case FIND_OBJ_DATA:
// find a data page
if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE &&
((obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0)) {
SPIFFS_GC_DBG("gc_clean: FIND_DATA state:%i - found obj id %04x\n", gc.state, obj_id);
// found a data page, stop scanning and handle in switch case below
SPIFFS_GC_DBG("gc_clean: FIND_DATA state:"_SPIPRIi" - found obj id "_SPIPRIid"\n", gc.state, obj_id);
gc.obj_id_found = 1;
gc.cur_obj_id = obj_id;
gc.cur_data_pix = cur_pix;
scan = 0;
}
break;
case MOVE_OBJ_DATA:
// evacuate found data pages for corresponding object index we have in memory,
// update memory representation
if (obj_id == gc.cur_obj_id) {
spiffs_page_header p_hdr;
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
SPIFFS_CHECK_RES(res);
SPIFFS_GC_DBG("gc_clean: MOVE_DATA found data page %04x:%04x @ %04x\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix);
SPIFFS_GC_DBG("gc_clean: MOVE_DATA found data page "_SPIPRIid":"_SPIPRIsp" @ "_SPIPRIpg"\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix);
if (SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix) != gc.cur_objix_spix) {
SPIFFS_GC_DBG("gc_clean: MOVE_DATA no objix spix match, take in another run\n");
} else {
......@@ -439,7 +446,7 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) {
// move page
res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_data_pix);
SPIFFS_GC_DBG("gc_clean: MOVE_DATA move objix %04x:%04x page %04x to %04x\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix, new_data_pix);
SPIFFS_GC_DBG("gc_clean: MOVE_DATA move objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg" to "_SPIPRIpg"\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix, new_data_pix);
SPIFFS_CHECK_RES(res);
// move wipes obj_lu, reload it
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
......@@ -447,8 +454,10 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
SPIFFS_CHECK_RES(res);
} else {
// page is deleted but not deleted in lookup, scrap it
SPIFFS_GC_DBG("gc_clean: MOVE_DATA wipe objix %04x:%04x page %04x\n", obj_id, p_hdr.span_ix, cur_pix);
// page is deleted but not deleted in lookup, scrap it -
// might seem unnecessary as we will erase this block, but
// we might get aborted
SPIFFS_GC_DBG("gc_clean: MOVE_DATA wipe objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix);
res = spiffs_page_delete(fs, cur_pix);
SPIFFS_CHECK_RES(res);
new_data_pix = SPIFFS_OBJ_ID_FREE;
......@@ -457,16 +466,17 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
if (gc.cur_objix_spix == 0) {
// update object index header page
((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[p_hdr.span_ix] = new_data_pix;
SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page %04x to objix_hdr entry %02x in mem\n", new_data_pix, SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix));
SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page "_SPIPRIpg" to objix_hdr entry "_SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix));
} else {
// update object index page
((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)] = new_data_pix;
SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page %04x to objix entry %02x in mem\n", new_data_pix, SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix));
SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page "_SPIPRIpg" to objix entry "_SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix));
}
}
}
break;
case MOVE_OBJ_IX:
// find and evacuate object index pages
if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE &&
(obj_id & SPIFFS_OBJ_ID_IX_FLAG)) {
// found an index object id
......@@ -479,20 +489,24 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) {
// move page
res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_pix);
SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX move objix %04x:%04x page %04x to %04x\n", obj_id, p_hdr.span_ix, cur_pix, new_pix);
SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX move objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg" to "_SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix, new_pix);
SPIFFS_CHECK_RES(res);
spiffs_cb_object_event(fs, 0, SPIFFS_EV_IX_UPD, obj_id, p_hdr.span_ix, new_pix, 0);
spiffs_cb_object_event(fs, (spiffs_page_object_ix *)&p_hdr,
SPIFFS_EV_IX_MOV, obj_id, p_hdr.span_ix, new_pix, 0);
// move wipes obj_lu, reload it
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
SPIFFS_CHECK_RES(res);
} else {
// page is deleted but not deleted in lookup, scrap it
SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX wipe objix %04x:%04x page %04x\n", obj_id, p_hdr.span_ix, cur_pix);
// page is deleted but not deleted in lookup, scrap it -
// might seem unnecessary as we will erase this block, but
// we might get aborted
SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX wipe objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix);
res = spiffs_page_delete(fs, cur_pix);
if (res == SPIFFS_OK) {
spiffs_cb_object_event(fs, 0, SPIFFS_EV_IX_DEL, obj_id, p_hdr.span_ix, cur_pix, 0);
spiffs_cb_object_event(fs, (spiffs_page_object_ix *)0,
SPIFFS_EV_IX_DEL, obj_id, p_hdr.span_ix, cur_pix, 0);
}
}
SPIFFS_CHECK_RES(res);
......@@ -501,69 +515,88 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
default:
scan = 0;
break;
}
} // switch gc state
cur_entry++;
} // per entry
obj_lookup_page++;
obj_lookup_page++; // no need to check scan variable here, obj_lookup_page is set in start of loop
} // per object lookup page
if (res != SPIFFS_OK) break;
// state finalization and switch
switch (gc.state) {
case FIND_OBJ_DATA:
if (gc.obj_id_found) {
// handle found data page -
// find out corresponding obj ix page and load it to memory
spiffs_page_header p_hdr;
spiffs_page_ix objix_pix;
gc.stored_scan_entry_index = cur_entry;
cur_entry = 0;
gc.stored_scan_entry_index = cur_entry; // push cursor
cur_entry = 0; // restart scan from start
gc.state = MOVE_OBJ_DATA;
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
SPIFFS_CHECK_RES(res);
gc.cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix);
SPIFFS_GC_DBG("gc_clean: FIND_DATA find objix span_ix:%04x\n", gc.cur_objix_spix);
SPIFFS_GC_DBG("gc_clean: FIND_DATA find objix span_ix:"_SPIPRIsp"\n", gc.cur_objix_spix);
res = spiffs_obj_lu_find_id_and_span(fs, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix, 0, &objix_pix);
if (res == SPIFFS_ERR_NOT_FOUND) {
// on borked systems we might get an ERR_NOT_FOUND here -
// this is handled by simply deleting the page as it is not referenced
// from anywhere
SPIFFS_GC_DBG("gc_clean: FIND_OBJ_DATA objix not found! Wipe page "_SPIPRIpg"\n", gc.cur_data_pix);
res = spiffs_page_delete(fs, gc.cur_data_pix);
SPIFFS_CHECK_RES(res);
// then we restore states and continue scanning for data pages
cur_entry = gc.stored_scan_entry_index; // pop cursor
gc.state = FIND_OBJ_DATA;
break; // done
}
SPIFFS_CHECK_RES(res);
SPIFFS_GC_DBG("gc_clean: FIND_DATA found object index at page %04x\n", objix_pix);
SPIFFS_GC_DBG("gc_clean: FIND_DATA found object index at page "_SPIPRIpg"\n", objix_pix);
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
0, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work);
SPIFFS_CHECK_RES(res);
// cannot allow a gc if the presumed index in fact is no index, a
// check must run or lot of data may be lost
SPIFFS_VALIDATE_OBJIX(objix->p_hdr, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix);
gc.cur_objix_pix = objix_pix;
} else {
// no more data pages found, passed thru all block, start evacuating object indices
gc.state = MOVE_OBJ_IX;
cur_entry = 0; // restart entry scan index
}
break;
case MOVE_OBJ_DATA: {
// store modified objix (hdr) page
// store modified objix (hdr) page residing in memory now that all
// data pages belonging to this object index and residing in the block
// we want to evacuate
spiffs_page_ix new_objix_pix;
gc.state = FIND_OBJ_DATA;
cur_entry = gc.stored_scan_entry_index;
cur_entry = gc.stored_scan_entry_index; // pop cursor
if (gc.cur_objix_spix == 0) {
// store object index header page
res = spiffs_object_update_index_hdr(fs, 0, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_pix, fs->work, 0, 0, &new_objix_pix);
SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix_hdr page, %04x:%04x\n", new_objix_pix, 0);
res = spiffs_object_update_index_hdr(fs, 0, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_pix, fs->work, 0, 0, 0, &new_objix_pix);
SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix_hdr page, "_SPIPRIpg":"_SPIPRIsp"\n", new_objix_pix, 0);
SPIFFS_CHECK_RES(res);
} else {
// store object index page
res = spiffs_page_move(fs, 0, fs->work, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, gc.cur_objix_pix, &new_objix_pix);
SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix page, %04x:%04x\n", new_objix_pix, objix->p_hdr.span_ix);
SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix page, "_SPIPRIpg":"_SPIPRIsp"\n", new_objix_pix, objix->p_hdr.span_ix);
SPIFFS_CHECK_RES(res);
spiffs_cb_object_event(fs, 0, SPIFFS_EV_IX_UPD, gc.cur_obj_id, objix->p_hdr.span_ix, new_objix_pix, 0);
spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work,
SPIFFS_EV_IX_UPD, gc.cur_obj_id, objix->p_hdr.span_ix, new_objix_pix, 0);
}
}
break;
case MOVE_OBJ_IX:
// scanned thru all block, no more object indices found - our work here is done
gc.state = FINISHED;
break;
default:
cur_entry = 0;
break;
}
SPIFFS_GC_DBG("gc_clean: state-> %i\n", gc.state);
} // switch gc.state
SPIFFS_GC_DBG("gc_clean: state-> "_SPIPRIi"\n", gc.state);
} // while state != FINISHED
......
......@@ -129,14 +129,14 @@ s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work,
res = spiffs_obj_lu_scan(fs);
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
SPIFFS_DBG("page index byte len: %i\n", SPIFFS_CFG_LOG_PAGE_SZ(fs));
SPIFFS_DBG("object lookup pages: %i\n", SPIFFS_OBJ_LOOKUP_PAGES(fs));
SPIFFS_DBG("page pages per block: %i\n", SPIFFS_PAGES_PER_BLOCK(fs));
SPIFFS_DBG("page header length: %i\n", sizeof(spiffs_page_header));
SPIFFS_DBG("object header index entries: %i\n", SPIFFS_OBJ_HDR_IX_LEN(fs));
SPIFFS_DBG("object index entries: %i\n", SPIFFS_OBJ_IX_LEN(fs));
SPIFFS_DBG("available file descriptors: %i\n", fs->fd_count);
SPIFFS_DBG("free blocks: %i\n", fs->free_blocks);
SPIFFS_DBG("page index byte len: "_SPIPRIi"\n", (u32_t)SPIFFS_CFG_LOG_PAGE_SZ(fs));
SPIFFS_DBG("object lookup pages: "_SPIPRIi"\n", (u32_t)SPIFFS_OBJ_LOOKUP_PAGES(fs));
SPIFFS_DBG("page pages per block: "_SPIPRIi"\n", (u32_t)SPIFFS_PAGES_PER_BLOCK(fs));
SPIFFS_DBG("page header length: "_SPIPRIi"\n", (u32_t)sizeof(spiffs_page_header));
SPIFFS_DBG("object header index entries: "_SPIPRIi"\n", (u32_t)SPIFFS_OBJ_HDR_IX_LEN(fs));
SPIFFS_DBG("object index entries: "_SPIPRIi"\n", (u32_t)SPIFFS_OBJ_IX_LEN(fs));
SPIFFS_DBG("available file descriptors: "_SPIPRIi"\n", (u32_t)fs->fd_count);
SPIFFS_DBG("free blocks: "_SPIPRIi"\n", (u32_t)fs->free_blocks);
fs->check_cb_f = check_cb_f;
......@@ -191,7 +191,7 @@ s32_t SPIFFS_creat(spiffs *fs, const char *path, spiffs_mode mode) {
res = spiffs_obj_lu_find_free_obj_id(fs, &obj_id, (const u8_t*)path);
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
res = spiffs_object_create(fs, obj_id, (const u8_t*)path, SPIFFS_TYPE_FILE, 0);
res = spiffs_object_create(fs, obj_id, (const u8_t*)path, 0, SPIFFS_TYPE_FILE, 0);
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
SPIFFS_UNLOCK(fs);
return 0;
......@@ -212,10 +212,10 @@ spiffs_file SPIFFS_open(spiffs *fs, const char *path, spiffs_flags flags, spiffs
#if SPIFFS_READ_ONLY
// not valid flags in read only mode
flags &= ~SPIFFS_WRONLY | SPIFFS_CREAT | SPIFFS_TRUNC;
flags &= ~(SPIFFS_WRONLY | SPIFFS_CREAT | SPIFFS_TRUNC);
#endif // SPIFFS_READ_ONLY
s32_t res = spiffs_fd_find_new(fs, &fd);
s32_t res = spiffs_fd_find_new(fs, &fd, path);
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)path, &pix);
......@@ -243,7 +243,7 @@ spiffs_file SPIFFS_open(spiffs *fs, const char *path, spiffs_flags flags, spiffs
spiffs_fd_return(fs, fd->file_nbr);
}
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
res = spiffs_object_create(fs, obj_id, (const u8_t*)path, SPIFFS_TYPE_FILE, &pix);
res = spiffs_object_create(fs, obj_id, (const u8_t*)path, 0, SPIFFS_TYPE_FILE, &pix);
if (res < SPIFFS_OK) {
spiffs_fd_return(fs, fd->file_nbr);
}
......@@ -285,7 +285,7 @@ spiffs_file SPIFFS_open_by_dirent(spiffs *fs, struct spiffs_dirent *e, spiffs_fl
spiffs_fd *fd;
s32_t res = spiffs_fd_find_new(fs, &fd);
s32_t res = spiffs_fd_find_new(fs, &fd, 0);
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
res = spiffs_object_open_by_page(fs, e->pix, fd, flags, mode);
......@@ -317,7 +317,7 @@ spiffs_file SPIFFS_open_by_page(spiffs *fs, spiffs_page_ix page_ix, spiffs_flags
spiffs_fd *fd;
s32_t res = spiffs_fd_find_new(fs, &fd);
s32_t res = spiffs_fd_find_new(fs, &fd, 0);
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
if (SPIFFS_IS_LOOKUP_PAGE(fs, page_ix)) {
......@@ -356,7 +356,7 @@ spiffs_file SPIFFS_open_by_page(spiffs *fs, spiffs_page_ix page_ix, spiffs_flags
return SPIFFS_FH_OFFS(fs, fd->file_nbr);
}
s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len) {
static s32_t spiffs_hydro_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len) {
SPIFFS_API_CHECK_CFG(fs);
SPIFFS_API_CHECK_MOUNT(fs);
SPIFFS_LOCK(fs);
......@@ -410,6 +410,15 @@ s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len) {
return len;
}
s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len) {
s32_t res = spiffs_hydro_read(fs, fh, buf, len);
if (res == SPIFFS_ERR_END_OF_OBJECT) {
res = 0;
}
return res;
}
#if !SPIFFS_READ_ONLY
static s32_t spiffs_hydro_write(spiffs *fs, spiffs_fd *fd, void *buf, u32_t offset, s32_t len) {
(void)fs;
......@@ -493,7 +502,7 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) {
offset + len > fd->cache_page->offset + SPIFFS_CFG_LOG_PAGE_SZ(fs)) // writing beyond cache page
{
// boundary violation, write back cache first and allocate new
SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page %i for fd %i:%04x, boundary viol, offs:%i size:%i\n",
SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page "_SPIPRIpg" for fd "_SPIPRIfd":"_SPIPRIid", boundary viol, offs:"_SPIPRIi" size:"_SPIPRIi"\n",
fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size);
res = spiffs_hydro_write(fs, fd,
spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix),
......@@ -511,14 +520,14 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) {
if (fd->cache_page) {
fd->cache_page->offset = offset;
fd->cache_page->size = 0;
SPIFFS_CACHE_DBG("CACHE_WR_ALLO: allocating cache page %i for fd %i:%04x\n",
SPIFFS_CACHE_DBG("CACHE_WR_ALLO: allocating cache page "_SPIPRIpg" for fd "_SPIPRIfd":"_SPIPRIid"\n",
fd->cache_page->ix, fd->file_nbr, fd->obj_id);
}
}
if (fd->cache_page) {
u32_t offset_in_cpage = offset - fd->cache_page->offset;
SPIFFS_CACHE_DBG("CACHE_WR_WRITE: storing to cache page %i for fd %i:%04x, offs %i:%i len %i\n",
SPIFFS_CACHE_DBG("CACHE_WR_WRITE: storing to cache page "_SPIPRIpg" for fd "_SPIPRIfd":"_SPIPRIid", offs "_SPIPRIi":"_SPIPRIi" len "_SPIPRIi"\n",
fd->cache_page->ix, fd->file_nbr, fd->obj_id,
offset, offset_in_cpage, len);
spiffs_cache *cache = spiffs_get_cache(fs);
......@@ -539,15 +548,14 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) {
// big write, no need to cache it - but first check if there is a cached write already
if (fd->cache_page) {
// write back cache first
SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page %i for fd %i:%04x, big write, offs:%i size:%i\n",
SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page "_SPIPRIpg" for fd "_SPIPRIfd":"_SPIPRIid", big write, offs:"_SPIPRIi" size:"_SPIPRIi"\n",
fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size);
res = spiffs_hydro_write(fs, fd,
spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix),
fd->cache_page->offset, fd->cache_page->size);
spiffs_cache_fd_release(fs, fd->cache_page);
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
res = spiffs_hydro_write(fs, fd, buf, offset, len);
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
// data written below
}
}
}
......@@ -578,16 +586,19 @@ s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence) {
spiffs_fflush_cache(fs, fh);
#endif
s32_t fileSize = fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size;
switch (whence) {
case SPIFFS_SEEK_CUR:
offs = fd->fdoffset+offs;
break;
case SPIFFS_SEEK_END:
offs = (fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size) + offs;
offs = fileSize + offs;
break;
}
if ((offs > (s32_t)fd->size) && (SPIFFS_UNDEFINED_LEN != fd->size)) {
if ((offs > fileSize)) {
fd->fdoffset = fileSize;
res = SPIFFS_ERR_END_OF_OBJECT;
}
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
......@@ -625,7 +636,7 @@ s32_t SPIFFS_remove(spiffs *fs, const char *path) {
spiffs_page_ix pix;
s32_t res;
res = spiffs_fd_find_new(fs, &fd);
res = spiffs_fd_find_new(fs, &fd, 0);
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)path, &pix);
......@@ -704,6 +715,9 @@ static s32_t spiffs_stat_pix(spiffs *fs, spiffs_page_ix pix, spiffs_file fh, spi
s->size = objix_hdr.size == SPIFFS_UNDEFINED_LEN ? 0 : objix_hdr.size;
s->pix = pix;
strncpy((char *)s->name, (char *)objix_hdr.name, SPIFFS_OBJ_NAME_LEN);
#if SPIFFS_OBJ_META_LEN
memcpy(s->meta, objix_hdr.meta, SPIFFS_OBJ_META_LEN);
#endif
return res;
}
......@@ -771,7 +785,7 @@ static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh) {
fd->cache_page = spiffs_cache_page_get_by_fd(fs, fd);
}
if (fd->cache_page) {
SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page %i for fd %i:%04x, flush, offs:%i size:%i\n",
SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page "_SPIPRIpg" for fd "_SPIPRIfd":"_SPIPRIid", flush, offs:"_SPIPRIi" size:"_SPIPRIi"\n",
fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size);
res = spiffs_hydro_write(fs, fd,
spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix),
......@@ -851,7 +865,7 @@ s32_t SPIFFS_rename(spiffs *fs, const char *old_path, const char *new_path) {
}
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
res = spiffs_fd_find_new(fs, &fd);
res = spiffs_fd_find_new(fs, &fd, 0);
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
res = spiffs_object_open_by_page(fs, pix_old, fd, 0, 0);
......@@ -861,6 +875,49 @@ s32_t SPIFFS_rename(spiffs *fs, const char *old_path, const char *new_path) {
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, (const u8_t*)new_path,
0, 0, &pix_dummy);
#if SPIFFS_TEMPORAL_FD_CACHE
if (res == SPIFFS_OK) {
spiffs_fd_temporal_cache_rehash(fs, old_path, new_path);
}
#endif
spiffs_fd_return(fs, fd->file_nbr);
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
SPIFFS_UNLOCK(fs);
return res;
#endif // SPIFFS_READ_ONLY
}
#if SPIFFS_OBJ_META_LEN
s32_t SPIFFS_update_meta(spiffs *fs, const char *name, const void *meta) {
#if SPIFFS_READ_ONLY
(void)fs; (void)name; (void)meta;
return SPIFFS_ERR_RO_NOT_IMPL;
#else
SPIFFS_API_CHECK_CFG(fs);
SPIFFS_API_CHECK_MOUNT(fs);
SPIFFS_LOCK(fs);
spiffs_page_ix pix, pix_dummy;
spiffs_fd *fd;
s32_t res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)name, &pix);
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
res = spiffs_fd_find_new(fs, &fd, 0);
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
res = spiffs_object_open_by_page(fs, pix, fd, 0, 0);
if (res != SPIFFS_OK) {
spiffs_fd_return(fs, fd->file_nbr);
}
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, 0, meta,
0, &pix_dummy);
spiffs_fd_return(fs, fd->file_nbr);
......@@ -873,6 +930,40 @@ s32_t SPIFFS_rename(spiffs *fs, const char *old_path, const char *new_path) {
#endif // SPIFFS_READ_ONLY
}
s32_t SPIFFS_fupdate_meta(spiffs *fs, spiffs_file fh, const void *meta) {
#if SPIFFS_READ_ONLY
(void)fs; (void)fh; (void)meta;
return SPIFFS_ERR_RO_NOT_IMPL;
#else
SPIFFS_API_CHECK_CFG(fs);
SPIFFS_API_CHECK_MOUNT(fs);
SPIFFS_LOCK(fs);
s32_t res;
spiffs_fd *fd;
spiffs_page_ix pix_dummy;
fh = SPIFFS_FH_UNOFFS(fs, fh);
res = spiffs_fd_get(fs, fh, &fd);
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
if ((fd->flags & SPIFFS_O_WRONLY) == 0) {
res = SPIFFS_ERR_NOT_WRITABLE;
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
}
res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, 0, meta,
0, &pix_dummy);
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
SPIFFS_UNLOCK(fs);
return res;
#endif // SPIFFS_READ_ONLY
}
#endif // SPIFFS_OBJ_META_LEN
spiffs_DIR *SPIFFS_opendir(spiffs *fs, const char *name, spiffs_DIR *d) {
(void)name;
......@@ -913,7 +1004,7 @@ static s32_t spiffs_read_dir_v(
if (res != SPIFFS_OK) return res;
if ((obj_id & SPIFFS_OBJ_ID_IX_FLAG) &&
objix_hdr.p_hdr.span_ix == 0 &&
(objix_hdr.p_hdr.flags& (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) ==
(objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) ==
(SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) {
struct spiffs_dirent *e = (struct spiffs_dirent*)user_var_p;
e->obj_id = obj_id;
......@@ -921,9 +1012,11 @@ static s32_t spiffs_read_dir_v(
e->type = objix_hdr.type;
e->size = objix_hdr.size == SPIFFS_UNDEFINED_LEN ? 0 : objix_hdr.size;
e->pix = pix;
#if SPIFFS_OBJ_META_LEN
memcpy(e->meta, objix_hdr.meta, SPIFFS_OBJ_META_LEN);
#endif
return SPIFFS_OK;
}
return SPIFFS_VIS_COUNTINUE;
}
......@@ -952,6 +1045,7 @@ struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e) {
if (res == SPIFFS_OK) {
d->block = bix;
d->entry = entry + 1;
e->obj_id &= ~SPIFFS_OBJ_ID_IX_FLAG;
ret = e;
} else {
d->fs->err_code = res;
......@@ -1103,6 +1197,138 @@ s32_t SPIFFS_set_file_callback_func(spiffs *fs, spiffs_file_callback cb_func) {
return 0;
}
#if SPIFFS_IX_MAP
s32_t SPIFFS_ix_map(spiffs *fs, spiffs_file fh, spiffs_ix_map *map,
u32_t offset, u32_t len, spiffs_page_ix *map_buf) {
s32_t res;
SPIFFS_API_CHECK_CFG(fs);
SPIFFS_API_CHECK_MOUNT(fs);
SPIFFS_LOCK(fs);
fh = SPIFFS_FH_UNOFFS(fs, fh);
spiffs_fd *fd;
res = spiffs_fd_get(fs, fh, &fd);
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
if (fd->ix_map) {
SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_IX_MAP_MAPPED);
}
map->map_buf = map_buf;
map->offset = offset;
// nb: spix range includes last
map->start_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs);
map->end_spix = (offset + len) / SPIFFS_DATA_PAGE_SIZE(fs);
memset(map_buf, 0, sizeof(spiffs_page_ix) * (map->end_spix - map->start_spix + 1));
fd->ix_map = map;
// scan for pixes
res = spiffs_populate_ix_map(fs, fd, 0, map->end_spix - map->start_spix + 1);
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
SPIFFS_UNLOCK(fs);
return res;
}
s32_t SPIFFS_ix_unmap(spiffs *fs, spiffs_file fh) {
s32_t res;
SPIFFS_API_CHECK_CFG(fs);
SPIFFS_API_CHECK_MOUNT(fs);
SPIFFS_LOCK(fs);
fh = SPIFFS_FH_UNOFFS(fs, fh);
spiffs_fd *fd;
res = spiffs_fd_get(fs, fh, &fd);
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
if (fd->ix_map == 0) {
SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_IX_MAP_UNMAPPED);
}
fd->ix_map = 0;
SPIFFS_UNLOCK(fs);
return res;
}
s32_t SPIFFS_ix_remap(spiffs *fs, spiffs_file fh, u32_t offset) {
s32_t res = SPIFFS_OK;
SPIFFS_API_CHECK_CFG(fs);
SPIFFS_API_CHECK_MOUNT(fs);
SPIFFS_LOCK(fs);
fh = SPIFFS_FH_UNOFFS(fs, fh);
spiffs_fd *fd;
res = spiffs_fd_get(fs, fh, &fd);
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
if (fd->ix_map == 0) {
SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_IX_MAP_UNMAPPED);
}
spiffs_ix_map *map = fd->ix_map;
s32_t spix_diff = offset / SPIFFS_DATA_PAGE_SIZE(fs) - map->start_spix;
map->offset = offset;
// move existing pixes if within map offs
if (spix_diff != 0) {
// move vector
int i;
const s32_t vec_len = map->end_spix - map->start_spix + 1; // spix range includes last
map->start_spix += spix_diff;
map->end_spix += spix_diff;
if (spix_diff >= vec_len) {
// moving beyond range
memset(&map->map_buf, 0, vec_len * sizeof(spiffs_page_ix));
// populate_ix_map is inclusive
res = spiffs_populate_ix_map(fs, fd, 0, vec_len-1);
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
} else if (spix_diff > 0) {
// diff positive
for (i = 0; i < vec_len - spix_diff; i++) {
map->map_buf[i] = map->map_buf[i + spix_diff];
}
// memset is non-inclusive
memset(&map->map_buf[vec_len - spix_diff], 0, spix_diff * sizeof(spiffs_page_ix));
// populate_ix_map is inclusive
res = spiffs_populate_ix_map(fs, fd, vec_len - spix_diff, vec_len-1);
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
} else {
// diff negative
for (i = vec_len - 1; i >= -spix_diff; i--) {
map->map_buf[i] = map->map_buf[i + spix_diff];
}
// memset is non-inclusive
memset(&map->map_buf[0], 0, -spix_diff * sizeof(spiffs_page_ix));
// populate_ix_map is inclusive
res = spiffs_populate_ix_map(fs, fd, 0, -spix_diff - 1);
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
}
}
SPIFFS_UNLOCK(fs);
return res;
}
s32_t SPIFFS_bytes_to_ix_map_entries(spiffs *fs, u32_t bytes) {
SPIFFS_API_CHECK_CFG(fs);
// always add one extra page, the offset might change to the middle of a page
return (bytes + SPIFFS_DATA_PAGE_SIZE(fs) ) / SPIFFS_DATA_PAGE_SIZE(fs);
}
s32_t SPIFFS_ix_map_entries_to_bytes(spiffs *fs, u32_t map_page_ix_entries) {
SPIFFS_API_CHECK_CFG(fs);
return map_page_ix_entries * SPIFFS_DATA_PAGE_SIZE(fs);
}
#endif // SPIFFS_IX_MAP
#if SPIFFS_TEST_VISUALISATION
s32_t SPIFFS_vis(spiffs *fs) {
s32_t res = SPIFFS_OK;
......@@ -1128,7 +1354,7 @@ s32_t SPIFFS_vis(spiffs *fs) {
cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) {
spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
if (cur_entry == 0) {
spiffs_printf("%4i ", bix);
spiffs_printf(_SPIPRIbl" ", bix);
} else if ((cur_entry & 0x3f) == 0) {
spiffs_printf(" ");
}
......@@ -1156,7 +1382,7 @@ s32_t SPIFFS_vis(spiffs *fs) {
SPIFFS_CHECK_RES(res);
if (erase_count != (spiffs_obj_id)-1) {
spiffs_printf("\tera_cnt: %i\n", erase_count);
spiffs_printf("\tera_cnt: "_SPIPRIi"\n", erase_count);
} else {
spiffs_printf("\tera_cnt: N/A\n");
}
......@@ -1164,16 +1390,16 @@ s32_t SPIFFS_vis(spiffs *fs) {
bix++;
} // per block
spiffs_printf("era_cnt_max: %i\n", fs->max_erase_count);
spiffs_printf("last_errno: %i\n", fs->err_code);
spiffs_printf("blocks: %i\n", fs->block_count);
spiffs_printf("free_blocks: %i\n", fs->free_blocks);
spiffs_printf("page_alloc: %i\n", fs->stats_p_allocated);
spiffs_printf("page_delet: %i\n", fs->stats_p_deleted);
spiffs_printf("era_cnt_max: "_SPIPRIi"\n", fs->max_erase_count);
spiffs_printf("last_errno: "_SPIPRIi"\n", fs->err_code);
spiffs_printf("blocks: "_SPIPRIi"\n", fs->block_count);
spiffs_printf("free_blocks: "_SPIPRIi"\n", fs->free_blocks);
spiffs_printf("page_alloc: "_SPIPRIi"\n", fs->stats_p_allocated);
spiffs_printf("page_delet: "_SPIPRIi"\n", fs->stats_p_deleted);
SPIFFS_UNLOCK(fs);
u32_t total, used;
SPIFFS_info(fs, &total, &used);
spiffs_printf("used: %i of %i\n", used, total);
spiffs_printf("used: "_SPIPRIi" of "_SPIPRIi"\n", used, total);
return res;
}
#endif
This diff is collapsed.
......@@ -116,13 +116,23 @@
#define SPIFFS_ERR_CHECK_FLAGS_BAD (SPIFFS_ERR_INTERNAL - 3)
#define _SPIFFS_ERR_CHECK_LAST (SPIFFS_ERR_INTERNAL - 4)
// visitor result, continue searching
#define SPIFFS_VIS_COUNTINUE (SPIFFS_ERR_INTERNAL - 20)
// visitor result, continue searching after reloading lu buffer
#define SPIFFS_VIS_COUNTINUE_RELOAD (SPIFFS_ERR_INTERNAL - 21)
// visitor result, stop searching
#define SPIFFS_VIS_END (SPIFFS_ERR_INTERNAL - 22)
#define SPIFFS_EV_IX_UPD 0
#define SPIFFS_EV_IX_NEW 1
#define SPIFFS_EV_IX_DEL 2
// updating an object index contents
#define SPIFFS_EV_IX_UPD (0)
// creating a new object index
#define SPIFFS_EV_IX_NEW (1)
// deleting an object index
#define SPIFFS_EV_IX_DEL (2)
// moving an object index without updating contents
#define SPIFFS_EV_IX_MOV (3)
// updating an object index header data only, not the table itself
#define SPIFFS_EV_IX_UPD_HDR (4)
#define SPIFFS_OBJ_ID_IX_FLAG ((spiffs_obj_id)(1<<(8*sizeof(spiffs_obj_id)-1)))
......@@ -137,7 +147,7 @@
((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs)))
#else // SPIFFS_USE_MAGIC_LENGTH
#define SPIFFS_MAGIC(fs, bix) \
((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs) ^ ((fs)->block_count - ((bix) < 3 ? (1<<(bix)) - 1 : (bix)<<2))))
((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs) ^ ((fs)->block_count - (bix))))
#endif // SPIFFS_USE_MAGIC_LENGTH
#endif // SPIFFS_USE_MAGIC
......@@ -228,7 +238,9 @@
// object index span index number for given data span index or entry
#define SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, spix) \
((spix) < SPIFFS_OBJ_HDR_IX_LEN(fs) ? 0 : (1+((spix)-SPIFFS_OBJ_HDR_IX_LEN(fs))/SPIFFS_OBJ_IX_LEN(fs)))
// get data span index for object index span index
#define SPIFFS_DATA_SPAN_IX_FOR_OBJ_IX_SPAN_IX(fs, spix) \
( (spix) == 0 ? 0 : (SPIFFS_OBJ_HDR_IX_LEN(fs) + (((spix)-1) * SPIFFS_OBJ_IX_LEN(fs))) )
#define SPIFFS_OP_T_OBJ_LU (0<<0)
#define SPIFFS_OP_T_OBJ_LU2 (1<<0)
......@@ -312,7 +324,7 @@
if ((ph).span_ix != (spix)) return SPIFFS_ERR_DATA_SPAN_MISMATCH;
// check id
// check id, only visit matching objec ids
#define SPIFFS_VIS_CHECK_ID (1<<0)
// report argument object id to visitor - else object lookup id is reported
#define SPIFFS_VIS_CHECK_PH (1<<1)
......@@ -425,6 +437,16 @@ typedef struct {
#if SPIFFS_CACHE_WR
spiffs_cache_page *cache_page;
#endif
#if SPIFFS_TEMPORAL_FD_CACHE
// djb2 hash of filename
u32_t name_hash;
// hit score (score == 0 indicates never used fd)
u16_t score;
#endif
#if SPIFFS_IX_MAP
// spiffs index map, if 0 it means unmapped
spiffs_ix_map *ix_map;
#endif
} spiffs_fd;
......@@ -458,6 +480,10 @@ typedef struct __attribute(( packed ))
spiffs_obj_type type;
// name of object
u8_t name[SPIFFS_OBJ_NAME_LEN];
#if SPIFFS_OBJ_META_LEN
// metadata. not interpreted by SPIFFS in any way.
u8_t meta[SPIFFS_OBJ_META_LEN];
#endif
} spiffs_page_object_ix_header;
// object index page header
......@@ -612,7 +638,8 @@ s32_t spiffs_page_delete(
s32_t spiffs_object_create(
spiffs *fs,
spiffs_obj_id obj_id,
const u8_t name[SPIFFS_OBJ_NAME_LEN],
const u8_t name[],
const u8_t meta[],
spiffs_obj_type type,
spiffs_page_ix *objix_hdr_pix);
......@@ -622,13 +649,24 @@ s32_t spiffs_object_update_index_hdr(
spiffs_obj_id obj_id,
spiffs_page_ix objix_hdr_pix,
u8_t *new_objix_hdr_data,
const u8_t name[SPIFFS_OBJ_NAME_LEN],
const u8_t name[],
const u8_t meta[],
u32_t size,
spiffs_page_ix *new_pix);
void spiffs_cb_object_event(
#if SPIFFS_IX_MAP
s32_t spiffs_populate_ix_map(
spiffs *fs,
spiffs_fd *fd,
u32_t vec_entry_start,
u32_t vec_entry_end);
#endif
void spiffs_cb_object_event(
spiffs *fs,
spiffs_page_object_ix *objix,
int ev,
spiffs_obj_id obj_id,
spiffs_span_ix spix,
......@@ -704,7 +742,8 @@ s32_t spiffs_gc_quick(
s32_t spiffs_fd_find_new(
spiffs *fs,
spiffs_fd **fd);
spiffs_fd **fd,
const char *name);
s32_t spiffs_fd_return(
spiffs *fs,
......@@ -715,6 +754,13 @@ s32_t spiffs_fd_get(
spiffs_file f,
spiffs_fd **fd);
#if SPIFFS_TEMPORAL_FD_CACHE
void spiffs_fd_temporal_cache_rehash(
spiffs *fs,
const char *old_path,
const char *new_path);
#endif
#if SPIFFS_CACHE
void spiffs_cache_init(
spiffs *fs);
......
......@@ -30,6 +30,7 @@ Run the following command to flash an *aggregated* binary as is produced for exa
- See [below](#determine-flash-size) if you don't know or are uncertain about the capacity of the flash chip on your device. It might help to double check as e.g. some ESP-01 modules come with 512kB while others are equipped with 1MB.
- esptool.py is under heavy development. It's advised you run the latest version (check with `esptool.py version`). Since this documentation may not have been able to keep up refer to the [esptool flash modes documentation](https://github.com/themadinventor/esptool#flash-modes) for current options and parameters.
- The firmware image file contains default settings `dio` for flash mode and `40m` for flash frequency.
- In some uncommon cases, the [SDK init data](#sdk-init-data) may be invalid and NodeMCU may fail to boot. The easiest solution is to fully erase the chip before flashing:
`esptool.py --port <serial-port-of-ESP8266> erase_flash`
......
# Hardware FAQ
## What is this FAQ for?
This FAQ addresses hardware-specific issues relating to the NodeMcu firmware on
NoceMCU Inc Devkits and other ESP-8266 modules.
## Hardware Specifics
## Why file writes fail all the time on DEVKIT V1.0?
NodeMCU DEVKIT V1.0 uses ESP12-E-DIO(ESP-12-D) module. This module runs the
Flash memory in [Dual IO SPI](#whats-the-different-between-dio-and-qio-mode)
(DIO) mode. This firmware will not be correctly loaded if you use old flashtool
versions, and the filesystem will not work if you used a pre 0.9.6 firmware
version (<0.9.5) or old. The easiest way to resolve this problem s update all
the firmware and flash tool to current version.
- Use the latest [esptool.py](https://github.com/themadinventor/esptool) with
DIO support and command option to flash firmware, or
- Use the latest [NodeMCU flasher](https://github.com/NodeMCU/NodeMCU-flasher)
with default option. (You must select the `restore to default` option in advanced
menu tab), or
- Use the latest Espressif's flash tool -- see [this Espressif forum
topic](http://bbs.espressif.com/viewtopic.php?f=5&t=433) (without auto download
support). Use DIO mode and 32M flash size option, and flash latest firmware to
0x00000. Before flashing firmware, remember to hold FLASH button, and press RST
button once. Note that the new NodeMCU our firmware download tool, when
released, will be capable of flashing firmware automatically without any button
presses.
## What's the different between DIO and QIO mode?
Whether DIO or QIO modes are available depends on the physical connection
between the ESP8266 CPU and its onboard flash chip. QIO connects to the flash
using 5 data pins as compared to DIO's 3. This frees up an extra 2 IO pins for
GPIO use, but this also halves the read/write data-rate to Flash compared to
QIO modules.
## How to use DEVKIT V0.9 on Mac OS X?
<TODO>
### How does DEVKIT use DTR and RTS enter download mode?
<TODO>
This content is now maintained at [http://www.esp8266.com/wiki/doku.php?id=nodemcu-unofficial-faq](http://www.esp8266.com/wiki/doku.php?id=nodemcu-unofficial-faq).
This diff is collapsed.
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