Commit c8ac5cfb authored by Arnim Läuger's avatar Arnim Läuger Committed by GitHub
Browse files

Merge pull request #1980 from nodemcu/dev

2.1.0 master drop
parents 22e1adc4 787379f0
...@@ -113,8 +113,6 @@ void platform_spi_select( unsigned id, int is_select ); ...@@ -113,8 +113,6 @@ void platform_spi_select( unsigned id, int is_select );
int platform_spi_blkwrite( uint8_t id, size_t len, const uint8_t *data ); int platform_spi_blkwrite( uint8_t id, size_t len, const uint8_t *data );
int platform_spi_blkread( uint8_t id, size_t len, uint8_t *data ); int platform_spi_blkread( uint8_t id, size_t len, uint8_t *data );
int platform_spi_set_mosi( uint8_t id, uint16_t offset, uint8_t bitlen, spi_data_type data );
spi_data_type platform_spi_get_miso( uint8_t id, uint16_t offset, uint8_t bitlen );
int platform_spi_transaction( uint8_t id, uint8_t cmd_bitlen, spi_data_type cmd_data, int platform_spi_transaction( uint8_t id, uint8_t cmd_bitlen, spi_data_type cmd_data,
uint8_t addr_bitlen, spi_data_type addr_data, uint8_t addr_bitlen, spi_data_type addr_data,
uint16_t mosi_bitlen, uint8_t dummy_bitlen, int16_t miso_bitlen ); uint16_t mosi_bitlen, uint8_t dummy_bitlen, int16_t miso_bitlen );
...@@ -304,6 +302,9 @@ int platform_gpio_exists( unsigned id ); ...@@ -304,6 +302,9 @@ int platform_gpio_exists( unsigned id );
int platform_tmr_exists( unsigned id ); int platform_tmr_exists( unsigned id );
// ***************************************************************************** // *****************************************************************************
void* platform_print_deprecation_note( const char *msg, const char *time_frame);
// Helper macros // Helper macros
#define MOD_CHECK_ID( mod, id )\ #define MOD_CHECK_ID( mod, id )\
if( !platform_ ## mod ## _exists( id ) )\ if( !platform_ ## mod ## _exists( id ) )\
......
...@@ -139,7 +139,7 @@ vfs_dir *vfs_opendir( const char *name ) ...@@ -139,7 +139,7 @@ vfs_dir *vfs_opendir( const char *name )
return NULL; return NULL;
} }
vfs_item *vfs_stat( const char *name ) sint32_t vfs_stat( const char *name, struct vfs_stat *buf )
{ {
vfs_fs_fns *fs_fns; vfs_fs_fns *fs_fns;
const char *normname = normalize_path( name ); const char *normname = normalize_path( name );
...@@ -147,19 +147,19 @@ vfs_item *vfs_stat( const char *name ) ...@@ -147,19 +147,19 @@ vfs_item *vfs_stat( const char *name )
#ifdef BUILD_SPIFFS #ifdef BUILD_SPIFFS
if (fs_fns = myspiffs_realm( normname, &outname, FALSE )) { if (fs_fns = myspiffs_realm( normname, &outname, FALSE )) {
return fs_fns->stat( outname ); return fs_fns->stat( outname, buf );
} }
#endif #endif
#ifdef BUILD_FATFS #ifdef BUILD_FATFS
if (fs_fns = myfatfs_realm( normname, &outname, FALSE )) { if (fs_fns = myfatfs_realm( normname, &outname, FALSE )) {
vfs_item *r = fs_fns->stat( outname ); sint32_t r = fs_fns->stat( outname, buf );
c_free( outname ); c_free( outname );
return r; return r;
} }
#endif #endif
return NULL; return VFS_RES_ERR;
} }
sint32_t vfs_remove( const char *name ) sint32_t vfs_remove( const char *name )
......
...@@ -104,57 +104,9 @@ inline sint32_t vfs_closedir( vfs_dir *dd ) { return dd->fns->close( dd ); } ...@@ -104,57 +104,9 @@ inline sint32_t vfs_closedir( vfs_dir *dd ) { return dd->fns->close( dd ); }
// vfs_readdir - read next directory item // vfs_readdir - read next directory item
// dd: dir descriptor // dd: dir descriptor
// Returns: item object, or NULL in case of error // buf: pre-allocated stat structure to be filled in
inline vfs_item *vfs_readdir( vfs_dir *dd ) { return dd->fns->readdir( dd ); } // Returns: VFS_RES_OK if next item found, otherwise VFS_RES_ERR
inline sint32_t vfs_readdir( vfs_dir *dd, struct vfs_stat *buf ) { return dd->fns->readdir( dd, buf ); }
// ---------------------------------------------------------------------------
// dir item functions
//
// vfs_closeitem - close directory item and free memory
// di: item descriptor
// Returns: nothing
inline void vfs_closeitem( vfs_item *di ) { return di->fns->close( di ); }
// vfs_item_size - get item's size
// di: item descriptor
// Returns: Item size
inline uint32_t vfs_item_size( vfs_item *di ) { return di->fns->size( di ); }
// vfs_item_time - get item's modification time
// di: item descriptor
// Returns: Item modification time
inline sint32_t vfs_item_time( vfs_item *di, struct vfs_time *tm ) { return di->fns->time ? di->fns->time( di, tm ) : VFS_RES_ERR; }
// vfs_item_name - get item's name
// di: item descriptor
// Returns: Item name
inline const char *vfs_item_name( vfs_item *di ) { return di->fns->name( di ); }
// vfs_item_is_dir - check for directory
// di: item descriptor
// Returns: >0 if item is a directory, 0 if not
inline sint32_t vfs_item_is_dir( vfs_item *di ) { return di->fns->is_dir ? di->fns->is_dir( di ) : 0; }
// vfs_item_is_rdonly - check for read-only
// di: item descriptor
// Returns: >0 if item is read only, 0 if not
inline sint32_t vfs_item_is_rdonly( vfs_item *di ) { return di->fns->is_rdonly ? di->fns->is_rdonly( di ) : 0; }
// vfs_item_is_hidden - check for hidden attribute
// di: item descriptor
// Returns: >0 if item is hidden, 0 if not
inline sint32_t vfs_item_is_hidden( vfs_item *di ) { return di->fns->is_hidden ? di->fns->is_hidden( di ) : 0; }
// vfs_item_is_sys - check for sys attribute
// di: item descriptor
// Returns: >0 if item is sys, 0 if not
inline sint32_t vfs_item_is_sys( vfs_item *di ) { return di->fns->is_sys ? di->fns->is_sys( di ) : 0; }
// vfs_item_is_arch - check for archive attribute
// di: item descriptor
// Returns: >0 if item is archive, 0 if not
inline sint32_t vfs_item_is_arch( vfs_item *di ) { return di->fns->is_arch ? di->fns->is_arch( di ) : 0; }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// volume functions // volume functions
...@@ -188,8 +140,9 @@ vfs_dir *vfs_opendir( const char *name ); ...@@ -188,8 +140,9 @@ vfs_dir *vfs_opendir( const char *name );
// vfs_stat - stat file or directory // vfs_stat - stat file or directory
// name: file or directory name // name: file or directory name
// Returns: Item object, or NULL in case of error // buf: pre-allocated structure to be filled in
vfs_item *vfs_stat( const char *name ); // Returns: VFS_RES_OK, or VFS_RES_ERR in case of error
sint32_t vfs_stat( const char *name, struct vfs_stat *buf );
// vfs_remove - remove file or directory // vfs_remove - remove file or directory
// name: file or directory name // name: file or directory name
......
...@@ -47,6 +47,19 @@ struct vfs_file { ...@@ -47,6 +47,19 @@ struct vfs_file {
}; };
typedef const struct vfs_file vfs_file; typedef const struct vfs_file vfs_file;
// stat data
struct vfs_stat {
uint32_t size;
char name[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 // file descriptor functions
struct vfs_file_fns { struct vfs_file_fns {
sint32_t (*close)( const struct vfs_file *fd ); sint32_t (*close)( const struct vfs_file *fd );
...@@ -61,27 +74,6 @@ struct vfs_file_fns { ...@@ -61,27 +74,6 @@ struct vfs_file_fns {
}; };
typedef const struct vfs_file_fns vfs_file_fns; typedef const struct vfs_file_fns vfs_file_fns;
// generic dir item descriptor
struct vfs_item {
int fs_type;
const struct vfs_item_fns *fns;
};
typedef const struct vfs_item vfs_item;
// directory item functions
struct vfs_item_fns {
void (*close)( const struct vfs_item *di );
uint32_t (*size)( const struct vfs_item *di );
sint32_t (*time)( const struct vfs_item *di, struct vfs_time *tm );
const char *(*name)( const struct vfs_item *di );
sint32_t (*is_dir)( const struct vfs_item *di );
sint32_t (*is_rdonly)( const struct vfs_item *di );
sint32_t (*is_hidden)( const struct vfs_item *di );
sint32_t (*is_sys)( const struct vfs_item *di );
sint32_t (*is_arch)( const struct vfs_item *di );
};
typedef const struct vfs_item_fns vfs_item_fns;
// generic dir descriptor // generic dir descriptor
struct vfs_dir { struct vfs_dir {
int fs_type; int fs_type;
...@@ -92,7 +84,7 @@ typedef const struct vfs_dir vfs_dir; ...@@ -92,7 +84,7 @@ typedef const struct vfs_dir vfs_dir;
// dir descriptor functions // dir descriptor functions
struct vfs_dir_fns { struct vfs_dir_fns {
sint32_t (*close)( const struct vfs_dir *dd ); sint32_t (*close)( const struct vfs_dir *dd );
vfs_item *(*readdir)( const struct vfs_dir *dd ); sint32_t (*readdir)( const struct vfs_dir *dd, struct vfs_stat *buf );
}; };
typedef const struct vfs_dir_fns vfs_dir_fns; typedef const struct vfs_dir_fns vfs_dir_fns;
...@@ -113,7 +105,7 @@ struct vfs_fs_fns { ...@@ -113,7 +105,7 @@ struct vfs_fs_fns {
vfs_vol *(*mount)( const char *name, int num ); vfs_vol *(*mount)( const char *name, int num );
vfs_file *(*open)( const char *name, const char *mode ); vfs_file *(*open)( const char *name, const char *mode );
vfs_dir *(*opendir)( const char *name ); vfs_dir *(*opendir)( const char *name );
vfs_item *(*stat)( const char *name ); sint32_t (*stat)( const char *name, struct vfs_stat *buf );
sint32_t (*remove)( const char *name ); sint32_t (*remove)( const char *name );
sint32_t (*rename)( const char *oldname, const char *newname ); sint32_t (*rename)( const char *oldname, const char *newname );
sint32_t (*mkdir)( const char *name ); sint32_t (*mkdir)( const char *name );
......
...@@ -12,11 +12,10 @@ ...@@ -12,11 +12,10 @@
# a generated lib/image xxx.a () # a generated lib/image xxx.a ()
# #
ifndef PDIR ifndef PDIR
GEN_LIBS = libpm.a
GEN_LIBS = libcjson.a
endif endif
STD_CFLAGS=-std=gnu11 -Wimplicit
############################################################# #############################################################
# Configuration i.e. compile options etc. # Configuration i.e. compile options etc.
...@@ -41,7 +40,13 @@ endif ...@@ -41,7 +40,13 @@ endif
INCLUDES := $(INCLUDES) -I $(PDIR)include INCLUDES := $(INCLUDES) -I $(PDIR)include
INCLUDES += -I ./ INCLUDES += -I ./
INCLUDES += -I ./include
INCLUDES += -I ../include
INCLUDES += -I ../../include
INCLUDES += -I ../lua
INCLUDES += -I ../platform
INCLUDES += -I ../libc INCLUDES += -I ../libc
PDIR := ../$(PDIR) PDIR := ../$(PDIR)
sinclude $(PDIR)Makefile sinclude $(PDIR)Makefile
#include "pmSleep.h"
#ifdef PMSLEEP_ENABLE
#define STRINGIFY_VAL(x) #x
#define STRINGIFY(x) STRINGIFY_VAL(x)
//holds duration error string
//uint32 PMSLEEP_SLEEP_MAX_TIME=FPM_SLEEP_MAX_TIME-1;
const char *PMSLEEP_DURATION_ERR_STR="duration: 0 or "STRINGIFY(PMSLEEP_SLEEP_MIN_TIME)"-"STRINGIFY(PMSLEEP_SLEEP_MAX_TIME)" us";
/* INTERNAL VARIABLES */
static void (*user_suspend_cb)(void) = NULL;
static void (*user_resume_cb)(void) = NULL;
static uint8 resume_opmode = 0;
static os_timer_t wifi_suspended_test_timer;
static uint8 autosleep_setting_temp = 0;
static os_timer_t null_mode_check_timer;
static pmSleep_param_t current_config;
/* INTERNAL FUNCTION DECLARATIONS */
static void suspend_all_timers(void);
static void null_mode_check_timer_cb(void* arg);
static void resume_all_timers(void);
static inline void register_lua_cb(lua_State* L,int* cb_ref);
static void resume_cb(void);
static void wifi_suspended_timer_cb(int arg);
/* INTERNAL FUNCTIONS */
#include "swTimer/swTimer.h"
static void suspend_all_timers(void){
#ifdef ENABLE_TIMER_SUSPEND
swtmr_suspend(NULL);
#endif
return;
}
static void resume_all_timers(void){
#ifdef ENABLE_TIMER_SUSPEND
swtmr_resume(NULL);
#endif
return;
}
static void null_mode_check_timer_cb(void* arg){
if (wifi_get_opmode() == NULL_MODE){
//check if uart 0 tx buffer is empty and uart 1 tx buffer is empty
if(current_config.sleep_mode == LIGHT_SLEEP_T){
if((READ_PERI_REG(UART_STATUS(0)) & (UART_TXFIFO_CNT<<UART_TXFIFO_CNT_S)) == 0 &&
(READ_PERI_REG(UART_STATUS(1)) & (UART_TXFIFO_CNT<<UART_TXFIFO_CNT_S)) == 0){
ets_timer_disarm(&null_mode_check_timer);
suspend_all_timers();
//Ensure UART 0/1 TX FIFO is clear
SET_PERI_REG_MASK(UART_CONF0(0), UART_TXFIFO_RST);//RESET FIFO
CLEAR_PERI_REG_MASK(UART_CONF0(0), UART_TXFIFO_RST);
SET_PERI_REG_MASK(UART_CONF0(1), UART_TXFIFO_RST);//RESET FIFO
CLEAR_PERI_REG_MASK(UART_CONF0(1), UART_TXFIFO_RST);
wifi_fpm_do_sleep(current_config.sleep_duration);
return;
}
else{
return;
}
}
else{ //MODEM_SLEEP_T
sint8 retval_wifi_fpm_do_sleep = wifi_fpm_do_sleep(current_config.sleep_duration); // Request WiFi suspension and store return value
// If wifi_fpm_do_sleep success
if (retval_wifi_fpm_do_sleep == 0){
PMSLEEP_DBG("wifi_fpm_do_sleep success, starting wifi_suspend_test timer");
os_timer_disarm(&wifi_suspended_test_timer);
os_timer_setfn(&wifi_suspended_test_timer, (os_timer_func_t*)wifi_suspended_timer_cb, NULL);
os_timer_arm(&wifi_suspended_test_timer, 1, 1);
}
else{ // This should never happen. if it does, return the value for error reporting
wifi_fpm_close();
PMSLEEP_ERR("wifi_fpm_do_sleep returned %d", retval_wifi_fpm_do_sleep);
}
}
ets_timer_disarm(&null_mode_check_timer);
return;
}
}
//function to register a lua callback function in the LUA_REGISTRYINDEX for later execution
static inline void register_lua_cb(lua_State* L, int* cb_ref){
int ref = luaL_ref(L, LUA_REGISTRYINDEX);
if( *cb_ref != LUA_NOREF) luaL_unref(L, LUA_REGISTRYINDEX, *cb_ref);
*cb_ref = ref;
}
// C callback for bringing WiFi back from forced sleep
static void resume_cb(void){
PMSLEEP_DBG("START");
//TODO: Add support for extended light sleep duration
wifi_fpm_close(); // Disable force sleep API
PMSLEEP_DBG("WiFi resume");
resume_all_timers();
//this section restores null mode auto sleep setting
if(autosleep_setting_temp == 1) {
wifi_fpm_auto_sleep_set_in_null_mode(autosleep_setting_temp);
autosleep_setting_temp = 0;
}
//this section restores previous wifi mode
if (resume_opmode == STATION_MODE || resume_opmode == SOFTAP_MODE || resume_opmode == STATIONAP_MODE){
if (wifi_set_opmode_current(resume_opmode)){
if (resume_opmode == STATION_MODE || resume_opmode == STATIONAP_MODE){
wifi_station_connect(); // Connect to currently configured Access Point
}
PMSLEEP_DBG("WiFi mode restored");
resume_opmode = 0; // reset variable to default value
}
}
else{
wifi_set_opmode_current(NULL_MODE);
}
//execute the external resume callback
if (user_resume_cb != NULL){
PMSLEEP_DBG("calling user resume cb (%p)", user_resume_cb);
user_resume_cb();
user_resume_cb = NULL;
}
PMSLEEP_DBG("END");
return;
}
// This callback executes the suspended callback when Wifi suspension is in effect
static void wifi_suspended_timer_cb(int arg){
// check if wifi is suspended.
if (pmSleep_get_state() == PMSLEEP_SUSPENDED){
os_timer_disarm(&wifi_suspended_test_timer); // Stop rf_closed_timer
PMSLEEP_DBG("WiFi is suspended");
//execute the external suspended callback
if (user_suspend_cb != NULL){
PMSLEEP_DBG("calling user suspend cb (%p)", user_suspend_cb);
user_suspend_cb();
user_suspend_cb = NULL;
}
PMSLEEP_DBG("END");
}
}
/* EXTERNAL FUNCTIONS */
//this function executes the application developer's Lua callback
void pmSleep_execute_lua_cb(int* cb_ref){
if (*cb_ref != LUA_NOREF){
lua_State* L = lua_getstate(); // Get Lua main thread pointer
lua_rawgeti(L, LUA_REGISTRYINDEX, *cb_ref); // Push resume callback onto the stack
lua_unref(L, *cb_ref); // Remove resume callback from registry
*cb_ref = LUA_NOREF; // Update variable since reference is no longer valid
lua_call(L, 0, 0); // Execute resume callback
}
}
//this function checks current wifi suspension state and returns the result
uint8 pmSleep_get_state(void){
if (fpm_rf_is_closed()) return PMSLEEP_SUSPENDED;
else if (fpm_is_open()) return PMSLEEP_SUSPENSION_PENDING;
else return PMSLEEP_AWAKE;
}
//this function parses the lua configuration table provided by the application developer
int pmSleep_parse_table_lua( lua_State* L, int table_idx, pmSleep_param_t *cfg, int *suspend_lua_cb_ref, int *resume_lua_cb_ref){
lua_Integer Linteger_tmp = 0;
lua_getfield(L, table_idx, "duration");
if( !lua_isnil(L, -1) ){ /* found? */
if( lua_isnumber(L, -1) ){
lua_Integer Linteger=luaL_checkinteger(L, -1);
luaL_argcheck(L,(((Linteger >= PMSLEEP_SLEEP_MIN_TIME) && (Linteger <= PMSLEEP_SLEEP_MAX_TIME)) ||
(Linteger == 0)), table_idx, PMSLEEP_DURATION_ERR_STR);
cfg->sleep_duration = (uint32)Linteger; // Get suspend duration
}
else{
return luaL_argerror( L, table_idx, "duration: must be number" );
}
}
else{
return luaL_argerror( L, table_idx, PMSLEEP_DURATION_ERR_STR );
}
lua_pop(L, 1);
if( cfg->sleep_mode == MODEM_SLEEP_T ){ //WiFi suspend
lua_getfield(L, table_idx, "suspend_cb");
if( !lua_isnil(L, -1) ){ /* found? */
if( lua_isfunction(L, -1) ){
lua_pushvalue(L, -1); // Push resume callback to the top of the stack
register_lua_cb(L, suspend_lua_cb_ref);
}
else{
return luaL_argerror( L, table_idx, "suspend_cb: must be function" );
}
}
lua_pop(L, 1);
}
else if (cfg->sleep_mode == LIGHT_SLEEP_T){ //CPU suspend
#ifdef ENABLE_TIMER_SUSPEND
lua_getfield(L, table_idx, "wake_pin");
if( !lua_isnil(L, -1) ){ /* found? */
if( lua_isnumber(L, -1) ){
Linteger_tmp=lua_tointeger(L, -1);
luaL_argcheck(L, (platform_gpio_exists(Linteger_tmp) && Linteger_tmp > 0), table_idx, "wake_pin: Invalid interrupt pin");
cfg->wake_pin = Linteger_tmp;
}
else{
return luaL_argerror( L, table_idx, "wake_pin: must be number" );
}
}
else if(cfg->sleep_duration == 0){
return luaL_argerror( L, table_idx, "wake_pin: must specify pin if sleep duration is indefinite" );
}
lua_pop(L, 1);
lua_getfield(L, table_idx, "int_type");
if( !lua_isnil(L, -1) ){ /* found? */
if( lua_isnumber(L, -1) ){
Linteger_tmp=lua_tointeger(L, -1);
luaL_argcheck(L, (Linteger_tmp == GPIO_PIN_INTR_ANYEDGE || Linteger_tmp == GPIO_PIN_INTR_HILEVEL
|| Linteger_tmp == GPIO_PIN_INTR_LOLEVEL || Linteger_tmp == GPIO_PIN_INTR_NEGEDGE
|| Linteger_tmp == GPIO_PIN_INTR_POSEDGE ), 1, "int_type: invalid interrupt type");
cfg->int_type = Linteger_tmp;
}
else{
return luaL_argerror( L, table_idx, "int_type: must be number" );
}
}
else{
cfg->int_type = GPIO_PIN_INTR_LOLEVEL;
}
lua_pop(L, 1);
#endif
}
else{
return luaL_error(L, "FPM Sleep mode not available");
}
lua_getfield(L, table_idx, "resume_cb");
if( !lua_isnil(L, -1) ){ /* found? */
if( lua_isfunction(L, -1) ){
lua_pushvalue(L, -1); // Push resume callback to the top of the stack
register_lua_cb(L, resume_lua_cb_ref);
}
else{
return luaL_argerror( L, table_idx, "resume_cb: must be function" );
}
}
lua_pop(L, 1);
lua_getfield(L, table_idx, "preserve_mode");
if( !lua_isnil(L, -1) ){ /* found? */
if( lua_isboolean(L, -1) ){
cfg->preserve_opmode=lua_toboolean(L, -1);
}
else{
return luaL_argerror( L, table_idx, "preseve_mode: must be boolean" );
}
}
else{
cfg->preserve_opmode = true;
}
lua_pop(L, 1);
//if sleep duration is zero, set indefinite sleep duration
if( cfg->sleep_duration == 0 ){
cfg->sleep_duration = FPM_SLEEP_MAX_TIME;
}
return 0;
}
//This function resumes ESP from MODEM_SLEEP
void pmSleep_resume(void (*resume_cb_ptr)(void)){
PMSLEEP_DBG("START");
uint8 fpm_state = pmSleep_get_state();
if(fpm_state>0){
if(resume_cb_ptr != NULL){
user_resume_cb = resume_cb_ptr;
}
wifi_fpm_do_wakeup(); // Wake up from sleep
resume_cb(); // Finish WiFi wakeup
}
PMSLEEP_DBG("END");
return;
}
//this function puts the ESP8266 into MODEM_SLEEP or LIGHT_SLEEP
void pmSleep_suspend(pmSleep_param_t *cfg){
PMSLEEP_DBG("START");
lua_State* L = lua_getstate();
#ifndef ENABLE_TIMER_SUSPEND
if(cfg->sleep_mode == LIGHT_SLEEP_T){
luaL_error(L, "timer suspend API is disabled, light sleep unavailable");
return;
}
#endif
uint8 current_wifi_mode = wifi_get_opmode(); // Get Current WiFi mode
user_resume_cb = cfg->resume_cb_ptr; //pointer to hold address of user_cb
user_suspend_cb = cfg->suspend_cb_ptr; //pointer to hold address of user_cb
// If Preserve_wifi_mode parameter is TRUE and current WiFi mode is not NULL_MODE
if (cfg->preserve_opmode && current_wifi_mode != 0){
resume_opmode = current_wifi_mode;
}
if (current_wifi_mode == STATION_MODE || current_wifi_mode == STATIONAP_MODE){
wifi_station_disconnect(); // Disconnect from Access Point
}
//the null mode sleep functionality interferes with the forced sleep API and must be disabled
if(get_fpm_auto_sleep_flag() == 1){
autosleep_setting_temp = 1;
wifi_fpm_auto_sleep_set_in_null_mode(0);
}
// If wifi_set_opmode_current is successful
if (wifi_set_opmode_current(NULL_MODE)){
PMSLEEP_DBG("sleep_mode is %s", cfg->sleep_mode == MODEM_SLEEP_T ? "MODEM_SLEEP_T" : "LIGHT_SLEEP_T");
wifi_fpm_set_sleep_type(cfg->sleep_mode);
wifi_fpm_open(); // Enable force sleep API
if (cfg->sleep_mode == LIGHT_SLEEP_T){
#ifdef ENABLE_TIMER_SUSPEND
if(platform_gpio_exists(cfg->wake_pin) && cfg->wake_pin > 0){
PMSLEEP_DBG("Wake-up pin is %d\t interrupt type is %d", cfg->wake_pin, cfg->int_type);
if((cfg->int_type != GPIO_PIN_INTR_ANYEDGE && cfg->int_type != GPIO_PIN_INTR_HILEVEL
&& cfg->int_type != GPIO_PIN_INTR_LOLEVEL && cfg->int_type != GPIO_PIN_INTR_NEGEDGE
&& cfg->int_type != GPIO_PIN_INTR_POSEDGE )){
wifi_fpm_close();
PMSLEEP_DBG("Invalid interrupt type");
return;
}
GPIO_DIS_OUTPUT(pin_num[cfg->wake_pin]);
PIN_FUNC_SELECT(pin_mux[cfg->wake_pin], pin_func[cfg->wake_pin]);
wifi_enable_gpio_wakeup(pin_num[cfg->wake_pin], cfg->int_type);
}
else if(cfg->sleep_duration == FPM_SLEEP_MAX_TIME && cfg->wake_pin == 255){
wifi_fpm_close();
PMSLEEP_DBG("No wake-up pin defined");
return;
}
#endif
}
wifi_fpm_set_wakeup_cb(resume_cb); // Set resume C callback
c_memcpy(&current_config, cfg, sizeof(pmSleep_param_t));
PMSLEEP_DBG("sleep duration is %d", current_config.sleep_duration);
//this timer intentionally bypasses the swtimer timer registration process
ets_timer_disarm(&null_mode_check_timer);
ets_timer_setfn(&null_mode_check_timer, null_mode_check_timer_cb, false);
ets_timer_arm_new(&null_mode_check_timer, 1, 1, 1);
}
else{
PMSLEEP_ERR("opmode change fail");
}
PMSLEEP_DBG("END");
return;
}
#endif
#ifndef __FPM_SLEEP_H__
#define __FPM_SLEEP_H__
#include "user_interface.h"
#include "c_types.h"
#include "lauxlib.h"
#include "gpio.h"
#include "platform.h"
#include "task/task.h"
#include "c_string.h"
#if defined(DEVELOP_VERSION)
#define PMSLEEP_DEBUG
#endif
#if defined(PMSLEEP_DEBUG)
#define PMSLEEP_DBG(fmt, ...) dbg_printf("\tPMSLEEP(%s):"fmt"\n", __FUNCTION__, ##__VA_ARGS__)
#else
#define PMSLEEP_DBG(...) //c_printf(__VA_ARGS__)
#endif
#if defined(NODE_ERROR)
#define PMSLEEP_ERR(fmt, ...) NODE_ERR("%s"fmt"\n", "PMSLEEP:", ##__VA_ARGS__)
#else
#define PMSLEEP_ERR(...)
#endif
#define PMSLEEP_SLEEP_MIN_TIME 50000
#define PMSLEEP_SLEEP_MAX_TIME 268435454 //FPM_MAX_SLEEP_TIME-1
#define pmSleep_INIT_CFG(X) pmSleep_param_t X = {.sleep_duration=0, .wake_pin=255, \
.preserve_opmode=TRUE, .suspend_cb_ptr=NULL, .resume_cb_ptr=NULL}
#define PMSLEEP_INT_MAP \
{ LSTRKEY( "INT_BOTH" ), LNUMVAL( GPIO_PIN_INTR_ANYEDGE ) }, \
{ LSTRKEY( "INT_UP" ), LNUMVAL( GPIO_PIN_INTR_POSEDGE ) }, \
{ LSTRKEY( "INT_DOWN" ), LNUMVAL( GPIO_PIN_INTR_NEGEDGE ) }, \
{ LSTRKEY( "INT_HIGH" ), LNUMVAL( GPIO_PIN_INTR_HILEVEL ) }, \
{ LSTRKEY( "INT_LOW" ), LNUMVAL( GPIO_PIN_INTR_LOLEVEL ) }
typedef struct pmSleep_param{
uint32 sleep_duration;
uint8 sleep_mode;
uint8 wake_pin;
uint8 int_type;
bool preserve_opmode;
void (*suspend_cb_ptr)(void);
void (*resume_cb_ptr)(void);
}pmSleep_param_t; //structure to hold pmSleep configuration
enum PMSLEEP_STATE{
PMSLEEP_AWAKE = 0,
PMSLEEP_SUSPENSION_PENDING = 1,
PMSLEEP_SUSPENDED = 2
};
uint8 pmSleep_get_state(void);
void pmSleep_resume(void (*resume_cb_ptr)(void));
void pmSleep_suspend(pmSleep_param_t *param);
void pmSleep_execute_lua_cb(int* cb_ref);
int pmSleep_parse_table_lua( lua_State* L, int table_idx, pmSleep_param_t *cfg, int *suspend_lua_cb_ref, int *resume_lua_cb_ref);
#endif // __FPM_SLEEP_H__
/* Copyright (C) 2012-2015 Mark Nunberg.
*
* See included LICENSE file for license details.
*/
#include "jsonsl.h"
#include <assert.h>
#include <limits.h>
#include <ctype.h>
#ifdef JSONSL_USE_METRICS
#define XMETRICS \
X(STRINGY_INSIGNIFICANT) \
X(STRINGY_SLOWPATH) \
X(ALLOWED_WHITESPACE) \
X(QUOTE_FASTPATH) \
X(SPECIAL_FASTPATH) \
X(SPECIAL_WSPOP) \
X(SPECIAL_SLOWPATH) \
X(GENERIC) \
X(STRUCTURAL_TOKEN) \
X(SPECIAL_SWITCHFIRST) \
X(STRINGY_CATCH) \
X(NUMBER_FASTPATH) \
X(ESCAPES) \
X(TOTAL) \
struct jsonsl_metrics_st {
#define X(m) \
unsigned long metric_##m;
XMETRICS
#undef X
};
static struct jsonsl_metrics_st GlobalMetrics = { 0 };
static unsigned long GenericCounter[0x100] = { 0 };
static unsigned long StringyCatchCounter[0x100] = { 0 };
#define INCR_METRIC(m) \
GlobalMetrics.metric_##m++;
#define INCR_GENERIC(c) \
INCR_METRIC(GENERIC); \
GenericCounter[c]++; \
#define INCR_STRINGY_CATCH(c) \
INCR_METRIC(STRINGY_CATCH); \
StringyCatchCounter[c]++;
JSONSL_API
void jsonsl_dump_global_metrics(void)
{
int ii;
printf("JSONSL Metrics:\n");
#define X(m) \
printf("\t%-30s %20lu (%0.2f%%)\n", #m, GlobalMetrics.metric_##m, \
(float)((float)(GlobalMetrics.metric_##m/(float)GlobalMetrics.metric_TOTAL)) * 100);
XMETRICS
#undef X
printf("Generic Characters:\n");
for (ii = 0; ii < 0xff; ii++) {
if (GenericCounter[ii]) {
printf("\t[ %c ] %lu\n", ii, GenericCounter[ii]);
}
}
printf("Weird string loop\n");
for (ii = 0; ii < 0xff; ii++) {
if (StringyCatchCounter[ii]) {
printf("\t[ %c ] %lu\n", ii, StringyCatchCounter[ii]);
}
}
}
#else
#define INCR_METRIC(m)
#define INCR_GENERIC(c)
#define INCR_STRINGY_CATCH(c)
JSONSL_API
void jsonsl_dump_global_metrics(void) { }
#endif /* JSONSL_USE_METRICS */
#define CASE_DIGITS \
case '1': \
case '2': \
case '3': \
case '4': \
case '5': \
case '6': \
case '7': \
case '8': \
case '9': \
case '0':
static unsigned extract_special(unsigned);
static int is_special_end(unsigned);
static int is_allowed_whitespace(unsigned);
static int is_allowed_escape(unsigned);
static int is_simple_char(unsigned);
static char get_escape_equiv(unsigned);
JSONSL_API
size_t jsonsl_get_size(int nlevels)
{
return sizeof (struct jsonsl_st) + ( (nlevels-1) * sizeof (struct jsonsl_state_st)) ;
}
JSONSL_API
jsonsl_t jsonsl_init(jsonsl_t jsn, int nlevels)
{
unsigned int ii;
memset(jsn, 0, jsonsl_get_size(nlevels));
jsn->levels_max = nlevels;
jsn->max_callback_level = -1;
jsonsl_reset(jsn);
for (ii = 0; ii < jsn->levels_max; ii++) {
jsn->stack[ii].level = ii;
}
return jsn;
}
JSONSL_API
jsonsl_t jsonsl_new(int nlevels)
{
struct jsonsl_st *jsn = (struct jsonsl_st *)
calloc(1, jsonsl_get_size(nlevels));
if (jsn) {
jsonsl_init(jsn, nlevels);
}
return jsn;
}
JSONSL_API
void jsonsl_reset(jsonsl_t jsn)
{
jsn->tok_last = 0;
jsn->can_insert = 1;
jsn->pos = 0;
jsn->level = 0;
jsn->stopfl = 0;
jsn->in_escape = 0;
jsn->expecting = 0;
}
JSONSL_API
void jsonsl_destroy(jsonsl_t jsn)
{
if (jsn) {
free(jsn);
}
}
#define FASTPARSE_EXHAUSTED 1
#define FASTPARSE_BREAK 0
/*
* This function is meant to accelerate string parsing, reducing the main loop's
* check if we are indeed a string.
*
* @param jsn the parser
* @param[in,out] bytes_p A pointer to the current buffer (i.e. current position)
* @param[in,out] nbytes_p A pointer to the current size of the buffer
* @return true if all bytes have been exhausted (and thus the main loop can
* return), false if a special character was examined which requires greater
* examination.
*/
static int
jsonsl__str_fastparse(jsonsl_t jsn,
const jsonsl_uchar_t **bytes_p, size_t *nbytes_p)
{
const jsonsl_uchar_t *bytes = *bytes_p;
const jsonsl_uchar_t *end;
for (end = bytes + *nbytes_p; bytes != end; bytes++) {
if (
#ifdef JSONSL_USE_WCHAR
*bytes >= 0x100 ||
#endif /* JSONSL_USE_WCHAR */
(is_simple_char(*bytes))) {
INCR_METRIC(TOTAL);
INCR_METRIC(STRINGY_INSIGNIFICANT);
} else {
/* Once we're done here, re-calculate the position variables */
jsn->pos += (bytes - *bytes_p);
*nbytes_p -= (bytes - *bytes_p);
*bytes_p = bytes;
return FASTPARSE_BREAK;
}
}
/* Once we're done here, re-calculate the position variables */
jsn->pos += (bytes - *bytes_p);
return FASTPARSE_EXHAUSTED;
}
/* Functions exactly like str_fastparse, except it also accepts a 'state'
* argument, since the number's value is updated in the state. */
static int
jsonsl__num_fastparse(jsonsl_t jsn,
const jsonsl_uchar_t **bytes_p, size_t *nbytes_p,
struct jsonsl_state_st *state)
{
int exhausted = 1;
size_t nbytes = *nbytes_p;
const jsonsl_uchar_t *bytes = *bytes_p;
for (; nbytes; nbytes--, bytes++) {
jsonsl_uchar_t c = *bytes;
if (isdigit(c)) {
INCR_METRIC(TOTAL);
INCR_METRIC(NUMBER_FASTPATH);
state->nelem = (state->nelem * 10) + (c - 0x30);
} else {
exhausted = 0;
break;
}
}
jsn->pos += (*nbytes_p - nbytes);
if (exhausted) {
return FASTPARSE_EXHAUSTED;
}
*nbytes_p = nbytes;
*bytes_p = bytes;
return FASTPARSE_BREAK;
}
JSONSL_API
void
jsonsl_feed(jsonsl_t jsn, const jsonsl_char_t *bytes, size_t nbytes)
{
#define INVOKE_ERROR(eb) \
if (jsn->error_callback(jsn, JSONSL_ERROR_##eb, state, (char*)c)) { \
goto GT_AGAIN; \
} \
return;
#define STACK_PUSH \
if (jsn->level >= (levels_max-1)) { \
jsn->error_callback(jsn, JSONSL_ERROR_LEVELS_EXCEEDED, state, (char*)c); \
return; \
} \
state = jsn->stack + (++jsn->level); \
state->ignore_callback = jsn->stack[jsn->level-1].ignore_callback; \
state->pos_begin = jsn->pos;
#define STACK_POP_NOPOS \
state->pos_cur = jsn->pos; \
state = jsn->stack + (--jsn->level);
#define STACK_POP \
STACK_POP_NOPOS; \
state->pos_cur = jsn->pos;
#define CALLBACK_AND_POP_NOPOS(T) \
state->pos_cur = jsn->pos; \
DO_CALLBACK(T, POP); \
state->nescapes = 0; \
state = jsn->stack + (--jsn->level);
#define CALLBACK_AND_POP(T) \
CALLBACK_AND_POP_NOPOS(T); \
state->pos_cur = jsn->pos;
#define SPECIAL_POP \
CALLBACK_AND_POP(SPECIAL); \
jsn->expecting = 0; \
jsn->tok_last = 0; \
#define CUR_CHAR (*(jsonsl_uchar_t*)c)
#define DO_CALLBACK(T, action) \
if (jsn->call_##T && \
jsn->max_callback_level > state->level && \
state->ignore_callback == 0) { \
\
if (jsn->action_callback_##action) { \
jsn->action_callback_##action(jsn, JSONSL_ACTION_##action, state, (jsonsl_char_t*)c); \
} else if (jsn->action_callback) { \
jsn->action_callback(jsn, JSONSL_ACTION_##action, state, (jsonsl_char_t*)c); \
} \
if (jsn->stopfl) { return; } \
}
/**
* Verifies that we are able to insert the (non-string) item into a hash.
*/
#define ENSURE_HVAL \
if (state->nelem % 2 == 0 && state->type == JSONSL_T_OBJECT) { \
INVOKE_ERROR(HKEY_EXPECTED); \
}
#define VERIFY_SPECIAL(lit) \
if (CUR_CHAR != (lit)[jsn->pos - state->pos_begin]) { \
INVOKE_ERROR(SPECIAL_EXPECTED); \
}
#define STATE_SPECIAL_LENGTH \
(state)->nescapes
#define IS_NORMAL_NUMBER \
((state)->special_flags == JSONSL_SPECIALf_UNSIGNED || \
(state)->special_flags == JSONSL_SPECIALf_SIGNED)
#define STATE_NUM_LAST jsn->tok_last
#define CONTINUE_NEXT_CHAR() continue
const jsonsl_uchar_t *c = (jsonsl_uchar_t*)bytes;
size_t levels_max = jsn->levels_max;
struct jsonsl_state_st *state = jsn->stack + jsn->level;
jsn->base = bytes;
for (; nbytes; nbytes--, jsn->pos++, c++) {
unsigned state_type;
INCR_METRIC(TOTAL);
GT_AGAIN:
state_type = state->type;
/* Most common type is typically a string: */
if (state_type & JSONSL_Tf_STRINGY) {
/* Special escape handling for some stuff */
if (jsn->in_escape) {
jsn->in_escape = 0;
if (!is_allowed_escape(CUR_CHAR)) {
INVOKE_ERROR(ESCAPE_INVALID);
} else if (CUR_CHAR == 'u') {
DO_CALLBACK(UESCAPE, UESCAPE);
if (jsn->return_UESCAPE) {
return;
}
}
CONTINUE_NEXT_CHAR();
}
if (jsonsl__str_fastparse(jsn, &c, &nbytes) ==
FASTPARSE_EXHAUSTED) {
/* No need to readjust variables as we've exhausted the iterator */
return;
} else {
if (CUR_CHAR == '"') {
goto GT_QUOTE;
} else if (CUR_CHAR == '\\') {
goto GT_ESCAPE;
} else {
INVOKE_ERROR(WEIRD_WHITESPACE);
}
}
INCR_METRIC(STRINGY_SLOWPATH);
} else if (state_type == JSONSL_T_SPECIAL) {
/* Fast track for signed/unsigned */
if (IS_NORMAL_NUMBER) {
if (jsonsl__num_fastparse(jsn, &c, &nbytes, state) ==
FASTPARSE_EXHAUSTED) {
return;
} else {
goto GT_SPECIAL_NUMERIC;
}
} else if (state->special_flags == JSONSL_SPECIALf_DASH) {
if (!isdigit(CUR_CHAR)) {
INVOKE_ERROR(INVALID_NUMBER);
}
if (CUR_CHAR == '0') {
state->special_flags = JSONSL_SPECIALf_ZERO|JSONSL_SPECIALf_SIGNED;
} else if (isdigit(CUR_CHAR)) {
state->special_flags = JSONSL_SPECIALf_SIGNED;
state->nelem = CUR_CHAR - 0x30;
} else {
INVOKE_ERROR(INVALID_NUMBER);
}
CONTINUE_NEXT_CHAR();
} else if (state->special_flags == JSONSL_SPECIALf_ZERO) {
if (isdigit(CUR_CHAR)) {
/* Following a zero! */
INVOKE_ERROR(INVALID_NUMBER);
}
/* Unset the 'zero' flag: */
if (state->special_flags & JSONSL_SPECIALf_SIGNED) {
state->special_flags = JSONSL_SPECIALf_SIGNED;
} else {
state->special_flags = JSONSL_SPECIALf_UNSIGNED;
}
goto GT_SPECIAL_NUMERIC;
}
if (state->special_flags & JSONSL_SPECIALf_NUMERIC) {
GT_SPECIAL_NUMERIC:
switch (CUR_CHAR) {
CASE_DIGITS
STATE_NUM_LAST = '1';
CONTINUE_NEXT_CHAR();
case '.':
if (state->special_flags & JSONSL_SPECIALf_FLOAT) {
INVOKE_ERROR(INVALID_NUMBER);
}
state->special_flags |= JSONSL_SPECIALf_FLOAT;
STATE_NUM_LAST = '.';
CONTINUE_NEXT_CHAR();
case 'e':
case 'E':
if (state->special_flags & JSONSL_SPECIALf_EXPONENT) {
INVOKE_ERROR(INVALID_NUMBER);
}
state->special_flags |= JSONSL_SPECIALf_EXPONENT;
STATE_NUM_LAST = 'e';
CONTINUE_NEXT_CHAR();
case '-':
case '+':
if (STATE_NUM_LAST != 'e') {
INVOKE_ERROR(INVALID_NUMBER);
}
STATE_NUM_LAST = '-';
CONTINUE_NEXT_CHAR();
default:
if (is_special_end(CUR_CHAR)) {
goto GT_SPECIAL_POP;
}
INVOKE_ERROR(INVALID_NUMBER);
break;
}
}
/* else if (!NUMERIC) */
if (!is_special_end(CUR_CHAR)) {
STATE_SPECIAL_LENGTH++;
/* Verify TRUE, FALSE, NULL */
if (state->special_flags == JSONSL_SPECIALf_TRUE) {
VERIFY_SPECIAL("true");
} else if (state->special_flags == JSONSL_SPECIALf_FALSE) {
VERIFY_SPECIAL("false");
} else if (state->special_flags == JSONSL_SPECIALf_NULL) {
VERIFY_SPECIAL("null");
}
INCR_METRIC(SPECIAL_FASTPATH);
CONTINUE_NEXT_CHAR();
}
GT_SPECIAL_POP:
jsn->can_insert = 0;
if (IS_NORMAL_NUMBER) {
/* Nothing */
} else if (state->special_flags == JSONSL_SPECIALf_ZERO ||
state->special_flags == (JSONSL_SPECIALf_ZERO|JSONSL_SPECIALf_SIGNED)) {
/* 0 is unsigned! */
state->special_flags = JSONSL_SPECIALf_UNSIGNED;
} else if (state->special_flags == JSONSL_SPECIALf_DASH) {
/* Still in dash! */
INVOKE_ERROR(INVALID_NUMBER);
} else if (state->special_flags & JSONSL_SPECIALf_NUMERIC) {
/* Check that we're not at the end of a token */
if (STATE_NUM_LAST != '1') {
INVOKE_ERROR(INVALID_NUMBER);
}
} else if (state->special_flags == JSONSL_SPECIALf_TRUE) {
if (STATE_SPECIAL_LENGTH != 4) {
INVOKE_ERROR(SPECIAL_INCOMPLETE);
}
state->nelem = 1;
} else if (state->special_flags == JSONSL_SPECIALf_FALSE) {
if (STATE_SPECIAL_LENGTH != 5) {
INVOKE_ERROR(SPECIAL_INCOMPLETE);
}
} else if (state->special_flags == JSONSL_SPECIALf_NULL) {
if (STATE_SPECIAL_LENGTH != 4) {
INVOKE_ERROR(SPECIAL_INCOMPLETE);
}
}
SPECIAL_POP;
jsn->expecting = ',';
if (is_allowed_whitespace(CUR_CHAR)) {
CONTINUE_NEXT_CHAR();
}
/**
* This works because we have a non-whitespace token
* which is not a special token. If this is a structural
* character then it will be gracefully handled by the
* switch statement. Otherwise it will default to the 'special'
* state again,
*/
goto GT_STRUCTURAL_TOKEN;
} else if (is_allowed_whitespace(CUR_CHAR)) {
INCR_METRIC(ALLOWED_WHITESPACE);
/* So we're not special. Harmless insignificant whitespace
* passthrough
*/
CONTINUE_NEXT_CHAR();
} else if (extract_special(CUR_CHAR)) {
/* not a string, whitespace, or structural token. must be special */
goto GT_SPECIAL_BEGIN;
}
INCR_GENERIC(CUR_CHAR);
if (CUR_CHAR == '"') {
GT_QUOTE:
jsn->can_insert = 0;
switch (state_type) {
/* the end of a string or hash key */
case JSONSL_T_STRING:
CALLBACK_AND_POP(STRING);
CONTINUE_NEXT_CHAR();
case JSONSL_T_HKEY:
CALLBACK_AND_POP(HKEY);
CONTINUE_NEXT_CHAR();
case JSONSL_T_OBJECT:
state->nelem++;
if ( (state->nelem-1) % 2 ) {
/* Odd, this must be a hash value */
if (jsn->tok_last != ':') {
INVOKE_ERROR(MISSING_TOKEN);
}
jsn->expecting = ','; /* Can't figure out what to expect next */
jsn->tok_last = 0;
STACK_PUSH;
state->type = JSONSL_T_STRING;
DO_CALLBACK(STRING, PUSH);
} else {
/* hash key */
if (jsn->expecting != '"') {
INVOKE_ERROR(STRAY_TOKEN);
}
jsn->tok_last = 0;
jsn->expecting = ':';
STACK_PUSH;
state->type = JSONSL_T_HKEY;
DO_CALLBACK(HKEY, PUSH);
}
CONTINUE_NEXT_CHAR();
case JSONSL_T_LIST:
state->nelem++;
STACK_PUSH;
state->type = JSONSL_T_STRING;
jsn->expecting = ',';
jsn->tok_last = 0;
DO_CALLBACK(STRING, PUSH);
CONTINUE_NEXT_CHAR();
case JSONSL_T_SPECIAL:
INVOKE_ERROR(STRAY_TOKEN);
break;
default:
INVOKE_ERROR(STRING_OUTSIDE_CONTAINER);
break;
} /* switch(state->type) */
} else if (CUR_CHAR == '\\') {
GT_ESCAPE:
INCR_METRIC(ESCAPES);
/* Escape */
if ( (state->type & JSONSL_Tf_STRINGY) == 0 ) {
INVOKE_ERROR(ESCAPE_OUTSIDE_STRING);
}
state->nescapes++;
jsn->in_escape = 1;
CONTINUE_NEXT_CHAR();
} /* " or \ */
GT_STRUCTURAL_TOKEN:
switch (CUR_CHAR) {
case ':':
INCR_METRIC(STRUCTURAL_TOKEN);
if (jsn->expecting != CUR_CHAR) {
INVOKE_ERROR(STRAY_TOKEN);
}
jsn->tok_last = ':';
jsn->can_insert = 1;
jsn->expecting = '"';
CONTINUE_NEXT_CHAR();
case ',':
INCR_METRIC(STRUCTURAL_TOKEN);
/**
* The comma is one of the more generic tokens.
* In the context of an OBJECT, the can_insert flag
* should never be set, and no other action is
* necessary.
*/
if (jsn->expecting != CUR_CHAR) {
/* make this branch execute only when we haven't manually
* just placed the ',' in the expecting register.
*/
INVOKE_ERROR(STRAY_TOKEN);
}
if (state->type == JSONSL_T_OBJECT) {
/* end of hash value, expect a string as a hash key */
jsn->expecting = '"';
} else {
jsn->can_insert = 1;
}
jsn->tok_last = ',';
jsn->expecting = '"';
CONTINUE_NEXT_CHAR();
/* new list or object */
/* hashes are more common */
case '{':
case '[':
INCR_METRIC(STRUCTURAL_TOKEN);
if (!jsn->can_insert) {
INVOKE_ERROR(CANT_INSERT);
}
ENSURE_HVAL;
state->nelem++;
STACK_PUSH;
/* because the constants match the opening delimiters, we can do this: */
state->type = CUR_CHAR;
state->nelem = 0;
jsn->can_insert = 1;
if (CUR_CHAR == '{') {
/* If we're a hash, we expect a key first, which is quouted */
jsn->expecting = '"';
}
if (CUR_CHAR == JSONSL_T_OBJECT) {
DO_CALLBACK(OBJECT, PUSH);
} else {
DO_CALLBACK(LIST, PUSH);
}
jsn->tok_last = 0;
CONTINUE_NEXT_CHAR();
/* closing of list or object */
case '}':
case ']':
INCR_METRIC(STRUCTURAL_TOKEN);
if (jsn->tok_last == ',' && jsn->options.allow_trailing_comma == 0) {
INVOKE_ERROR(TRAILING_COMMA);
}
jsn->can_insert = 0;
jsn->level--;
jsn->expecting = ',';
jsn->tok_last = 0;
if (CUR_CHAR == ']') {
if (state->type != '[') {
INVOKE_ERROR(BRACKET_MISMATCH);
}
DO_CALLBACK(LIST, POP);
} else {
if (state->type != '{') {
INVOKE_ERROR(BRACKET_MISMATCH);
} else if (state->nelem && state->nelem % 2 != 0) {
INVOKE_ERROR(VALUE_EXPECTED);
}
DO_CALLBACK(OBJECT, POP);
}
state = jsn->stack + jsn->level;
state->pos_cur = jsn->pos;
CONTINUE_NEXT_CHAR();
default:
GT_SPECIAL_BEGIN:
/**
* Not a string, not a structural token, and not benign whitespace.
* Technically we should iterate over the character always, but since
* we are not doing full numerical/value decoding anyway (but only hinting),
* we only check upon entry.
*/
if (state->type != JSONSL_T_SPECIAL) {
int special_flags = extract_special(CUR_CHAR);
if (!special_flags) {
/**
* Try to do some heuristics here anyway to figure out what kind of
* error this is. The 'special' case is a fallback scenario anyway.
*/
if (CUR_CHAR == '\0') {
INVOKE_ERROR(FOUND_NULL_BYTE);
} else if (CUR_CHAR < 0x20) {
INVOKE_ERROR(WEIRD_WHITESPACE);
} else {
INVOKE_ERROR(SPECIAL_EXPECTED);
}
}
ENSURE_HVAL;
state->nelem++;
if (!jsn->can_insert) {
INVOKE_ERROR(CANT_INSERT);
}
STACK_PUSH;
state->type = JSONSL_T_SPECIAL;
state->special_flags = special_flags;
STATE_SPECIAL_LENGTH = 1;
if (special_flags == JSONSL_SPECIALf_UNSIGNED) {
state->nelem = CUR_CHAR - 0x30;
STATE_NUM_LAST = '1';
} else {
STATE_NUM_LAST = '-';
state->nelem = 0;
}
DO_CALLBACK(SPECIAL, PUSH);
}
CONTINUE_NEXT_CHAR();
}
}
}
JSONSL_API
const char* jsonsl_strerror(jsonsl_error_t err)
{
if (err == JSONSL_ERROR_SUCCESS) {
return "SUCCESS";
}
#define X(t) \
if (err == JSONSL_ERROR_##t) \
return #t;
JSONSL_XERR;
#undef X
return "<UNKNOWN_ERROR>";
}
JSONSL_API
const char *jsonsl_strtype(jsonsl_type_t type)
{
#define X(o,c) \
if (type == JSONSL_T_##o) \
return #o;
JSONSL_XTYPE
#undef X
return "UNKNOWN TYPE";
}
/*
*
* JPR/JSONPointer functions
*
*
*/
#ifndef JSONSL_NO_JPR
static
jsonsl_jpr_type_t
populate_component(char *in,
struct jsonsl_jpr_component_st *component,
char **next,
jsonsl_error_t *errp)
{
unsigned long pctval;
char *c = NULL, *outp = NULL, *end = NULL;
size_t input_len;
jsonsl_jpr_type_t ret = JSONSL_PATH_NONE;
if (*next == NULL || *(*next) == '\0') {
return JSONSL_PATH_NONE;
}
/* Replace the next / with a NULL */
*next = strstr(in, "/");
if (*next != NULL) {
*(*next) = '\0'; /* drop the forward slash */
input_len = *next - in;
end = *next;
*next += 1; /* next character after the '/' */
} else {
input_len = strlen(in);
end = in + input_len + 1;
}
component->pstr = in;
/* Check for special components of interest */
if (*in == JSONSL_PATH_WILDCARD_CHAR && input_len == 1) {
/* Lone wildcard */
ret = JSONSL_PATH_WILDCARD;
goto GT_RET;
} else if (isdigit(*in)) {
/* ASCII Numeric */
char *endptr;
component->idx = strtoul(in, &endptr, 10);
if (endptr && *endptr == '\0') {
ret = JSONSL_PATH_NUMERIC;
goto GT_RET;
}
}
/* Default, it's a string */
ret = JSONSL_PATH_STRING;
for (c = outp = in; c < end; c++, outp++) {
char origc;
if (*c != '%') {
goto GT_ASSIGN;
}
/*
* c = { [+0] = '%', [+1] = 'b', [+2] = 'e', [+3] = '\0' }
*/
/* Need %XX */
if (c+2 >= end) {
*errp = JSONSL_ERROR_PERCENT_BADHEX;
return JSONSL_PATH_INVALID;
}
if (! (isxdigit(*(c+1)) && isxdigit(*(c+2))) ) {
*errp = JSONSL_ERROR_PERCENT_BADHEX;
return JSONSL_PATH_INVALID;
}
/* Temporarily null-terminate the characters */
origc = *(c+3);
*(c+3) = '\0';
pctval = strtoul(c+1, NULL, 16);
*(c+3) = origc;
*outp = (char) pctval;
c += 2;
continue;
GT_ASSIGN:
*outp = *c;
}
/* Null-terminate the string */
for (; outp < c; outp++) {
*outp = '\0';
}
GT_RET:
component->ptype = ret;
if (ret != JSONSL_PATH_WILDCARD) {
component->len = strlen(component->pstr);
}
return ret;
}
JSONSL_API
jsonsl_jpr_t
jsonsl_jpr_new(const char *path, jsonsl_error_t *errp)
{
char *my_copy = NULL;
int count, curidx;
struct jsonsl_jpr_st *ret = NULL;
struct jsonsl_jpr_component_st *components = NULL;
size_t origlen;
jsonsl_error_t errstacked;
#define JPR_BAIL(err) *errp = err; goto GT_ERROR;
if (errp == NULL) {
errp = &errstacked;
}
if (path == NULL || *path != '/') {
JPR_BAIL(JSONSL_ERROR_JPR_NOROOT);
return NULL;
}
count = 1;
path++;
{
const char *c = path;
for (; *c; c++) {
if (*c == '/') {
count++;
if (*(c+1) == '/') {
JPR_BAIL(JSONSL_ERROR_JPR_DUPSLASH);
}
}
}
}
if(*path) {
count++;
}
components = (struct jsonsl_jpr_component_st *)
malloc(sizeof(*components) * count);
if (!components) {
JPR_BAIL(JSONSL_ERROR_ENOMEM);
}
my_copy = (char *)malloc(strlen(path) + 1);
if (!my_copy) {
JPR_BAIL(JSONSL_ERROR_ENOMEM);
}
strcpy(my_copy, path);
components[0].ptype = JSONSL_PATH_ROOT;
if (*my_copy) {
char *cur = my_copy;
int pathret = JSONSL_PATH_STRING;
curidx = 1;
while (pathret > 0 && curidx < count) {
pathret = populate_component(cur, components + curidx, &cur, errp);
if (pathret > 0) {
curidx++;
} else {
break;
}
}
if (pathret == JSONSL_PATH_INVALID) {
JPR_BAIL(JSONSL_ERROR_JPR_BADPATH);
}
} else {
curidx = 1;
}
path--; /*revert path to leading '/' */
origlen = strlen(path) + 1;
ret = (struct jsonsl_jpr_st *)malloc(sizeof(*ret));
if (!ret) {
JPR_BAIL(JSONSL_ERROR_ENOMEM);
}
ret->orig = (char *)malloc(origlen);
if (!ret->orig) {
JPR_BAIL(JSONSL_ERROR_ENOMEM);
}
ret->components = components;
ret->ncomponents = curidx;
ret->basestr = my_copy;
ret->norig = origlen-1;
strcpy(ret->orig, path);
return ret;
GT_ERROR:
free(my_copy);
free(components);
if (ret) {
free(ret->orig);
}
free(ret);
return NULL;
#undef JPR_BAIL
}
void jsonsl_jpr_destroy(jsonsl_jpr_t jpr)
{
free(jpr->components);
free(jpr->basestr);
free(jpr->orig);
free(jpr);
}
/**
* Call when there is a possibility of a match, either as a final match or
* as a path within a match
* @param jpr The JPR path
* @param component Component corresponding to the current element
* @param prlevel The level of the *parent*
* @param chtype The type of the child
* @return Match status
*/
static jsonsl_jpr_match_t
jsonsl__match_continue(jsonsl_jpr_t jpr,
const struct jsonsl_jpr_component_st *component,
unsigned prlevel, unsigned chtype)
{
const struct jsonsl_jpr_component_st *next_comp = component + 1;
if (prlevel == jpr->ncomponents - 1) {
/* This is the match. Check the expected type of the match against
* the child */
if (jpr->match_type == 0 || jpr->match_type == chtype) {
return JSONSL_MATCH_COMPLETE;
} else {
return JSONSL_MATCH_TYPE_MISMATCH;
}
}
if (chtype == JSONSL_T_LIST) {
if (next_comp->ptype == JSONSL_PATH_NUMERIC) {
return JSONSL_MATCH_POSSIBLE;
} else {
return JSONSL_MATCH_TYPE_MISMATCH;
}
} else if (chtype == JSONSL_T_OBJECT) {
if (next_comp->ptype == JSONSL_PATH_NUMERIC) {
return JSONSL_MATCH_TYPE_MISMATCH;
} else {
return JSONSL_MATCH_POSSIBLE;
}
} else {
return JSONSL_MATCH_TYPE_MISMATCH;
}
}
JSONSL_API
jsonsl_jpr_match_t
jsonsl_path_match(jsonsl_jpr_t jpr,
const struct jsonsl_state_st *parent,
const struct jsonsl_state_st *child,
const char *key, size_t nkey)
{
const struct jsonsl_jpr_component_st *comp;
if (!parent) {
/* No parent. Return immediately since it's always a match */
return jsonsl__match_continue(jpr, jpr->components, 0, child->type);
}
comp = jpr->components + parent->level;
/* note that we don't need to verify the type of the match, this is
* always done through the previous call to jsonsl__match_continue.
* If we are in a POSSIBLE tree then we can be certain the types (at
* least at this level) are correct */
if (parent->type == JSONSL_T_OBJECT) {
if (comp->len != nkey || strncmp(key, comp->pstr, nkey) != 0) {
return JSONSL_MATCH_NOMATCH;
}
} else {
if (comp->idx != parent->nelem - 1) {
return JSONSL_MATCH_NOMATCH;
}
}
return jsonsl__match_continue(jpr, comp, parent->level, child->type);
}
JSONSL_API
jsonsl_jpr_match_t
jsonsl_jpr_match(jsonsl_jpr_t jpr,
unsigned int parent_type,
unsigned int parent_level,
const char *key,
size_t nkey)
{
/* find our current component. This is the child level */
int cmpret;
struct jsonsl_jpr_component_st *p_component;
p_component = jpr->components + parent_level;
if (parent_level >= jpr->ncomponents) {
return JSONSL_MATCH_NOMATCH;
}
/* Lone query for 'root' element. Always matches */
if (parent_level == 0) {
if (jpr->ncomponents == 1) {
return JSONSL_MATCH_COMPLETE;
} else {
return JSONSL_MATCH_POSSIBLE;
}
}
/* Wildcard, always matches */
if (p_component->ptype == JSONSL_PATH_WILDCARD) {
if (parent_level == jpr->ncomponents-1) {
return JSONSL_MATCH_COMPLETE;
} else {
return JSONSL_MATCH_POSSIBLE;
}
}
/* Check numeric array index. This gets its special block so we can avoid
* string comparisons */
if (p_component->ptype == JSONSL_PATH_NUMERIC) {
if (parent_type == JSONSL_T_LIST) {
if (p_component->idx != nkey) {
/* Wrong index */
return JSONSL_MATCH_NOMATCH;
} else {
if (parent_level == jpr->ncomponents-1) {
/* This is the last element of the path */
return JSONSL_MATCH_COMPLETE;
} else {
/* Intermediate element */
return JSONSL_MATCH_POSSIBLE;
}
}
} else if (p_component->is_arridx) {
/* Numeric and an array index (set explicitly by user). But not
* a list for a parent */
return JSONSL_MATCH_TYPE_MISMATCH;
}
} else if (parent_type == JSONSL_T_LIST) {
return JSONSL_MATCH_TYPE_MISMATCH;
}
/* Check lengths */
if (p_component->len != nkey) {
return JSONSL_MATCH_NOMATCH;
}
/* Check string comparison */
cmpret = strncmp(p_component->pstr, key, nkey);
if (cmpret == 0) {
if (parent_level == jpr->ncomponents-1) {
return JSONSL_MATCH_COMPLETE;
} else {
return JSONSL_MATCH_POSSIBLE;
}
}
return JSONSL_MATCH_NOMATCH;
}
JSONSL_API
void jsonsl_jpr_match_state_init(jsonsl_t jsn,
jsonsl_jpr_t *jprs,
size_t njprs)
{
size_t ii, *firstjmp;
if (njprs == 0) {
return;
}
jsn->jprs = (jsonsl_jpr_t *)malloc(sizeof(jsonsl_jpr_t) * njprs);
jsn->jpr_count = njprs;
jsn->jpr_root = (size_t*)calloc(1, sizeof(size_t) * njprs * jsn->levels_max);
memcpy(jsn->jprs, jprs, sizeof(jsonsl_jpr_t) * njprs);
/* Set the initial jump table values */
firstjmp = jsn->jpr_root;
for (ii = 0; ii < njprs; ii++) {
firstjmp[ii] = ii+1;
}
}
JSONSL_API
void jsonsl_jpr_match_state_cleanup(jsonsl_t jsn)
{
if (jsn->jpr_count == 0) {
return;
}
free(jsn->jpr_root);
free(jsn->jprs);
jsn->jprs = NULL;
jsn->jpr_root = NULL;
jsn->jpr_count = 0;
}
/**
* This function should be called exactly once on each element...
* This should also be called in recursive order, since we rely
* on the parent having been initalized for a match.
*
* Since the parent is checked for a match as well, we maintain a 'serial' counter.
* Whenever we traverse an element, we expect the serial to be the same as a global
* integer. If they do not match, we re-initialize the context, and set the serial.
*
* This ensures a type of consistency without having a proactive reset by the
* main lexer itself.
*
*/
JSONSL_API
jsonsl_jpr_t jsonsl_jpr_match_state(jsonsl_t jsn,
struct jsonsl_state_st *state,
const char *key,
size_t nkey,
jsonsl_jpr_match_t *out)
{
struct jsonsl_state_st *parent_state;
jsonsl_jpr_t ret = NULL;
/* Jump and JPR tables for our own state and the parent state */
size_t *jmptable, *pjmptable;
size_t jmp_cur, ii, ourjmpidx;
if (!jsn->jpr_root) {
*out = JSONSL_MATCH_NOMATCH;
return NULL;
}
pjmptable = jsn->jpr_root + (jsn->jpr_count * (state->level-1));
jmptable = pjmptable + jsn->jpr_count;
/* If the parent cannot match, then invalidate it */
if (*pjmptable == 0) {
*jmptable = 0;
*out = JSONSL_MATCH_NOMATCH;
return NULL;
}
parent_state = jsn->stack + state->level - 1;
if (parent_state->type == JSONSL_T_LIST) {
nkey = (size_t) parent_state->nelem;
}
*jmptable = 0;
ourjmpidx = 0;
memset(jmptable, 0, sizeof(int) * jsn->jpr_count);
for (ii = 0; ii < jsn->jpr_count; ii++) {
jmp_cur = pjmptable[ii];
if (jmp_cur) {
jsonsl_jpr_t jpr = jsn->jprs[jmp_cur-1];
*out = jsonsl_jpr_match(jpr,
parent_state->type,
parent_state->level,
key, nkey);
if (*out == JSONSL_MATCH_COMPLETE) {
ret = jpr;
*jmptable = 0;
return ret;
} else if (*out == JSONSL_MATCH_POSSIBLE) {
jmptable[ourjmpidx] = ii+1;
ourjmpidx++;
}
} else {
break;
}
}
if (!*jmptable) {
*out = JSONSL_MATCH_NOMATCH;
}
return NULL;
}
JSONSL_API
const char *jsonsl_strmatchtype(jsonsl_jpr_match_t match)
{
#define X(T,v) \
if ( match == JSONSL_MATCH_##T ) \
return #T;
JSONSL_XMATCH
#undef X
return "<UNKNOWN>";
}
#endif /* JSONSL_WITH_JPR */
static char *
jsonsl__writeutf8(uint32_t pt, char *out)
{
#define ADD_OUTPUT(c) *out = (char)(c); out++;
if (pt < 0x80) {
ADD_OUTPUT(pt);
} else if (pt < 0x800) {
ADD_OUTPUT((pt >> 6) | 0xC0);
ADD_OUTPUT((pt & 0x3F) | 0x80);
} else if (pt < 0x10000) {
ADD_OUTPUT((pt >> 12) | 0xE0);
ADD_OUTPUT(((pt >> 6) & 0x3F) | 0x80);
ADD_OUTPUT((pt & 0x3F) | 0x80);
} else {
ADD_OUTPUT((pt >> 18) | 0xF0);
ADD_OUTPUT(((pt >> 12) & 0x3F) | 0x80);
ADD_OUTPUT(((pt >> 6) & 0x3F) | 0x80);
ADD_OUTPUT((pt & 0x3F) | 0x80);
}
return out;
#undef ADD_OUTPUT
}
/* Thanks snej (https://github.com/mnunberg/jsonsl/issues/9) */
static int
jsonsl__digit2int(char ch) {
int d = ch - '0';
if ((unsigned) d < 10) {
return d;
}
d = ch - 'a';
if ((unsigned) d < 6) {
return d + 10;
}
d = ch - 'A';
if ((unsigned) d < 6) {
return d + 10;
}
return -1;
}
/* Assume 's' is at least 4 bytes long */
static int
jsonsl__get_uescape_16(const char *s)
{
int ret = 0;
int cur;
#define GET_DIGIT(off) \
cur = jsonsl__digit2int(s[off]); \
if (cur == -1) { return -1; } \
ret |= (cur << (12 - (off * 4)));
GET_DIGIT(0);
GET_DIGIT(1);
GET_DIGIT(2);
GET_DIGIT(3);
#undef GET_DIGIT
return ret;
}
/**
* Utility function to convert escape sequences
*/
JSONSL_API
size_t jsonsl_util_unescape_ex(const char *in,
char *out,
size_t len,
const int toEscape[128],
unsigned *oflags,
jsonsl_error_t *err,
const char **errat)
{
const unsigned char *c = (const unsigned char*)in;
char *begin_p = out;
unsigned oflags_s;
uint16_t last_codepoint = 0;
if (!oflags) {
oflags = &oflags_s;
}
*oflags = 0;
#define UNESCAPE_BAIL(e,offset) \
*err = JSONSL_ERROR_##e; \
if (errat) { \
*errat = (const char*)(c+ (ptrdiff_t)(offset)); \
} \
return 0;
for (; len; len--, c++, out++) {
int uescval;
if (*c != '\\') {
/* Not an escape, so we don't care about this */
goto GT_ASSIGN;
}
if (len < 2) {
UNESCAPE_BAIL(ESCAPE_INVALID, 0);
}
if (!is_allowed_escape(c[1])) {
UNESCAPE_BAIL(ESCAPE_INVALID, 1)
}
if ((toEscape && toEscape[(unsigned char)c[1] & 0x7f] == 0 &&
c[1] != '\\' && c[1] != '"')) {
/* if we don't want to unescape this string, write the escape sequence to the output */
*out++ = *c++;
if (--len == 0)
break;
goto GT_ASSIGN;
}
if (c[1] != 'u') {
/* simple skip-and-replace using pre-defined maps.
* TODO: should the maps actually reflect the desired
* replacement character in toEscape?
*/
char esctmp = get_escape_equiv(c[1]);
if (esctmp) {
/* Check if there is a corresponding replacement */
*out = esctmp;
} else {
/* Just gobble up the 'reverse-solidus' */
*out = c[1];
}
len--;
c++;
/* do not assign, just continue */
continue;
}
/* next == 'u' */
if (len < 6) {
/* Need at least six characters.. */
UNESCAPE_BAIL(UESCAPE_TOOSHORT, 2);
}
uescval = jsonsl__get_uescape_16((const char *)c + 2);
if (uescval == -1) {
UNESCAPE_BAIL(PERCENT_BADHEX, -1);
} else if (uescval == 0) {
UNESCAPE_BAIL(INVALID_CODEPOINT, 2);
}
if (last_codepoint) {
uint16_t w1 = last_codepoint, w2 = (uint16_t)uescval;
uint32_t cp;
if (uescval < 0xDC00 || uescval > 0xDFFF) {
UNESCAPE_BAIL(INVALID_CODEPOINT, -1);
}
cp = (w1 & 0x3FF) << 10;
cp |= (w2 & 0x3FF);
cp += 0x10000;
out = jsonsl__writeutf8(cp, out) - 1;
last_codepoint = 0;
} else if (uescval < 0xD800 || uescval > 0xDFFF) {
*oflags |= JSONSL_SPECIALf_NONASCII;
out = jsonsl__writeutf8(uescval, out) - 1;
} else if (uescval > 0xD7FF && uescval < 0xDC00) {
*oflags |= JSONSL_SPECIALf_NONASCII;
last_codepoint = (uint16_t)uescval;
out--;
} else {
UNESCAPE_BAIL(INVALID_CODEPOINT, 2);
}
/* Post uescape cleanup */
len -= 5; /* Gobble up 5 chars after 'u' */
c += 5;
continue;
/* Only reached by previous branches */
GT_ASSIGN:
*out = *c;
}
if (last_codepoint) {
*err = JSONSL_ERROR_INVALID_CODEPOINT;
return 0;
}
*err = JSONSL_ERROR_SUCCESS;
return out - begin_p;
}
/**
* Character Table definitions.
* These were all generated via srcutil/genchartables.pl
*/
/**
* This table contains the beginnings of non-string
* allowable (bareword) values.
*/
static const unsigned short Special_Table[0x80] = {
/* 0x00 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x1f */
/* 0x20 */ 0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x2c */
/* 0x2d */ JSONSL_SPECIALf_DASH /* <-> */, /* 0x2d */
/* 0x2e */ 0,0, /* 0x2f */
/* 0x30 */ JSONSL_SPECIALf_ZERO /* <0> */, /* 0x30 */
/* 0x31 */ JSONSL_SPECIALf_UNSIGNED /* <1> */, /* 0x31 */
/* 0x32 */ JSONSL_SPECIALf_UNSIGNED /* <2> */, /* 0x32 */
/* 0x33 */ JSONSL_SPECIALf_UNSIGNED /* <3> */, /* 0x33 */
/* 0x34 */ JSONSL_SPECIALf_UNSIGNED /* <4> */, /* 0x34 */
/* 0x35 */ JSONSL_SPECIALf_UNSIGNED /* <5> */, /* 0x35 */
/* 0x36 */ JSONSL_SPECIALf_UNSIGNED /* <6> */, /* 0x36 */
/* 0x37 */ JSONSL_SPECIALf_UNSIGNED /* <7> */, /* 0x37 */
/* 0x38 */ JSONSL_SPECIALf_UNSIGNED /* <8> */, /* 0x38 */
/* 0x39 */ JSONSL_SPECIALf_UNSIGNED /* <9> */, /* 0x39 */
/* 0x3a */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x59 */
/* 0x5a */ 0,0,0,0,0,0,0,0,0,0,0,0, /* 0x65 */
/* 0x66 */ JSONSL_SPECIALf_FALSE /* <f> */, /* 0x66 */
/* 0x67 */ 0,0,0,0,0,0,0, /* 0x6d */
/* 0x6e */ JSONSL_SPECIALf_NULL /* <n> */, /* 0x6e */
/* 0x6f */ 0,0,0,0,0, /* 0x73 */
/* 0x74 */ JSONSL_SPECIALf_TRUE /* <t> */ /* 0x74 */
};
// Bit tables are order such that the MSB is bit 0.
//
/**
* Contains characters which signal the termination of any of the 'special' bareword
* values.
*/
static const char Special_Endings[0x100] = {
/* 0x00 */ 0,0,0,0,0,0,0,0,0, /* 0x08 */
/* 0x09 */ 1 /* <TAB> */, /* 0x09 */
/* 0x0a */ 1 /* <LF> */, /* 0x0a */
/* 0x0b */ 0,0, /* 0x0c */
/* 0x0d */ 1 /* <CR> */, /* 0x0d */
/* 0x0e */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x1f */
/* 0x20 */ 1 /* <SP> */, /* 0x20 */
/* 0x21 */ 0, /* 0x21 */
/* 0x22 */ 1 /* " */, /* 0x22 */
/* 0x23 */ 0,0,0,0,0,0,0,0,0, /* 0x2b */
/* 0x2c */ 1 /* , */, /* 0x2c */
/* 0x2d */ 0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x39 */
/* 0x3a */ 1 /* : */, /* 0x3a */
/* 0x3b */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x5a */
/* 0x5b */ 1 /* [ */, /* 0x5b */
/* 0x5c */ 1 /* \ */, /* 0x5c */
/* 0x5d */ 1 /* ] */, /* 0x5d */
/* 0x5e */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x7a */
/* 0x7b */ 1 /* { */, /* 0x7b */
/* 0x7c */ 0, /* 0x7c */
/* 0x7d */ 1 /* } */, /* 0x7d */
/* 0x7e */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x9d */
/* 0x9e */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xbd */
/* 0xbe */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xdd */
/* 0xde */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xfd */
/* 0xfe */ 0 /* 0xfe */
};
static const uint32_t Special_Endings_bits[0x80 / 32] = {
0b00000000110010000000000000000000,
0b10100000000010000000000000100000,
0b00000000000000000000000000011100,
0b00000000000000000000000000010100
};
/**
* This table contains entries for the allowed whitespace as per RFC 4627
*/
static const char Allowed_Whitespace[0x100] = {
/* 0x00 */ 0,0,0,0,0,0,0,0,0, /* 0x08 */
/* 0x09 */ 1 /* <TAB> */, /* 0x09 */
/* 0x0a */ 1 /* <LF> */, /* 0x0a */
/* 0x0b */ 0,0, /* 0x0c */
/* 0x0d */ 1 /* <CR> */, /* 0x0d */
/* 0x0e */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x1f */
/* 0x20 */ 1 /* <SP> */, /* 0x20 */
/* 0x21 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x40 */
/* 0x41 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x60 */
/* 0x61 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x80 */
/* 0x81 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xa0 */
/* 0xa1 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xc0 */
/* 0xc1 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xe0 */
/* 0xe1 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* 0xfe */
};
static const uint32_t Allowed_Whitespace_bits = 0b00000000011001000000000000000000;
static const char String_No_Passthrough[0x100] = {
/* 0x00 */ 1 /* <NUL> */, /* 0x00 */
/* 0x01 */ 1 /* <SOH> */, /* 0x01 */
/* 0x02 */ 1 /* <STX> */, /* 0x02 */
/* 0x03 */ 1 /* <ETX> */, /* 0x03 */
/* 0x04 */ 1 /* <EOT> */, /* 0x04 */
/* 0x05 */ 1 /* <ENQ> */, /* 0x05 */
/* 0x06 */ 1 /* <ACK> */, /* 0x06 */
/* 0x07 */ 1 /* <BEL> */, /* 0x07 */
/* 0x08 */ 1 /* <BS> */, /* 0x08 */
/* 0x09 */ 1 /* <HT> */, /* 0x09 */
/* 0x0a */ 1 /* <LF> */, /* 0x0a */
/* 0x0b */ 1 /* <VT> */, /* 0x0b */
/* 0x0c */ 1 /* <FF> */, /* 0x0c */
/* 0x0d */ 1 /* <CR> */, /* 0x0d */
/* 0x0e */ 1 /* <SO> */, /* 0x0e */
/* 0x0f */ 1 /* <SI> */, /* 0x0f */
/* 0x10 */ 1 /* <DLE> */, /* 0x10 */
/* 0x11 */ 1 /* <DC1> */, /* 0x11 */
/* 0x12 */ 1 /* <DC2> */, /* 0x12 */
/* 0x13 */ 1 /* <DC3> */, /* 0x13 */
/* 0x14 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x21 */
/* 0x22 */ 1 /* <"> */, /* 0x22 */
/* 0x23 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x42 */
/* 0x43 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x5b */
/* 0x5c */ 1 /* <\> */, /* 0x5c */
/* 0x5d */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x7c */
/* 0x7d */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x9c */
/* 0x9d */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xbc */
/* 0xbd */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xdc */
/* 0xdd */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xfc */
/* 0xfd */ 0,0, /* 0xfe */
};
/**
* Allowable two-character 'common' escapes:
*/
static const char Allowed_Escapes[0x100] = {
/* 0x00 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x1f */
/* 0x20 */ 0,0, /* 0x21 */
/* 0x22 */ 1 /* <"> */, /* 0x22 */
/* 0x23 */ 0,0,0,0,0,0,0,0,0,0,0,0, /* 0x2e */
/* 0x2f */ 1 /* </> */, /* 0x2f */
/* 0x30 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x4f */
/* 0x50 */ 0,0,0,0,0,0,0,0,0,0,0,0, /* 0x5b */
/* 0x5c */ 1 /* <\> */, /* 0x5c */
/* 0x5d */ 0,0,0,0,0, /* 0x61 */
/* 0x62 */ 1 /* <b> */, /* 0x62 */
/* 0x63 */ 0,0,0, /* 0x65 */
/* 0x66 */ 1 /* <f> */, /* 0x66 */
/* 0x67 */ 0,0,0,0,0,0,0, /* 0x6d */
/* 0x6e */ 1 /* <n> */, /* 0x6e */
/* 0x6f */ 0,0,0, /* 0x71 */
/* 0x72 */ 1 /* <r> */, /* 0x72 */
/* 0x73 */ 0, /* 0x73 */
/* 0x74 */ 1 /* <t> */, /* 0x74 */
/* 0x75 */ 1 /* <u> */, /* 0x75 */
/* 0x76 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x95 */
/* 0x96 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xb5 */
/* 0xb6 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xd5 */
/* 0xd6 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xf5 */
/* 0xf6 */ 0,0,0,0,0,0,0,0,0, /* 0xfe */
};
static const uint32_t Allowed_Escapes_bits[0x80 / 32] = {
0b00000000000000000000000000000000,
0b00100000000000010000000000000000,
0b00000000000000000000000000001000,
0b00100010000000100010110000000000
};
/**
* This table contains the _values_ for a given (single) escaped character.
*/
static unsigned char Escape_Equivs[0x100] = {
/* 0x00 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x1f */
/* 0x20 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x3f */
/* 0x40 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x5f */
/* 0x60 */ 0,0, /* 0x61 */
/* 0x62 */ 8 /* <b> */, /* 0x62 */
/* 0x63 */ 0,0,0, /* 0x65 */
/* 0x66 */ 12 /* <f> */, /* 0x66 */
/* 0x67 */ 0,0,0,0,0,0,0, /* 0x6d */
/* 0x6e */ 10 /* <n> */, /* 0x6e */
/* 0x6f */ 0,0,0, /* 0x71 */
/* 0x72 */ 13 /* <r> */, /* 0x72 */
/* 0x73 */ 0, /* 0x73 */
/* 0x74 */ 9 /* <t> */, /* 0x74 */
/* 0x75 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x94 */
/* 0x95 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xb4 */
/* 0xb5 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xd4 */
/* 0xd5 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xf4 */
/* 0xf5 */ 0,0,0,0,0,0,0,0,0,0 /* 0xfe */
};
/* Definitions of above-declared static functions */
static char get_escape_equiv(unsigned c) {
switch(c) {
case 'b':
return '\b';
case 'n':
return '\n';
case 'r':
return '\r';
case 't':
return '\t';
case 'f':
return '\f';
}
return 0;
}
static unsigned extract_special(unsigned c) {
return (c < 0x80) ? Special_Table[c & 0xff] : 0;
}
static int is_special_end(unsigned c) {
return (c < 0x80) && (Special_Endings_bits[c >> 5] & (1 << (31 - (c & 31))));
}
static int is_allowed_whitespace(unsigned c) {
return c == ' ' || (c < 0x20 && (Allowed_Whitespace_bits & (1 << (31 - c))));
}
static int is_allowed_escape(unsigned c) {
return (c < 0x80) && (Allowed_Escapes_bits[c >> 5] & (1 << (31 - (c & 31))));
}
static int is_simple_char(unsigned c) {
return !(c < 0x14 || c == '"' || c == '\\');
}
/* Clean up all our macros! */
#undef INCR_METRIC
#undef INCR_GENERIC
#undef INCR_STRINGY_CATCH
#undef CASE_DIGITS
#undef INVOKE_ERROR
#undef STACK_PUSH
#undef STACK_POP_NOPOS
#undef STACK_POP
#undef CALLBACK_AND_POP_NOPOS
#undef CALLBACK_AND_POP
#undef SPECIAL_POP
#undef CUR_CHAR
#undef DO_CALLBACK
#undef ENSURE_HVAL
#undef VERIFY_SPECIAL
#undef STATE_SPECIAL_LENGTH
#undef IS_NORMAL_NUMBER
#undef STATE_NUM_LAST
#undef FASTPARSE_EXHAUSTED
#undef FASTPARSE_BREAK
/**
* JSON Simple/Stacked/Stateful Lexer.
* - Does not buffer data
* - Maintains state
* - Callback oriented
* - Lightweight and fast. One source file and one header file
*
* Copyright (C) 2012-2015 Mark Nunberg
* See included LICENSE file for license details.
*/
#ifndef JSONSL_H_
#define JSONSL_H_
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <sys/types.h>
#include <wchar.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#ifdef JSONSL_USE_WCHAR
typedef jsonsl_char_t wchar_t;
typedef jsonsl_uchar_t unsigned wchar_t;
#else
typedef char jsonsl_char_t;
typedef unsigned char jsonsl_uchar_t;
#endif /* JSONSL_USE_WCHAR */
/* Stolen from http-parser.h, and possibly others */
#if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER<1600)
typedef __int8 int8_t;
typedef unsigned __int8 uint8_t;
typedef __int16 int16_t;
typedef unsigned __int16 uint16_t;
typedef __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
#if !defined(_MSC_VER) || _MSC_VER<1400
typedef unsigned int size_t;
typedef int ssize_t;
#endif
#else
#include <stdint.h>
#endif
#if (!defined(JSONSL_STATE_GENERIC)) && (!defined(JSONSL_STATE_USER_FIELDS))
#define JSONSL_STATE_GENERIC
#endif /* !defined JSONSL_STATE_GENERIC */
#ifdef JSONSL_STATE_GENERIC
#define JSONSL_STATE_USER_FIELDS
#endif /* JSONSL_STATE_GENERIC */
/* Additional fields for component object */
#ifndef JSONSL_JPR_COMPONENT_USER_FIELDS
#define JSONSL_JPR_COMPONENT_USER_FIELDS
#endif
#ifndef JSONSL_API
/**
* We require a /DJSONSL_DLL so that users already using this as a static
* or embedded library don't get confused
*/
#if defined(_WIN32) && defined(JSONSL_DLL)
#define JSONSL_API __declspec(dllexport)
#else
#define JSONSL_API
#endif /* _WIN32 */
#endif /* !JSONSL_API */
#ifndef JSONSL_INLINE
#if defined(_MSC_VER)
#define JSONSL_INLINE __inline
#elif defined(__GNUC__)
#define JSONSL_INLINE __inline__
#else
#define JSONSL_INLINE inline
#endif /* _MSC_VER or __GNUC__ */
#endif /* JSONSL_INLINE */
#define JSONSL_MAX_LEVELS 512
struct jsonsl_st;
typedef struct jsonsl_st *jsonsl_t;
typedef struct jsonsl_jpr_st* jsonsl_jpr_t;
/**
* This flag is true when AND'd against a type whose value
* must be in "quoutes" i.e. T_HKEY and T_STRING
*/
#define JSONSL_Tf_STRINGY 0xffff00
/**
* Constant representing the special JSON types.
* The values are special and aid in speed (the OBJECT and LIST
* values are the char literals of their openings).
*
* Their actual value is a character which attempts to resemble
* some mnemonic reference to the actual type.
*
* If new types are added, they must fit into the ASCII printable
* range (so they should be AND'd with 0x7f and yield something
* meaningful)
*/
#define JSONSL_XTYPE \
X(STRING, '"'|JSONSL_Tf_STRINGY) \
X(HKEY, '#'|JSONSL_Tf_STRINGY) \
X(OBJECT, '{') \
X(LIST, '[') \
X(SPECIAL, '^') \
X(UESCAPE, 'u')
typedef enum {
#define X(o, c) \
JSONSL_T_##o = c,
JSONSL_XTYPE
JSONSL_T_UNKNOWN = '?',
/* Abstract 'root' object */
JSONSL_T_ROOT = 0
#undef X
} jsonsl_type_t;
/**
* Subtypes for T_SPECIAL. We define them as flags
* because more than one type can be applied to a
* given object.
*/
#define JSONSL_XSPECIAL \
X(NONE, 0) \
X(SIGNED, 1<<0) \
X(UNSIGNED, 1<<1) \
X(TRUE, 1<<2) \
X(FALSE, 1<<3) \
X(NULL, 1<<4) \
X(FLOAT, 1<<5) \
X(EXPONENT, 1<<6) \
X(NONASCII, 1<<7)
typedef enum {
#define X(o,b) \
JSONSL_SPECIALf_##o = b,
JSONSL_XSPECIAL
#undef X
/* Handy flags for checking */
JSONSL_SPECIALf_UNKNOWN = 1 << 8,
/** @private Private */
JSONSL_SPECIALf_ZERO = 1 << 9 | JSONSL_SPECIALf_UNSIGNED,
/** @private */
JSONSL_SPECIALf_DASH = 1 << 10,
/** Type is numeric */
JSONSL_SPECIALf_NUMERIC = (JSONSL_SPECIALf_SIGNED| JSONSL_SPECIALf_UNSIGNED),
/** Type is a boolean */
JSONSL_SPECIALf_BOOLEAN = (JSONSL_SPECIALf_TRUE|JSONSL_SPECIALf_FALSE),
/** Type is an "extended", not integral type (but numeric) */
JSONSL_SPECIALf_NUMNOINT = (JSONSL_SPECIALf_FLOAT|JSONSL_SPECIALf_EXPONENT)
} jsonsl_special_t;
/**
* These are the various types of stack (or other) events
* which will trigger a callback.
* Like the type constants, this are also mnemonic
*/
#define JSONSL_XACTION \
X(PUSH, '+') \
X(POP, '-') \
X(UESCAPE, 'U') \
X(ERROR, '!')
typedef enum {
#define X(a,c) \
JSONSL_ACTION_##a = c,
JSONSL_XACTION
JSONSL_ACTION_UNKNOWN = '?'
#undef X
} jsonsl_action_t;
/**
* Various errors which may be thrown while parsing JSON
*/
#define JSONSL_XERR \
/* Trailing garbage characters */ \
X(GARBAGE_TRAILING) \
/* We were expecting a 'special' (numeric, true, false, null) */ \
X(SPECIAL_EXPECTED) \
/* The 'special' value was incomplete */ \
X(SPECIAL_INCOMPLETE) \
/* Found a stray token */ \
X(STRAY_TOKEN) \
/* We were expecting a token before this one */ \
X(MISSING_TOKEN) \
/* Cannot insert because the container is not ready */ \
X(CANT_INSERT) \
/* Found a '\' outside a string */ \
X(ESCAPE_OUTSIDE_STRING) \
/* Found a ':' outside of a hash */ \
X(KEY_OUTSIDE_OBJECT) \
/* found a string outside of a container */ \
X(STRING_OUTSIDE_CONTAINER) \
/* Found a null byte in middle of string */ \
X(FOUND_NULL_BYTE) \
/* Current level exceeds limit specified in constructor */ \
X(LEVELS_EXCEEDED) \
/* Got a } as a result of an opening [ or vice versa */ \
X(BRACKET_MISMATCH) \
/* We expected a key, but got something else instead */ \
X(HKEY_EXPECTED) \
/* We got an illegal control character (bad whitespace or something) */ \
X(WEIRD_WHITESPACE) \
/* Found a \u-escape, but there were less than 4 following hex digits */ \
X(UESCAPE_TOOSHORT) \
/* Invalid two-character escape */ \
X(ESCAPE_INVALID) \
/* Trailing comma */ \
X(TRAILING_COMMA) \
/* An invalid number was passed in a numeric field */ \
X(INVALID_NUMBER) \
/* Value is missing for object */ \
X(VALUE_EXPECTED) \
/* The following are for JPR Stuff */ \
\
/* Found a literal '%' but it was only followed by a single valid hex digit */ \
X(PERCENT_BADHEX) \
/* jsonpointer URI is malformed '/' */ \
X(JPR_BADPATH) \
/* Duplicate slash */ \
X(JPR_DUPSLASH) \
/* No leading root */ \
X(JPR_NOROOT) \
/* Allocation failure */ \
X(ENOMEM) \
/* Invalid unicode codepoint detected (in case of escapes) */ \
X(INVALID_CODEPOINT)
typedef enum {
JSONSL_ERROR_SUCCESS = 0,
#define X(e) \
JSONSL_ERROR_##e,
JSONSL_XERR
#undef X
JSONSL_ERROR_GENERIC
} jsonsl_error_t;
/**
* A state is a single level of the stack.
* Non-private data (i.e. the 'data' field, see the STATE_GENERIC section)
* will remain in tact until the item is popped.
*
* As a result, it means a parent state object may be accessed from a child
* object, (the parents fields will all be valid). This allows a user to create
* an ad-hoc hierarchy on top of the JSON one.
*
*/
struct jsonsl_state_st {
/**
* The JSON object type
*/
unsigned int type;
/**
* The position (in terms of number of bytes since the first call to
* jsonsl_feed()) at which the state was first pushed. This includes
* opening tokens, if applicable.
*
* @note For strings (i.e. type & JSONSL_Tf_STRINGY is nonzero) this will
* be the position of the first quote.
*
* @see jsonsl_st::pos which contains the _current_ position and can be
* used during a POP callback to get the length of the element.
*/
size_t pos_begin;
/**FIXME: This is redundant as the same information can be derived from
* jsonsl_st::pos at pop-time */
size_t pos_cur;
/** If this element is special, then its extended type is here */
unsigned short special_flags;
/**
* Level of recursion into nesting. This is mainly a convenience
* variable, as this can technically be deduced from the lexer's
* level parameter (though the logic is not that simple)
*/
unsigned short level;
/**
* how many elements in the object/list.
* For objects (hashes), an element is either
* a key or a value. Thus for one complete pair,
* nelem will be 2.
*
* For special types, this will hold the sum of the digits.
* This only holds true for values which are simple signed/unsigned
* numbers. Otherwise a special flag is set, and extra handling is not
* performed.
*/
uint32_t nelem;
/*TODO: merge this and special_flags into a union */
/**
* Useful for an opening nest, this will prevent a callback from being
* invoked on this item or any of its children
*/
int ignore_callback : 1;
/**
* Counter which is incremented each time an escape ('\') is encountered.
* This is used internally for non-string types and should only be
* inspected by the user if the state actually represents a string
* type.
*/
unsigned int nescapes : 31;
/**
* Put anything you want here. if JSONSL_STATE_USER_FIELDS is here, then
* the macro expansion happens here.
*
* You can use these fields to store hierarchical or 'tagging' information
* for specific objects.
*
* See the documentation above for the lifetime of the state object (i.e.
* if the private data points to allocated memory, it should be freed
* when the object is popped, as the state object will be re-used)
*/
#ifndef JSONSL_STATE_GENERIC
JSONSL_STATE_USER_FIELDS
#else
/**
* Otherwise, this is a simple void * pointer for anything you want
*/
void *data;
#endif /* JSONSL_STATE_USER_FIELDS */
};
/**Gets the number of elements in the list.
* @param st The state. Must be of type JSONSL_T_LIST
* @return number of elements in the list
*/
#define JSONSL_LIST_SIZE(st) ((st)->nelem)
/**Gets the number of key-value pairs in an object
* @param st The state. Must be of type JSONSL_T_OBJECT
* @return the number of key-value pairs in the object
*/
#define JSONSL_OBJECT_SIZE(st) ((st)->nelem / 2)
/**Gets the numeric value.
* @param st The state. Must be of type JSONSL_T_SPECIAL and
* special_flags must have the JSONSL_SPECIALf_NUMERIC flag
* set.
* @return the numeric value of the state.
*/
#define JSONSL_NUMERIC_VALUE(st) ((st)->nelem)
/*
* So now we need some special structure for keeping the
* JPR info in sync. Preferrably all in a single block
* of memory (there's no need for separate allocations.
* So we will define a 'table' with the following layout
*
* Level nPosbl JPR1_last JPR2_last JPR3_last
*
* 0 1 NOMATCH POSSIBLE POSSIBLE
* 1 0 NOMATCH NOMATCH COMPLETE
* [ table ends here because no further path is possible]
*
* Where the JPR..n corresponds to the number of JPRs
* requested, and nPosble is a quick flag to determine
*
* the number of possibilities. In the future this might
* be made into a proper 'jump' table,
*
* Since we always mark JPRs from the higher levels descending
* into the lower ones, a prospective child match would first
* look at the parent table to check the possibilities, and then
* see which ones were possible..
*
* Thus, the size of this blob would be (and these are all ints here)
* nLevels * nJPR * 2.
*
* the 'Width' of the table would be nJPR*2, and the 'height' would be
* nlevels
*/
/**
* This is called when a stack change ocurs.
*
* @param jsn The lexer
* @param action The type of action, this can be PUSH or POP
* @param state A pointer to the stack currently affected by the action
* @param at A pointer to the position of the input buffer which triggered
* this action.
*/
typedef void (*jsonsl_stack_callback)(
jsonsl_t jsn,
jsonsl_action_t action,
struct jsonsl_state_st* state,
const jsonsl_char_t *at);
/**
* This is called when an error is encountered.
* Sometimes it's possible to 'erase' characters (by replacing them
* with whitespace). If you think you have corrected the error, you
* can return a true value, in which case the parser will backtrack
* and try again.
*
* @param jsn The lexer
* @param error The error which was thrown
* @param state the current state
* @param a pointer to the position of the input buffer which triggered
* the error. Note that this is not const, this is because you have the
* possibility of modifying the character in an attempt to correct the
* error
*
* @return zero to bail, nonzero to try again (this only makes sense if
* the input buffer has been modified by this callback)
*/
typedef int (*jsonsl_error_callback)(
jsonsl_t jsn,
jsonsl_error_t error,
struct jsonsl_state_st* state,
jsonsl_char_t *at);
struct jsonsl_st {
/** Public, read-only */
/** This is the current level of the stack */
unsigned int level;
/** Flag set to indicate we should stop processing */
unsigned int stopfl;
/**
* This is the current position, relative to the beginning
* of the stream.
*/
size_t pos;
/** This is the 'bytes' variable passed to feed() */
const jsonsl_char_t *base;
/** Callback invoked for PUSH actions */
jsonsl_stack_callback action_callback_PUSH;
/** Callback invoked for POP actions */
jsonsl_stack_callback action_callback_POP;
/** Default callback for any action, if neither PUSH or POP callbacks are defined */
jsonsl_stack_callback action_callback;
/**
* Do not invoke callbacks for objects deeper than this level.
* NOTE: This field establishes the lower bound for ignored callbacks,
* and is thus misnamed. `min_ignore_level` would actually make more
* sense, but we don't want to break API.
*/
unsigned int max_callback_level;
/** The error callback. Invoked when an error happens. Should not be NULL */
jsonsl_error_callback error_callback;
/* these are boolean flags you can modify. You will be called
* about notification for each of these types if the corresponding
* variable is true.
*/
/**
* @name Callback Booleans.
* These determine whether a callback is to be invoked for certain types of objects
* @{*/
/** Boolean flag to enable or disable the invokcation for events on this type*/
int call_SPECIAL;
int call_OBJECT;
int call_LIST;
int call_STRING;
int call_HKEY;
/*@}*/
/**
* @name u-Escape handling
* Special handling for the \\u-f00d type sequences. These are meant
* to be translated back into the corresponding octet(s).
* A special callback (if set) is invoked with *at=='u'. An application
* may wish to temporarily suspend parsing and handle the 'u-' sequence
* internally (or not).
*/
/*@{*/
/** Callback to be invoked for a u-escape */
jsonsl_stack_callback action_callback_UESCAPE;
/** Boolean flag, whether to invoke the callback */
int call_UESCAPE;
/** Boolean flag, whether we should return after encountering a u-escape:
* the callback is invoked and then we return if this is true
*/
int return_UESCAPE;
/*@}*/
struct {
int allow_trailing_comma;
} options;
/** Put anything here */
void *data;
/*@{*/
/** Private */
int in_escape;
char expecting;
char tok_last;
int can_insert;
unsigned int levels_max;
#ifndef JSONSL_NO_JPR
size_t jpr_count;
jsonsl_jpr_t *jprs;
/* Root pointer for JPR matching information */
size_t *jpr_root;
#endif /* JSONSL_NO_JPR */
/*@}*/
/**
* This is the stack. Its upper bound is levels_max, or the
* nlevels argument passed to jsonsl_new. If you modify this structure,
* make sure that this member is last.
*/
struct jsonsl_state_st stack[1];
};
/**
* Creates a new lexer object, with capacity for recursion up to nlevels
*
* @param nlevels maximum recursion depth
*/
JSONSL_API
jsonsl_t jsonsl_new(int nlevels);
JSONSL_API
jsonsl_t jsonsl_init(jsonsl_t jsn, int nlevels);
JSONSL_API
size_t jsonsl_get_size(int nlevels);
/**
* Feeds data into the lexer.
*
* @param jsn the lexer object
* @param bytes new data to be fed
* @param nbytes size of new data
*/
JSONSL_API
void jsonsl_feed(jsonsl_t jsn, const jsonsl_char_t *bytes, size_t nbytes);
/**
* Resets the internal parser state. This does not free the parser
* but does clean it internally, so that the next time feed() is called,
* it will be treated as a new stream
*
* @param jsn the lexer
*/
JSONSL_API
void jsonsl_reset(jsonsl_t jsn);
/**
* Frees the lexer, cleaning any allocated memory taken
*
* @param jsn the lexer
*/
JSONSL_API
void jsonsl_destroy(jsonsl_t jsn);
/**
* Gets the 'parent' element, given the current one
*
* @param jsn the lexer
* @param cur the current nest, which should be a struct jsonsl_nest_st
*/
static JSONSL_INLINE
struct jsonsl_state_st *jsonsl_last_state(const jsonsl_t jsn,
const struct jsonsl_state_st *state)
{
/* Don't complain about overriding array bounds */
if (state->level > 1) {
return jsn->stack + state->level - 1;
} else {
return NULL;
}
}
/**
* Gets the state of the last fully consumed child of this parent. This is
* only valid in the parent's POP callback.
*
* @param the lexer
* @return A pointer to the child.
*/
static JSONSL_INLINE
struct jsonsl_state_st *jsonsl_last_child(const jsonsl_t jsn,
const struct jsonsl_state_st *parent)
{
return jsn->stack + (parent->level + 1);
}
/**Call to instruct the parser to stop parsing and return. This is valid
* only from within a callback */
static JSONSL_INLINE
void jsonsl_stop(jsonsl_t jsn)
{
jsn->stopfl = 1;
}
/**
* This enables receiving callbacks on all events. Doesn't do
* anything special but helps avoid some boilerplate.
* This does not touch the UESCAPE callbacks or flags.
*/
static JSONSL_INLINE
void jsonsl_enable_all_callbacks(jsonsl_t jsn)
{
jsn->call_HKEY = 1;
jsn->call_STRING = 1;
jsn->call_OBJECT = 1;
jsn->call_SPECIAL = 1;
jsn->call_LIST = 1;
}
/**
* A macro which returns true if the current state object can
* have children. This means a list type or an object type.
*/
#define JSONSL_STATE_IS_CONTAINER(state) \
(state->type == JSONSL_T_OBJECT || state->type == JSONSL_T_LIST)
/**
* These two functions, dump a string representation
* of the error or type, respectively. They will never
* return NULL
*/
JSONSL_API
const char* jsonsl_strerror(jsonsl_error_t err);
JSONSL_API
const char* jsonsl_strtype(jsonsl_type_t jt);
/**
* Dumps global metrics to the screen. This is a noop unless
* jsonsl was compiled with JSONSL_USE_METRICS
*/
JSONSL_API
void jsonsl_dump_global_metrics(void);
/* This macro just here for editors to do code folding */
#ifndef JSONSL_NO_JPR
/**
* @name JSON Pointer API
*
* JSONPointer API. This isn't really related to the lexer (at least not yet)
* JSONPointer provides an extremely simple specification for providing
* locations within JSON objects. We will extend it a bit and allow for
* providing 'wildcard' characters by which to be able to 'query' the stream.
*
* See http://tools.ietf.org/html/draft-pbryan-zyp-json-pointer-00
*
* Currently I'm implementing the 'single query' API which can only use a single
* query component. In the future I will integrate my yet-to-be-published
* Boyer-Moore-esque prefix searching implementation, in order to allow
* multiple paths to be merged into one for quick and efficient searching.
*
*
* JPR (as we'll refer to it within the source) can be used by splitting
* the components into mutliple sections, and incrementally 'track' each
* component. When JSONSL delivers a 'pop' callback for a string, or a 'push'
* callback for an object, we will check to see whether the index matching
* the component corresponding to the current level contains a match
* for our path.
*
* In order to do this properly, a structure must be maintained within the
* parent indicating whether its children are possible matches. This flag
* will be 'inherited' by call children which may conform to the match
* specification, and discarded by all which do not (thereby eliminating
* their children from inheriting it).
*
* A successful match is a complete one. One can provide multiple paths with
* multiple levels of matches e.g.
* /foo/bar/baz/^/blah
*
* @{
*/
/** The wildcard character */
#ifndef JSONSL_PATH_WILDCARD_CHAR
#define JSONSL_PATH_WILDCARD_CHAR '^'
#endif /* WILDCARD_CHAR */
#define JSONSL_XMATCH \
X(COMPLETE,1) \
X(POSSIBLE,0) \
X(NOMATCH,-1) \
X(TYPE_MISMATCH, -2)
typedef enum {
#define X(T,v) \
JSONSL_MATCH_##T = v,
JSONSL_XMATCH
#undef X
JSONSL_MATCH_UNKNOWN
} jsonsl_jpr_match_t;
typedef enum {
JSONSL_PATH_STRING = 1,
JSONSL_PATH_WILDCARD,
JSONSL_PATH_NUMERIC,
JSONSL_PATH_ROOT,
/* Special */
JSONSL_PATH_INVALID = -1,
JSONSL_PATH_NONE = 0
} jsonsl_jpr_type_t;
struct jsonsl_jpr_component_st {
/** The string the component points to */
char *pstr;
/** if this is a numeric type, the number is 'cached' here */
unsigned long idx;
/** The length of the string */
size_t len;
/** The type of component (NUMERIC or STRING) */
jsonsl_jpr_type_t ptype;
/** Set this to true to enforce type checking between dict keys and array
* indices. jsonsl_jpr_match() will return TYPE_MISMATCH if it detects
* that an array index is actually a child of a dictionary. */
short is_arridx;
/* Extra fields (for more advanced searches. Default is empty) */
JSONSL_JPR_COMPONENT_USER_FIELDS
};
struct jsonsl_jpr_st {
/** Path components */
struct jsonsl_jpr_component_st *components;
size_t ncomponents;
/**Type of the match to be expected. If nonzero, will be compared against
* the actual type */
unsigned match_type;
/** Base of allocated string for components */
char *basestr;
/** The original match string. Useful for returning to the user */
char *orig;
size_t norig;
};
/**
* Create a new JPR object.
*
* @param path the JSONPointer path specification.
* @param errp a pointer to a jsonsl_error_t. If this function returns NULL,
* then more details will be in this variable.
*
* @return a new jsonsl_jpr_t object, or NULL on error.
*/
JSONSL_API
jsonsl_jpr_t jsonsl_jpr_new(const char *path, jsonsl_error_t *errp);
/**
* Destroy a JPR object
*/
JSONSL_API
void jsonsl_jpr_destroy(jsonsl_jpr_t jpr);
/**
* Match a JSON object against a type and specific level
*
* @param jpr the JPR object
* @param parent_type the type of the parent (should be T_LIST or T_OBJECT)
* @param parent_level the level of the parent
* @param key the 'key' of the child. If the parent is an array, this should be
* empty.
* @param nkey - the length of the key. If the parent is an array (T_LIST), then
* this should be the current index.
*
* NOTE: The key of the child means any kind of associative data related to the
* element. Thus: <<< { "foo" : [ >>,
* the opening array's key is "foo".
*
* @return a status constant. This indicates whether a match was excluded, possible,
* or successful.
*/
JSONSL_API
jsonsl_jpr_match_t jsonsl_jpr_match(jsonsl_jpr_t jpr,
unsigned int parent_type,
unsigned int parent_level,
const char *key, size_t nkey);
/**
* Alternate matching algorithm. This matching algorithm does not use
* JSONPointer but relies on a more structured searching mechanism. It
* assumes that there is a clear distinction between array indices and
* object keys. In this case, the jsonsl_path_component_st::ptype should
* be set to @ref JSONSL_PATH_NUMERIC for an array index (the
* jsonsl_path_comonent_st::is_arridx field will be removed in a future
* version).
*
* @param jpr The path
* @param parent The parent structure. Can be NULL if this is the root object
* @param child The child structure. Should not be NULL
* @param key Object key, if an object
* @param nkey Length of object key
* @return Status constant if successful
*
* @note
* For successful matching, both the key and the path itself should be normalized
* to contain 'proper' utf8 sequences rather than utf16 '\uXXXX' escapes. This
* should currently be done in the application. Another version of this function
* may use a temporary buffer in such circumstances (allocated by the application).
*
* Since this function also checks the state of the child, it should only
* be called on PUSH callbacks, and not POP callbacks
*/
JSONSL_API
jsonsl_jpr_match_t
jsonsl_path_match(jsonsl_jpr_t jpr,
const struct jsonsl_state_st *parent,
const struct jsonsl_state_st *child,
const char *key, size_t nkey);
/**
* Associate a set of JPR objects with a lexer instance.
* This should be called before the lexer has been fed any data (and
* behavior is undefined if you don't adhere to this).
*
* After using this function, you may subsequently call match_state() on
* given states (presumably from within the callbacks).
*
* Note that currently the first JPR is the quickest and comes
* pre-allocated with the state structure. Further JPR objects
* are chained.
*
* @param jsn The lexer
* @param jprs An array of jsonsl_jpr_t objects
* @param njprs How many elements in the jprs array.
*/
JSONSL_API
void jsonsl_jpr_match_state_init(jsonsl_t jsn,
jsonsl_jpr_t *jprs,
size_t njprs);
/**
* This follows the same semantics as the normal match,
* except we infer parent and type information from the relevant state objects.
* The match status (for all possible JPR objects) is set in the *out parameter.
*
* If a match has succeeded, then its JPR object will be returned. In all other
* instances, NULL is returned;
*
* @param jpr The jsonsl_jpr_t handle
* @param state The jsonsl_state_st which is a candidate
* @param key The hash key (if applicable, can be NULL if parent is list)
* @param nkey Length of hash key (if applicable, can be zero if parent is list)
* @param out A pointer to a jsonsl_jpr_match_t. This will be populated with
* the match result
*
* @return If a match was completed in full, then the JPR object containing
* the matching path will be returned. Otherwise, the return is NULL (note, this
* does not mean matching has failed, it can still be part of the match: check
* the out parameter).
*/
JSONSL_API
jsonsl_jpr_t jsonsl_jpr_match_state(jsonsl_t jsn,
struct jsonsl_state_st *state,
const char *key,
size_t nkey,
jsonsl_jpr_match_t *out);
/**
* Cleanup any memory allocated and any states set by
* match_state_init() and match_state()
* @param jsn The lexer
*/
JSONSL_API
void jsonsl_jpr_match_state_cleanup(jsonsl_t jsn);
/**
* Return a string representation of the match result returned by match()
*/
JSONSL_API
const char *jsonsl_strmatchtype(jsonsl_jpr_match_t match);
/* @}*/
/**
* Utility function to convert escape sequences into their original form.
*
* The decoders I've sampled do not seem to specify a standard behavior of what
* to escape/unescape.
*
* RFC 4627 Mandates only that the quoute, backslash, and ASCII control
* characters (0x00-0x1f) be escaped. It is often common for applications
* to escape a '/' - however this may also be desired behavior. the JSON
* spec is not clear on this, and therefore jsonsl leaves it up to you.
*
* Additionally, sometimes you may wish to _normalize_ JSON. This is specifically
* true when dealing with 'u-escapes' which can be expressed perfectly fine
* as utf8. One use case for normalization is JPR string comparison, in which
* case two effectively equivalent strings may not match because one is using
* u-escapes and the other proper utf8. To normalize u-escapes only, pass in
* an empty `toEscape` table, enabling only the `u` index.
*
* @param in The input string.
* @param out An allocated output (should be the same size as in)
* @param len the size of the buffer
* @param toEscape - A sparse array of characters to unescape. Characters
* which are not present in this array, e.g. toEscape['c'] == 0 will be
* ignored and passed to the output in their original form.
* @param oflags If not null, and a \uXXXX escape expands to a non-ascii byte,
* then this variable will have the SPECIALf_NONASCII flag on.
*
* @param err A pointer to an error variable. If an error ocurrs, it will be
* set in this variable
* @param errat If not null and an error occurs, this will be set to point
* to the position within the string at which the offending character was
* encountered.
*
* @return The effective size of the output buffer.
*
* @note
* This function now encodes the UTF8 equivalents of utf16 escapes (i.e.
* 'u-escapes'). Previously this would encode the escapes as utf16 literals,
* which while still correct in some sense was confusing for many (especially
* considering that the inputs were variations of char).
*
* @note
* The output buffer will never be larger than the input buffer, since
* standard escape sequences (i.e. '\t') occupy two bytes in the source
* but only one byte (when unescaped) in the output. Likewise u-escapes
* (i.e. \uXXXX) will occupy six bytes in the source, but at the most
* two bytes when escaped.
*/
JSONSL_API
size_t jsonsl_util_unescape_ex(const char *in,
char *out,
size_t len,
const int toEscape[128],
unsigned *oflags,
jsonsl_error_t *err,
const char **errat);
/**
* Convenience macro to avoid passing too many parameters
*/
#define jsonsl_util_unescape(in, out, len, toEscape, err) \
jsonsl_util_unescape_ex(in, out, len, toEscape, NULL, err, NULL)
#endif /* JSONSL_NO_JPR */
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* JSONSL_H_ */
...@@ -53,14 +53,8 @@ static bool myspiffs_set_location(spiffs_config *cfg, int align, int offset, int ...@@ -53,14 +53,8 @@ static bool myspiffs_set_location(spiffs_config *cfg, int align, int offset, int
#ifdef SPIFFS_FIXED_LOCATION #ifdef SPIFFS_FIXED_LOCATION
cfg->phys_addr = (SPIFFS_FIXED_LOCATION + block_size - 1) & ~(block_size-1); cfg->phys_addr = (SPIFFS_FIXED_LOCATION + block_size - 1) & ~(block_size-1);
#else #else
if (flash_safe_get_size_byte() <= FLASH_SIZE_4MBYTE) {
// 256kByte - 4MByte modules: SPIFFS partition starts right after firmware image
cfg->phys_addr = ( u32_t )platform_flash_get_first_free_block_address( NULL ) + offset; cfg->phys_addr = ( u32_t )platform_flash_get_first_free_block_address( NULL ) + offset;
cfg->phys_addr = (cfg->phys_addr + align - 1) & ~(align - 1); cfg->phys_addr = (cfg->phys_addr + align - 1) & ~(align - 1);
} else {
// > 4MByte modules: SPIFFS partition starts after SDK data
cfg->phys_addr = flash_rom_get_size_byte();
}
#endif #endif
#ifdef SPIFFS_SIZE_1M_BOUNDARY #ifdef SPIFFS_SIZE_1M_BOUNDARY
cfg->phys_size = ((0x100000 - (SYS_PARAM_SEC_NUM * INTERNAL_FLASH_SECTOR_SIZE) - ( ( u32_t )cfg->phys_addr )) & ~(block_size - 1)) & 0xfffff; cfg->phys_size = ((0x100000 - (SYS_PARAM_SEC_NUM * INTERNAL_FLASH_SECTOR_SIZE) - ( ( u32_t )cfg->phys_addr )) & ~(block_size - 1)) & 0xfffff;
...@@ -255,17 +249,12 @@ static uint32_t myspiffs_vfs_size( const struct vfs_file *fd ); ...@@ -255,17 +249,12 @@ static uint32_t myspiffs_vfs_size( const struct vfs_file *fd );
static sint32_t myspiffs_vfs_ferrno( const struct vfs_file *fd ); static sint32_t myspiffs_vfs_ferrno( const struct vfs_file *fd );
static sint32_t myspiffs_vfs_closedir( const struct vfs_dir *dd ); static sint32_t myspiffs_vfs_closedir( const struct vfs_dir *dd );
static vfs_item *myspiffs_vfs_readdir( const struct vfs_dir *dd ); static sint32_t myspiffs_vfs_readdir( const struct vfs_dir *dd, struct vfs_stat *buf );
static void myspiffs_vfs_iclose( const struct vfs_item *di );
static uint32_t myspiffs_vfs_isize( const struct vfs_item *di );
//static const struct tm *myspiffs_vfs_time( const struct vfs_item *di );
static const char *myspiffs_vfs_name( const struct vfs_item *di );
static vfs_vol *myspiffs_vfs_mount( const char *name, int num ); static vfs_vol *myspiffs_vfs_mount( const char *name, int num );
static vfs_file *myspiffs_vfs_open( const char *name, const char *mode ); static vfs_file *myspiffs_vfs_open( const char *name, const char *mode );
static vfs_dir *myspiffs_vfs_opendir( const char *name ); static vfs_dir *myspiffs_vfs_opendir( const char *name );
static vfs_item *myspiffs_vfs_stat( const char *name ); static sint32_t myspiffs_vfs_stat( const char *name, struct vfs_stat *buf );
static sint32_t myspiffs_vfs_remove( const char *name ); static sint32_t myspiffs_vfs_remove( const char *name );
static sint32_t myspiffs_vfs_rename( const char *oldname, const char *newname ); static sint32_t myspiffs_vfs_rename( const char *oldname, const char *newname );
static sint32_t myspiffs_vfs_fsinfo( uint32_t *total, uint32_t *used ); static sint32_t myspiffs_vfs_fsinfo( uint32_t *total, uint32_t *used );
...@@ -308,18 +297,6 @@ static vfs_file_fns myspiffs_file_fns = { ...@@ -308,18 +297,6 @@ static vfs_file_fns myspiffs_file_fns = {
.ferrno = myspiffs_vfs_ferrno .ferrno = myspiffs_vfs_ferrno
}; };
static vfs_item_fns myspiffs_item_fns = {
.close = myspiffs_vfs_iclose,
.size = myspiffs_vfs_isize,
.time = NULL,
.name = myspiffs_vfs_name,
.is_dir = NULL,
.is_rdonly = NULL,
.is_hidden = NULL,
.is_sys = NULL,
.is_arch = NULL
};
static vfs_dir_fns myspiffs_dd_fns = { static vfs_dir_fns myspiffs_dd_fns = {
.close = myspiffs_vfs_closedir, .close = myspiffs_vfs_closedir,
.readdir = myspiffs_vfs_readdir .readdir = myspiffs_vfs_readdir
...@@ -339,36 +316,6 @@ struct myvfs_dir { ...@@ -339,36 +316,6 @@ struct myvfs_dir {
spiffs_DIR d; spiffs_DIR d;
}; };
struct myvfs_stat {
struct vfs_item vfs_item;
spiffs_stat s;
};
// ---------------------------------------------------------------------------
// stat functions
//
#define GET_STAT_S(descr) \
const struct myvfs_stat *mystat = (const struct myvfs_stat *)descr; \
spiffs_stat *s = (spiffs_stat *)&(mystat->s);
static void myspiffs_vfs_iclose( const struct vfs_item *di ) {
// free descriptor memory
c_free( (void *)di );
}
static uint32_t myspiffs_vfs_isize( const struct vfs_item *di ) {
GET_STAT_S(di);
return s->size;
}
static const char *myspiffs_vfs_name( const struct vfs_item *di ) {
GET_STAT_S(di);
return s->name;
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// volume functions // volume functions
...@@ -396,25 +343,22 @@ static sint32_t myspiffs_vfs_closedir( const struct vfs_dir *dd ) { ...@@ -396,25 +343,22 @@ static sint32_t myspiffs_vfs_closedir( const struct vfs_dir *dd ) {
c_free( (void *)dd ); c_free( (void *)dd );
} }
static vfs_item *myspiffs_vfs_readdir( const struct vfs_dir *dd ) { static sint32_t myspiffs_vfs_readdir( const struct vfs_dir *dd, struct vfs_stat *buf ) {
GET_DIR_D(dd); GET_DIR_D(dd);
struct myvfs_stat *stat;
struct spiffs_dirent dirent; struct spiffs_dirent dirent;
if (stat = c_malloc( sizeof( struct myvfs_stat ) )) {
if (SPIFFS_readdir( d, &dirent )) { if (SPIFFS_readdir( d, &dirent )) {
stat->vfs_item.fs_type = VFS_FS_FATFS; c_memset( buf, 0, sizeof( struct vfs_stat ) );
stat->vfs_item.fns = &myspiffs_item_fns;
// copy entries to vfs' directory item // copy entries to item
stat->s.size = dirent.size; // fill in supported stat entries
c_strncpy( stat->s.name, dirent.name, SPIFFS_OBJ_NAME_LEN ); c_strncpy( buf->name, dirent.name, FS_OBJ_NAME_LEN+1 );
return (vfs_item *)stat; buf->name[FS_OBJ_NAME_LEN] = '\0';
} else { buf->size = dirent.size;
c_free( stat ); return VFS_RES_OK;
}
} }
return NULL; return VFS_RES_ERR;
} }
...@@ -566,20 +510,21 @@ static vfs_dir *myspiffs_vfs_opendir( const char *name ){ ...@@ -566,20 +510,21 @@ static vfs_dir *myspiffs_vfs_opendir( const char *name ){
return NULL; return NULL;
} }
static vfs_item *myspiffs_vfs_stat( const char *name ) { static sint32_t myspiffs_vfs_stat( const char *name, struct vfs_stat *buf ) {
struct myvfs_stat *s; spiffs_stat stat;
if (0 <= SPIFFS_stat( &fs, name, &stat )) {
c_memset( buf, 0, sizeof( struct vfs_stat ) );
if (s = (struct myvfs_stat *)c_malloc( sizeof( struct myvfs_stat ) )) { // fill in supported stat entries
if (0 <= SPIFFS_stat( &fs, name, &(s->s) )) { c_strncpy( buf->name, stat.name, FS_OBJ_NAME_LEN+1 );
s->vfs_item.fs_type = VFS_FS_SPIFFS; buf->name[FS_OBJ_NAME_LEN] = '\0';
s->vfs_item.fns = &myspiffs_item_fns; buf->size = stat.size;
return (vfs_item *)s;
return VFS_RES_OK;
} else { } else {
c_free( s ); return VFS_RES_ERR;
}
} }
return NULL;
} }
static sint32_t myspiffs_vfs_remove( const char *name ) { static sint32_t myspiffs_vfs_remove( const char *name ) {
......
#############################################################
# 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 = libswtimer.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 +=
#############################################################
# 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
INCLUDES += -I ../lua
INCLUDES += -I ../platform
INCLUDES += -I ../libc
PDIR := ../$(PDIR)
sinclude $(PDIR)Makefile
/* swTimer.c SDK timer suspend API
*
* SDK software timer API info:
*
* The SDK software timer uses a linked list called `os_timer_t* timer_list` to keep track of
* all currently armed timers.
*
* The SDK software timer API executes in a task. The priority of this task in relation to the
* application level tasks is unknown (at time of writing).
*
*
* To determine when a timer's callback should be executed, the respective timer's `timer_expire`
* variable is compared to the hardware counter(FRC2), then, if the timer's `timer_expire` is
* less than the current FRC2 count, the timer's callback is fired.
*
* The timers in this list are organized in an ascending order starting with the timer
* with the lowest timer_expire.
*
* When a timer expires that has a timer_period greater than 0, timer_expire is changed to
* current FRC2 + timer_period, then the timer is inserted back in to the list in the correct position.
*
* when using millisecond(default) timers, FRC2 resolution is 312.5 ticks per millisecond.
*
*
* TIMER SUSPEND API:
*
* Timer registry:
* void sw_timer_register(void* timer_ptr);
* - Adds timers to the timer registry by adding it to a queue that is later
* processed by timer_register_task that performs the registry maintenance
*
* void sw_timer_unregister(void* timer_ptr);
* - Removes timers from the timer registry by adding it to a queue that is later
* processed by timer_unregister_task that performs the registry maintenance
*
*
* int sw_timer_suspend(os_timer_t* timer_ptr);
* - Suspend a single active timer or suspend all active timers.
* - if no timer pointer is provided, timer_ptr == NULL, then all currently active timers will be suspended.
*
* int sw_timer_resume(os_timer_t* timer_ptr);
* - Resume a single suspended timer or resume all suspended timers.
* - if no timer pointer is provided, timer_ptr == NULL, then all currently suspended timers will be resumed.
*
*
*
*/
#include "swTimer/swTimer.h"
#include "c_stdio.h"
#include "misc/dynarr.h"
#include "task/task.h"
#ifdef ENABLE_TIMER_SUSPEND
/* Settings */
#define TIMER_REGISTRY_INITIAL_SIZE 10
#ifdef USE_SWTMR_ERROR_STRINGS
static const char* SWTMR_ERROR_STRINGS[]={
[SWTMR_MALLOC_FAIL] = "Out of memory!",
[SWTMR_TIMER_NOT_ARMED] = "Timer is not armed",
// [SWTMR_NULL_PTR] = "A NULL pointer was passed to timer suspend api",
[SWTMR_REGISTRY_NO_REGISTERED_TIMERS] = "No timers in registry",
// [SWTMR_SUSPEND_ARRAY_INITIALIZATION_FAILED] = "Suspend array init fail",
// [SWTMR_SUSPEND_ARRAY_ADD_FAILED] = "Unable to add suspended timer to array",
// [SWTMR_SUSPEND_ARRAY_REMOVE_FAILED] = "Unable to remove suspended timer from array",
[SWTMR_SUSPEND_TIMER_ALREADY_SUSPENDED] = "Already suspended",
[SWTMR_SUSPEND_TIMER_ALREADY_REARMED] = "Already been re-armed",
[SWTMR_SUSPEND_NO_SUSPENDED_TIMERS] = "No suspended timers",
[SWTMR_SUSPEND_TIMER_NOT_SUSPENDED] = "Not suspended",
};
#endif
/* Private Function Declarations */
static inline bool timer_armed_check(os_timer_t* timer_ptr);
static inline int timer_do_suspend(os_timer_t* timer_ptr);
static inline int timer_do_resume_single(os_timer_t** suspended_timer_ptr);
static void timer_register_task(task_param_t param, uint8 priority);
static inline os_timer_t** timer_registry_check(os_timer_t* timer_ptr);
static inline void timer_registry_remove_unarmed(void);
static inline os_timer_t** timer_suspended_check(os_timer_t* timer_ptr);
static void timer_unregister_task(task_param_t param, uint8 priority);
/* Private Variable Definitions */
static task_handle_t timer_reg_task_id = false;
static task_handle_t timer_unreg_task_id = false;
static dynarr_t timer_registry = {0};
static dynarr_t suspended_timers = {0};
typedef struct registry_queue{
struct registry_queue* next;
os_timer_t* timer_ptr;
}registry_queue_t;
static registry_queue_t* register_queue = NULL;
static registry_queue_t* unregister_queue = NULL;
/* Private Function Definitions */
//NOTE: Interrupts are temporarily blocked during the execution of this function
static inline bool timer_armed_check(os_timer_t* timer_ptr){
bool retval = FALSE;
// we are messing around with the SDK timer structure here, may not be necessary, better safe than sorry though.
ETS_INTR_LOCK();
os_timer_t* timer_list_ptr = timer_list; //get head node pointer of timer_list
//if present find timer_ptr in timer_list rand return result
while(timer_list_ptr != NULL){
if(timer_list_ptr == timer_ptr){
retval = TRUE;
break;
}
timer_list_ptr = timer_list_ptr->timer_next;
}
//we are done with timer_list, it is now safe to unlock interrupts
ETS_INTR_UNLOCK();
//return value
return retval;
}
static inline int timer_do_suspend(os_timer_t* timer_ptr){
if(timer_ptr == NULL){
SWTMR_ERR("timer_ptr is invalid");
return SWTMR_FAIL;
}
volatile uint32 frc2_count = RTC_REG_READ(FRC2_COUNT_ADDRESS);
if(timer_armed_check(timer_ptr) == FALSE){
return SWTMR_TIMER_NOT_ARMED;
}
os_timer_t** suspended_timer_ptr = timer_suspended_check(timer_ptr);
uint32 expire_temp = 0;
uint32 period_temp = timer_ptr->timer_period;
if(timer_ptr->timer_expire < frc2_count){
expire_temp = 5; // 16 us in ticks (1 tick = ~3.2 us) (arbitrarily chosen value)
}
else{
expire_temp = timer_ptr->timer_expire - frc2_count;
}
ets_timer_disarm(timer_ptr);
timer_unregister_task((task_param_t)timer_ptr, false);
timer_ptr->timer_expire = expire_temp;
timer_ptr->timer_period = period_temp;
if(suspended_timers.data_ptr == NULL){
if(!dynarr_init(&suspended_timers, 10, sizeof(os_timer_t*))){
SWTMR_ERR("Suspend array init fail");
return SWTMR_FAIL;
}
}
if(suspended_timer_ptr == NULL){
// return SWTMR_SUSPEND_TIMER_ALREADY_SUSPENDED;
if(!dynarr_add(&suspended_timers, &timer_ptr, sizeof(timer_ptr))){
SWTMR_ERR("Unable to add suspended timer to array");
return SWTMR_FAIL;
}
}
return SWTMR_OK;
}
//NOTE: Interrupts are temporarily blocked during the execution of this function
static inline int timer_do_resume_single(os_timer_t** suspended_timer_ptr){
if(suspended_timer_ptr == NULL){
SWTMR_ERR("suspended_timer_ptr is invalid");
return SWTMR_FAIL;
}
os_timer_t* timer_list_ptr = NULL;
os_timer_t* resume_timer_ptr = *suspended_timer_ptr;
volatile uint32 frc2_count = RTC_REG_READ(FRC2_COUNT_ADDRESS);
//verify timer has not been rearmed
if(timer_armed_check(resume_timer_ptr) == TRUE){
SWTMR_DBG("Timer(%p) already rearmed, removing from array", resume_timer_ptr);
if(!dynarr_remove(&suspended_timers, suspended_timer_ptr)){
SWTMR_ERR("Failed to remove timer from suspend array");
return SWTMR_FAIL;
}
return SWTMR_OK;
}
//Prepare timer for resume
resume_timer_ptr->timer_expire += frc2_count;
timer_register_task((task_param_t)resume_timer_ptr, false);
SWTMR_DBG("Removing timer(%p) from suspend array", resume_timer_ptr);
//This section performs the actual resume of the suspended timer
// we are messing around with the SDK timer structure here, may not be necessary, better safe than sorry though.
ETS_INTR_LOCK();
timer_list_ptr = timer_list;
while(timer_list_ptr != NULL){
if(resume_timer_ptr->timer_expire > timer_list_ptr->timer_expire){
if(timer_list_ptr->timer_next != NULL){
if(resume_timer_ptr->timer_expire < timer_list_ptr->timer_next->timer_expire){
resume_timer_ptr->timer_next = timer_list_ptr->timer_next;
timer_list_ptr->timer_next = resume_timer_ptr;
break;
}
else{
//next timer in timer_list
}
}
else{
timer_list_ptr->timer_next = resume_timer_ptr;
resume_timer_ptr->timer_next = NULL;
break;
}
}
else if(timer_list_ptr == timer_list){
resume_timer_ptr->timer_next=timer_list_ptr;
timer_list = timer_list_ptr = resume_timer_ptr;
break;
}
timer_list_ptr = timer_list_ptr->timer_next;
}
//we no longer need to block interrupts
ETS_INTR_UNLOCK();
return SWTMR_OK;
}
static void timer_register_task(task_param_t param, uint8 priority){
if(timer_registry.data_ptr==NULL){
if(!dynarr_init(&timer_registry, TIMER_REGISTRY_INITIAL_SIZE, sizeof(os_timer_t*))){
SWTMR_ERR("timer registry init Fail!");
return;
}
}
os_timer_t* timer_ptr = NULL;
//if a timer pointer is provided, override normal queue processing behavior
if(param != 0){
timer_ptr = (os_timer_t*)param;
}
else{
//process an item in the register queue
if(register_queue == NULL){
/**/SWTMR_ERR("ERROR: REGISTER QUEUE EMPTY");
return;
}
registry_queue_t* queue_temp = register_queue;
register_queue = register_queue->next;
timer_ptr = queue_temp->timer_ptr;
c_free(queue_temp);
if(register_queue != NULL){
SWTMR_DBG("register_queue not empty, posting task");
task_post_low(timer_reg_task_id, false);
}
}
os_timer_t** suspended_tmr_ptr = timer_suspended_check(timer_ptr);
if(suspended_tmr_ptr != NULL){
if(!dynarr_remove(&suspended_timers, suspended_tmr_ptr)){
SWTMR_ERR("failed to remove %p from suspend registry", suspended_tmr_ptr);
}
SWTMR_DBG("removed timer from suspended timers");
}
if(timer_registry_check(timer_ptr) != NULL){
/**/SWTMR_DBG("timer(%p) found in registry, returning", timer_ptr);
return;
}
if(!dynarr_add(&timer_registry, &timer_ptr, sizeof(timer_ptr))){
/**/SWTMR_ERR("Registry append failed");
return;
}
return;
}
static inline os_timer_t** timer_registry_check(os_timer_t* timer_ptr){
if(timer_registry.data_ptr == NULL){
return NULL;
}
if(timer_registry.used > 0){
os_timer_t** timer_registry_array = timer_registry.data_ptr;
for(uint32 i=0; i < timer_registry.used; i++){
if(timer_registry_array[i] == timer_ptr){
/**/SWTMR_DBG("timer(%p) is registered", timer_registry_array[i]);
return &timer_registry_array[i];
}
}
}
return NULL;
}
static inline void timer_registry_remove_unarmed(void){
if(timer_registry.data_ptr == NULL){
return;
}
if(timer_registry.used > 0){
os_timer_t** timer_registry_array = timer_registry.data_ptr;
for(uint32 i=0; i < timer_registry.used; i++){
if(timer_armed_check(timer_registry_array[i]) == FALSE){
timer_unregister_task((task_param_t)timer_registry_array[i], false);
}
}
}
}
static inline os_timer_t** timer_suspended_check(os_timer_t* timer_ptr){
if(suspended_timers.data_ptr == NULL){
return NULL;
}
if(suspended_timers.used > 0){
os_timer_t** suspended_timer_array = suspended_timers.data_ptr;
for(uint32 i=0; i < suspended_timers.used; i++){
if(suspended_timer_array[i] == timer_ptr){
return &suspended_timer_array[i];
}
}
}
return NULL;
}
static void timer_unregister_task(task_param_t param, uint8 priority){
if(timer_registry.data_ptr == NULL){
return;
}
os_timer_t* timer_ptr = NULL;
if(param != false){
timer_ptr = (os_timer_t*)param;
}
else{
if(unregister_queue == NULL) {
SWTMR_ERR("ERROR register queue empty");
return;
}
registry_queue_t* queue_temp = unregister_queue;
timer_ptr = queue_temp->timer_ptr;
unregister_queue = unregister_queue->next;
c_free(queue_temp);
if(unregister_queue != NULL){
SWTMR_DBG("unregister_queue not empty, posting task");
task_post_low(timer_unreg_task_id, false);
}
}
if(timer_armed_check(timer_ptr) == TRUE){
SWTMR_DBG("%p still armed, can't remove from registry", timer_ptr);
return;
}
os_timer_t** registry_ptr = timer_registry_check(timer_ptr);
if(registry_ptr != NULL){
if(!dynarr_remove(&timer_registry, registry_ptr)){
/**/SWTMR_ERR("Failed to remove timer from registry");
/**/SWTMR_DBG("registry_ptr = %p", registry_ptr);
return;
}
}
else{
//timer not in registry
}
return;
}
/* Global Function Definitions */
#if defined(SWTMR_DEBUG)
void swtmr_print_registry(void){
volatile uint32 frc2_count = RTC_REG_READ(FRC2_COUNT_ADDRESS);
uint32 time_till_fire = 0;
uint32 time = system_get_time();
timer_registry_remove_unarmed();
time = system_get_time()-time;
/**/SWTMR_DBG("registry_remove_unarmed_timers() took %u us", time);
os_timer_t** timer_array = timer_registry.data_ptr;
c_printf("\n array used(%u)/size(%u)\ttotal size(bytes)=%u\n FRC2 COUNT %u\n",
timer_registry.used, timer_registry.array_size, timer_registry.array_size * timer_registry.data_size, frc2_count);
c_printf("\n Registered timer array contents:\n");
c_printf(" %-5s %-10s %-10s %-13s %-10s %-10s %-10s\n", "idx", "ptr", "expire", "period(tick)", "period(ms)", "fire(tick)", "fire(ms)");
for(uint32 i=0; i < timer_registry.used; i++){
time_till_fire = (timer_array[i]->timer_expire - frc2_count);
c_printf(" %-5d %-10p %-10d %-13d %-10d %-10d %-10d\n", i, timer_array[i], timer_array[i]->timer_expire, timer_array[i]->timer_period, (uint32)(timer_array[i]->timer_period/312.5), time_till_fire, (uint32)(time_till_fire/312.5));
}
return;
}
void swtmr_print_suspended(void){
os_timer_t** susp_timer_array = suspended_timers.data_ptr;
c_printf("\n array used(%u)/size(%u)\ttotal size(bytes)=%u\n",
suspended_timers.used, suspended_timers.array_size, suspended_timers.array_size * suspended_timers.data_size);
c_printf("\n Suspended timer array contents:\n");
c_printf(" %-5s %-10s %-15s %-15s %-14s %-10s\n", "idx", "ptr", "time left(tick)", "time left(ms)", "period(tick)", "period(ms)");
for(uint32 i=0; i < suspended_timers.used; i++){
c_printf(" %-5d %-10p %-15d %-15d %-14d %-10d\n", i, susp_timer_array[i], susp_timer_array[i]->timer_expire, (uint32)(susp_timer_array[i]->timer_expire/312.5), susp_timer_array[i]->timer_period, (uint32)(susp_timer_array[i]->timer_period/312.5));
}
return;
}
void swtmr_print_timer_list(void){
volatile uint32 frc2_count=RTC_REG_READ(FRC2_COUNT_ADDRESS);
os_timer_t* timer_list_ptr=NULL;
uint32 time_till_fire=0;
c_printf("\n\tcurrent FRC2 count:%u\n", frc2_count);
c_printf(" timer_list contents:\n");
c_printf(" %-10s %-10s %-10s %-10s %-10s %-10s %-10s\n", "ptr", "expire", "period", "func", "arg", "fire(tick)", "fire(ms)");
ETS_INTR_LOCK();
timer_list_ptr=timer_list;
while(timer_list_ptr != NULL){
time_till_fire=(timer_list_ptr->timer_expire - frc2_count) / 312.5;
c_printf(" %-10p %-10u %-10u %-10p %-10p %-10u %-10u\n",
timer_list_ptr, (uint32)(timer_list_ptr->timer_expire),
(uint32)(timer_list_ptr->timer_period ), timer_list_ptr->timer_func,
timer_list_ptr->timer_arg, (timer_list_ptr->timer_expire - frc2_count), time_till_fire);
timer_list_ptr=timer_list_ptr->timer_next;
}
ETS_INTR_UNLOCK();
c_printf(" NOTE: some timers in the above list belong to the SDK and can not be suspended\n");
return;
}
#endif
int swtmr_suspend(os_timer_t* timer_ptr){
int return_value = SWTMR_OK;
if(timer_ptr != NULL){
// Timer pointer was provided, suspending specified timer
return_value = timer_do_suspend(timer_ptr);
if(return_value != SWTMR_OK){
return return_value;
}
}
else{
//timer pointer not found, suspending all timers
if(timer_registry.data_ptr == NULL){
return SWTMR_REGISTRY_NO_REGISTERED_TIMERS;
}
timer_registry_remove_unarmed();
os_timer_t** tmr_reg_arr = timer_registry.data_ptr;
os_timer_t* temp_ptr = tmr_reg_arr[0];
while(temp_ptr != NULL){
return_value = timer_do_suspend(temp_ptr);
if(return_value != SWTMR_OK){
return return_value;
}
temp_ptr = tmr_reg_arr[0];
}
}
return return_value;
}
int swtmr_resume(os_timer_t* timer_ptr){
if(suspended_timers.data_ptr == NULL){
return SWTMR_SUSPEND_NO_SUSPENDED_TIMERS;
}
os_timer_t** suspended_tmr_array = suspended_timers.data_ptr;
os_timer_t** suspended_timer_ptr = NULL;
int retval=SWTMR_OK;
if(timer_ptr != NULL){
suspended_timer_ptr = timer_suspended_check(timer_ptr);
if(suspended_timer_ptr == NULL){
//timer not suspended
return SWTMR_SUSPEND_TIMER_NOT_SUSPENDED;
}
retval = timer_do_resume_single(suspended_timer_ptr);
if(retval != SWTMR_OK){
return retval;
}
}
else{
suspended_timer_ptr = &suspended_tmr_array[0];
while(suspended_timers.used > 0){
retval = timer_do_resume_single(suspended_timer_ptr);
if(retval != SWTMR_OK){
SWTMR_ERR("Unable to continue resuming timers, error(%u)", retval);
return retval;
}
suspended_timer_ptr = &suspended_tmr_array[0];
}
}
return SWTMR_OK;
}
void swtmr_register(void* timer_ptr){
if(timer_ptr == NULL){
SWTMR_DBG("error: timer_ptr is NULL");
return;
}
registry_queue_t* queue_temp = c_zalloc(sizeof(registry_queue_t));
if(queue_temp == NULL){
SWTMR_ERR("MALLOC FAIL! req:%u, free:%u", sizeof(registry_queue_t), system_get_free_heap_size());
return;
}
queue_temp->timer_ptr = timer_ptr;
if(register_queue == NULL){
register_queue = queue_temp;
if(timer_reg_task_id == false) timer_reg_task_id = task_get_id(timer_register_task);
task_post_low(timer_reg_task_id, false);
SWTMR_DBG("queue empty, adding timer(%p) to queue and posting task", timer_ptr);
}
else{
registry_queue_t* register_queue_tail = register_queue;
while(register_queue_tail->next != NULL){
register_queue_tail = register_queue_tail->next;
}
register_queue_tail->next = queue_temp;
SWTMR_DBG("queue NOT empty, appending timer(%p) to queue", timer_ptr);
}
return;
}
void swtmr_unregister(void* timer_ptr){
if(timer_ptr == NULL){
SWTMR_DBG("error: timer_ptr is NULL");
return;
}
registry_queue_t* queue_temp = c_zalloc(sizeof(registry_queue_t));
if(queue_temp == NULL){
SWTMR_ERR("MALLOC FAIL! req:%u, free:%u", sizeof(registry_queue_t), system_get_free_heap_size());
return;
}
queue_temp->timer_ptr=timer_ptr;
if(unregister_queue == NULL){
unregister_queue = queue_temp;
if(timer_unreg_task_id==false) timer_unreg_task_id=task_get_id(timer_unregister_task);
task_post_low(timer_unreg_task_id, false);
SWTMR_DBG("queue empty, adding timer(%p) to queue and posting task", timer_ptr);
}
else{
registry_queue_t* unregister_queue_tail=unregister_queue;
while(unregister_queue_tail->next != NULL){
unregister_queue_tail=unregister_queue_tail->next;
}
unregister_queue_tail->next = queue_temp;
// SWTMR_DBG("queue NOT empty, appending timer(%p) to queue", timer_ptr);
}
return;
}
const char* swtmr_errorcode2str(int error_value){
#ifdef USE_SWTMR_ERROR_STRINGS
if(SWTMR_ERROR_STRINGS[error_value] == NULL){
SWTMR_ERR("error string %d not found", error_value);
return NULL;
}
else{
return SWTMR_ERROR_STRINGS[error_value];
}
#else
SWTMR_ERR("error(%u)", error_value);
return "ERROR! for more info, use debug build";
#endif
}
bool swtmr_suspended_test(os_timer_t* timer_ptr){
os_timer_t** test_var = timer_suspended_check(timer_ptr);
if(test_var == NULL){
return false;
}
return true;
}
#endif
...@@ -76,6 +76,12 @@ static tsl2561Gain_t _tsl2561Gain = TSL2561_GAIN_1X; ...@@ -76,6 +76,12 @@ static tsl2561Gain_t _tsl2561Gain = TSL2561_GAIN_1X;
static tsl2561Address_t tsl2561Address = TSL2561_ADDRESS_FLOAT; static tsl2561Address_t tsl2561Address = TSL2561_ADDRESS_FLOAT;
static tsl2561Package_t tsl2561Package = TSL2561_PACKAGE_T_FN_CL; static tsl2561Package_t tsl2561Package = TSL2561_PACKAGE_T_FN_CL;
static void delay_ms(uint16_t ms)
{
while (ms--)
os_delay_us(1000);
}
/**************************************************************************/ /**************************************************************************/
/*! /*!
@brief Writes an 8 bit values over I2C @brief Writes an 8 bit values over I2C
...@@ -230,13 +236,13 @@ tsl2561Error_t tsl2561GetLuminosity(uint16_t *broadband, uint16_t *ir) { ...@@ -230,13 +236,13 @@ tsl2561Error_t tsl2561GetLuminosity(uint16_t *broadband, uint16_t *ir) {
// Wait x ms for ADC to complete // Wait x ms for ADC to complete
switch (_tsl2561IntegrationTime) { switch (_tsl2561IntegrationTime) {
case TSL2561_INTEGRATIONTIME_13MS: case TSL2561_INTEGRATIONTIME_13MS:
os_delay_us(14000); //systickDelay(14); delay_ms(14); //systickDelay(14);
break; break;
case TSL2561_INTEGRATIONTIME_101MS: case TSL2561_INTEGRATIONTIME_101MS:
os_delay_us(102000); //systickDelay(102); delay_ms(102); //systickDelay(102);
break; break;
default: default:
os_delay_us(404000); //systickDelay(404); delay_ms(404); //systickDelay(404);
break; break;
} }
......
...@@ -72,9 +72,24 @@ void TEXT_SECTION_ATTR user_start_trampoline (void) ...@@ -72,9 +72,24 @@ void TEXT_SECTION_ATTR user_start_trampoline (void)
* is deliberately quite terse and not as readable as one might like. * is deliberately quite terse and not as readable as one might like.
*/ */
SPIFlashInfo sfi; SPIFlashInfo sfi;
// enable operations on >4MB flash chip
extern SpiFlashChip * flashchip;
uint32 orig_chip_size = flashchip->chip_size;
flashchip->chip_size = FLASH_SIZE_16MBYTE;
SPIRead (0, (uint32_t *)(&sfi), sizeof (sfi)); // Cache read not enabled yet, safe to use SPIRead (0, (uint32_t *)(&sfi), sizeof (sfi)); // Cache read not enabled yet, safe to use
if (sfi.size < 2) // Compensate for out-of-order 4mbit vs 2mbit values // handle all size entries
sfi.size ^= 1; switch (sfi.size) {
case 0: sfi.size = 1; break; // SIZE_4MBIT
case 1: sfi.size = 0; break; // SIZE_2MBIT
case 5: sfi.size = 3; break; // SIZE_16MBIT_8M_8M
case 6: // fall-through
case 7: sfi.size = 4; break; // SIZE_32MBIT_8M_8M, SIZE_32MBIT_16M_16M
case 8: sfi.size = 5; break; // SIZE_64MBIT
case 9: sfi.size = 6; break; // SIZE_128MBIT
default: break;
}
uint32_t flash_end_addr = (256 * 1024) << sfi.size; uint32_t flash_end_addr = (256 * 1024) << sfi.size;
uint32_t init_data_hdr = 0xffffffff; uint32_t init_data_hdr = 0xffffffff;
uint32_t init_data_addr = flash_end_addr - 4 * SPI_FLASH_SEC_SIZE; uint32_t init_data_addr = flash_end_addr - 4 * SPI_FLASH_SEC_SIZE;
...@@ -85,6 +100,9 @@ void TEXT_SECTION_ATTR user_start_trampoline (void) ...@@ -85,6 +100,9 @@ void TEXT_SECTION_ATTR user_start_trampoline (void)
SPIWrite (init_data_addr, init_data, 4 * (init_data_end - init_data)); SPIWrite (init_data_addr, init_data, 4 * (init_data_end - init_data));
} }
// revert temporary setting
flashchip->chip_size = orig_chip_size;
call_user_start (); call_user_start ();
} }
...@@ -122,21 +140,10 @@ void nodemcu_init(void) ...@@ -122,21 +140,10 @@ void nodemcu_init(void)
return; return;
} }
if( flash_safe_get_size_byte() <= FLASH_SIZE_4MBYTE ) { if( flash_detect_size_byte() != flash_rom_get_size_byte() ) {
if( flash_safe_get_size_byte() != flash_rom_get_size_byte() ) {
NODE_ERR("Self adjust flash size.\n"); NODE_ERR("Self adjust flash size.\n");
// Fit hardware real flash size. // Fit hardware real flash size.
flash_rom_set_size_byte(flash_safe_get_size_byte()); flash_rom_set_size_byte(flash_detect_size_byte());
system_restart ();
// Don't post the start_lua task, we're about to reboot...
return;
}
} else if( (flash_rom_get_size_byte() < FLASH_SIZE_1MBYTE) ||
(flash_rom_get_size_byte() > FLASH_SIZE_4MBYTE) ) {
NODE_ERR("Locking flash size for SDK to 1MByte.\n");
// SDK/ROM can't handle flash size > 4MByte, ensure a minimum of 1MByte for firmware image
flash_rom_set_size_byte(FLASH_SIZE_1MBYTE);
system_restart (); system_restart ();
// Don't post the start_lua task, we're about to reboot... // Don't post the start_lua task, we're about to reboot...
...@@ -190,7 +197,7 @@ void user_rf_pre_init(void) ...@@ -190,7 +197,7 @@ void user_rf_pre_init(void)
uint32 uint32
user_rf_cal_sector_set(void) user_rf_cal_sector_set(void)
{ {
enum ext_flash_size_map size_map = system_get_flash_size_map(); enum flash_size_map size_map = system_get_flash_size_map();
uint32 rf_cal_sec = 0; uint32 rf_cal_sec = 0;
switch (size_map) { switch (size_map) {
...@@ -213,11 +220,11 @@ user_rf_cal_sector_set(void) ...@@ -213,11 +220,11 @@ user_rf_cal_sector_set(void)
rf_cal_sec = 1024 - 5; rf_cal_sec = 1024 - 5;
break; break;
case FLASH_SIZE_64M_MAP: case FLASH_SIZE_64M_MAP_1024_1024:
rf_cal_sec = 2048 - 5; rf_cal_sec = 2048 - 5;
break; break;
case FLASH_SIZE_128M_MAP: case FLASH_SIZE_128M_MAP_1024_1024:
rf_cal_sec = 4096 - 5; rf_cal_sec = 4096 - 5;
break; break;
......
There are essentially three ways to build your NodeMCU firmware: cloud build service, Docker image, dedicated Linux environment (possibly VM). There are essentially three ways to build your NodeMCU firmware: cloud build service, Docker image, dedicated Linux environment (possibly VM).
**Building manually** ## Tools
Note that the *default configuration in the C header files* (`user_config.h`, `user_modules.h`) is designed to run on all ESP modules including the 512 KB modules like ESP-01 and only includes general purpose interface modules which require at most two GPIO pins. ### Cloud Build Service
## Cloud Build Service
NodeMCU "application developers" just need a ready-made firmware. There's a [cloud build service](http://nodemcu-build.com/) with a nice UI and configuration options for them. NodeMCU "application developers" just need a ready-made firmware. There's a [cloud build service](http://nodemcu-build.com/) with a nice UI and configuration options for them.
## Docker Image ### Docker Image
Occasional NodeMCU firmware hackers don't need full control over the complete tool chain. They might not want to setup a Linux VM with the build environment. Docker to the rescue. Give [Docker NodeMCU build](https://hub.docker.com/r/marcelstoer/nodemcu-build/) a try. Occasional NodeMCU firmware hackers don't need full control over the complete tool chain. They might not want to setup a Linux VM with the build environment. Docker to the rescue. Give [Docker NodeMCU build](https://hub.docker.com/r/marcelstoer/nodemcu-build/) a try.
## Linux Build Environment ### Linux Build Environment
NodeMCU firmware developers commit or contribute to the project on GitHub and might want to build their own full fledged build environment with the complete tool chain. There is a [post in the esp8266.com Wiki](http://www.esp8266.com/wiki/doku.php?id=toolchain#how_to_setup_a_vm_to_host_your_toolchain) that describes this. NodeMCU firmware developers commit or contribute to the project on GitHub and might want to build their own full fledged build environment with the complete tool chain. There is a [post in the esp8266.com Wiki](http://www.esp8266.com/wiki/doku.php?id=toolchain#how_to_setup_a_vm_to_host_your_toolchain) that describes this.
## Build Options
The following sections explain some of the options you have if you want to build your own NodeMCU firmware.
### Select Modules
Disable modules you won't be using to reduce firmware size and free up some RAM. The ESP8266 is quite limited in available RAM and running out of memory can cause a system panic. The *default configuration* is designed to run on all ESP modules including the 512 KB modules like ESP-01 and only includes general purpose interface modules which require at most two GPIO pins.
Edit `app/include/user_modules.h` and comment-out the `#define` statement for modules you don't need. Example:
```c
...
#define LUA_USE_MODULES_MQTT
// #define LUA_USE_MODULES_COAP
// #define LUA_USE_MODULES_U8G
...
```
### TLS/SSL Support
To enable TLS support edit `app/include/user_config.h` and uncomment the following flag:
```c
//#define CLIENT_SSL_ENABLE
```
The complete configuration is stored in `app/include/user_mbedtls.h`. This is the file to edit if you build your own firmware and want to change mbed TLS behavior. See the [`tls` documentation](modules/tls.md) for details.
### Debugging
To enable runtime debug messages to serial console edit `app/include/user_config.h`
```c
#define DEVELOP_VERSION
```
### Set UART Bit Rate
The initial baud rate at boot time is 115200bps. You can change this by
editing `BIT_RATE_DEFAULT` in `app/include/user_config.h`:
```c
#define BIT_RATE_DEFAULT BIT_RATE_115200
```
Note that, by default, the firmware runs an auto-baudrate detection algorithm so that typing a few characters at boot time will cause
the firmware to lock onto that baud rate (between 1200 and 230400).
### Integer build
By default a build will be generated supporting floating-point variables.
To reduce memory size an integer build can be created. You can change this
either by uncommenting `LUA_NUMBER_INTEGRAL` in `app/include/user_config.h`:
```c
#define LUA_NUMBER_INTEGRAL
```
OR by overriding this with the `make` command as it's [done during the CI
build](https://github.com/nodemcu/nodemcu-firmware/blob/master/.travis.yml#L30):
```
make EXTRA_CCFLAGS="-DLUA_NUMBER_INTEGRAL ....
```
### Tag Your Build
Identify your firmware builds by editing `app/include/user_version.h`
```c
#define NODE_VERSION "NodeMCU 2.1.0+myname"
#ifndef BUILD_DATE
#define BUILD_DATE "YYYYMMDD"
#endif
```
### u8g Module Configuration
Display drivers and embedded fonts are compiled into the firmware image based on the settings in `app/include/u8g_config.h`. See the [`u8g` documentation](modules/u8g.md#displays) for details.
### ucg Module Configuration
Display drivers and embedded fonts are compiled into the firmware image based on the settings in `app/include/ucg_config.h`. See the [`ucg` documentation](modules/ucg.md#displays) for details.
# Extension Developer FAQ # Extension Developer FAQ
**# # # Work in Progress # # #** ## Firmware build options
[Building the firmware → Build Options](build.md#build-options) lists a few of the common parameters to customize your firmware *at build time*.
## How does the non-OS SDK structure execution ## How does the non-OS SDK structure execution
......
...@@ -32,8 +32,6 @@ Run the following command to flash an *aggregated* binary as is produced for exa ...@@ -32,8 +32,6 @@ Run the following command to flash an *aggregated* binary as is produced for exa
- 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. - 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.
- 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: - 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` `esptool.py --port <serial-port-of-ESP8266> erase_flash`
- Modules with flash chips larger than 4&nbsp;MByte (e.g. WeMos D1 mini pro) need to be manually configured to at least 1&nbsp;MByte: Firmware image and SDK init data occupy the first MByte, while the remaining 7/15&nbsp;MByte of the flash are used for SPIFFS:
`esptool.py --port <serial-port-of-ESP8266> write_flash -fm <mode> -fs 8m 0x00000 <nodemcu-firmware>.bin`
### NodeMCU Flasher ### NodeMCU Flasher
> A firmware Flash tool for NodeMCU...We are working on next version and will use QT framework. It will be cross platform and open-source. > A firmware Flash tool for NodeMCU...We are working on next version and will use QT framework. It will be cross platform and open-source.
...@@ -102,12 +100,14 @@ Espressif refers to this area as "System Param" and it resides in the last four ...@@ -102,12 +100,14 @@ Espressif refers to this area as "System Param" and it resides in the last four
The default init data is provided as part of the SDK in the file `esp_init_data_default.bin`. NodeMCU will automatically flash this file to the right place on first boot if the sector appears to be empty. The default init data is provided as part of the SDK in the file `esp_init_data_default.bin`. NodeMCU will automatically flash this file to the right place on first boot if the sector appears to be empty.
If you need to customize init data then first download the [Espressif SDK 2.0.0](https://espressif.com/sites/default/files/sdks/esp8266_nonos_sdk_v2.0.0_16_08_10.zip) and extract `esp_init_data_default.bin`. Then flash that file just like you'd flash the firmware. The correct address for the init data depends on the capacity of the flash chip. If you need to customize init data then first download the [Espressif SDK 2.1.0](https://github.com/espressif/ESP8266_NONOS_SDK/archive/v2.1.0.zip) and extract `esp_init_data_default.bin`. Then flash that file just like you'd flash the firmware. The correct address for the init data depends on the capacity of the flash chip.
- `0x7c000` for 512 kB, modules like most ESP-01, -03, -07 etc. - `0x7c000` for 512 kB, modules like most ESP-01, -03, -07 etc.
- `0xfc000` for 1 MB, modules like ESP8285, PSF-A85, some ESP-01, -03 etc. - `0xfc000` for 1 MB, modules like ESP8285, PSF-A85, some ESP-01, -03 etc.
- `0x1fc000` for 2 MB - `0x1fc000` for 2 MB
- `0x3fc000` for 4 MB, modules like ESP-12E, NodeMCU devkit 1.0, WeMos D1 mini - `0x3fc000` for 4 MB, modules like ESP-12E, NodeMCU devkit 1.0, WeMos D1 mini
- `0x7fc000` for 8 MB
- `0xffc000` for 16 MB, modules like WeMos D1 mini pro
See "4.1 Non-FOTA Flash Map" and "6.3 RF Initialization Configuration" of the [ESP8266 Getting Started Guide](https://espressif.com/en/support/explore/get-started/esp8266/getting-started-guide) for details on init data addresses and customization. See "4.1 Non-FOTA Flash Map" and "6.3 RF Initialization Configuration" of the [ESP8266 Getting Started Guide](https://espressif.com/en/support/explore/get-started/esp8266/getting-started-guide) for details on init data addresses and customization.
......
...@@ -29,7 +29,7 @@ Whilst the Lua standard distribution includes a host stand-alone Lua interpreter ...@@ -29,7 +29,7 @@ Whilst the Lua standard distribution includes a host stand-alone Lua interpreter
The ESP8266 was designed and is fabricated in China by [Espressif Systems](http://espressif.com/new-sdk-release/). Espressif have also developed and released a companion software development kit (SDK) to enable developers to build practical IoT applications for the ESP8266. The SDK is made freely available to developers in the form of binary libraries and SDK documentation. However this is in a *closed format*, with no developer access to the source files, so ESP8266 applications *must* rely solely on the SDK API (and the somewhat Spartan SDK API documentation). The ESP8266 was designed and is fabricated in China by [Espressif Systems](http://espressif.com/new-sdk-release/). Espressif have also developed and released a companion software development kit (SDK) to enable developers to build practical IoT applications for the ESP8266. The SDK is made freely available to developers in the form of binary libraries and SDK documentation. However this is in a *closed format*, with no developer access to the source files, so ESP8266 applications *must* rely solely on the SDK API (and the somewhat Spartan SDK API documentation).
The NodeMCU Lua firmware is an ESP8266 application and must therefore be layered over the ESP8266 SDK. However, the hooks and features of Lua enable it to be seamlessly integrated without loosing any of the standard Lua language features. The firmware has replaced some standard Lua modules that don't align well with the SDK structure with ESP8266-specific versions. For example, the standard `io` and `os` libraries don't work, but have been largely replaced by the NodeMCU `node` and `file` libraries. The `debug` and `math` libraries have also been omitted to reduce the runtime footprint. The NodeMCU Lua firmware is an ESP8266 application and must therefore be layered over the ESP8266 SDK. However, the hooks and features of Lua enable it to be seamlessly integrated without loosing any of the standard Lua language features. The firmware has replaced some standard Lua modules that don't align well with the SDK structure with ESP8266-specific versions. For example, the standard `io` and `os` libraries don't work, but have been largely replaced by the NodeMCU `node` and `file` libraries. The `debug` and `math` libraries have also been omitted to reduce the runtime footprint (`modulo` can be done via `%`, `power` via `^`).
NodeMCU Lua is based on [eLua](http://www.eluaproject.net/overview), a fully featured implementation of Lua 5.1 that has been optimized for embedded system development and execution to provide a scripting framework that can be used to deliver useful applications within the limited RAM and Flash memory resources of embedded processors such as the ESP8266. One of the main changes introduced in the eLua fork is to use read-only tables and constants wherever practical for library modules. On a typical build this approach reduces the RAM footprint by some 20-25KB and this makes a Lua implementation for the ESP8266 feasible. This technique is called LTR and this is documented in detail in an eLua technical paper: [Lua Tiny RAM](http://www.eluaproject.net/doc/master/en_arch_ltr.html). NodeMCU Lua is based on [eLua](http://www.eluaproject.net/overview), a fully featured implementation of Lua 5.1 that has been optimized for embedded system development and execution to provide a scripting framework that can be used to deliver useful applications within the limited RAM and Flash memory resources of embedded processors such as the ESP8266. One of the main changes introduced in the eLua fork is to use read-only tables and constants wherever practical for library modules. On a typical build this approach reduces the RAM footprint by some 20-25KB and this makes a Lua implementation for the ESP8266 feasible. This technique is called LTR and this is documented in detail in an eLua technical paper: [Lua Tiny RAM](http://www.eluaproject.net/doc/master/en_arch_ltr.html).
...@@ -340,6 +340,6 @@ Of course you should still use functions to structure your code and encapsulate ...@@ -340,6 +340,6 @@ Of course you should still use functions to structure your code and encapsulate
## Firmware and Lua app development ## Firmware and Lua app development
### How to save memory? ### How to save memory?
* The NodeMCU development team recommends that you consider using a tailored firmware build, which only includes the modules that you plan to use before developing any Lua application. Once you have the ability to make and flash custom builds, the you also have the option of moving time sensitive or logic intensive code into your own custom module. Doing this can save a large amount of RAM as C code can be run directly from Flash memory. If you want an easy-to-use intermediate option then why note try the [cloud based NodeMCU custom build service](http://frightanic.com/NodeMCU-custom-build)?. * The NodeMCU development team recommends that you consider using a tailored firmware build, which only includes the modules that you plan to use before developing any Lua application. Once you have the ability to make and flash custom builds, the you also have the option of moving time sensitive or logic intensive code into your own custom module. Doing this can save a large amount of RAM as C code can be run directly from Flash memory. If you want an easy-to-use intermediate option then why note try the [cloud based NodeMCU custom build service](https://nodemcu-build.com)?.
# ADS1115 Module
| Since | Origin / Contributor | Maintainer | Source |
| :----- | :-------------------- | :---------- | :------ |
| 2017-04-24 | [fetchbot](https://github.com/fetchbot) | [fetchbot](https://github.com/fetchbot) | [ads1115.c](../../../app/modules/ads1115.c)|
This module provides access to the ADS1115 16-Bit analog-to-digital converter.
!!! caution
The **ABSOLUTE MAXIMUM RATINGS** for all analog inputs are `–0.3V to VDD+0.3V` referred to GND.
## ads1115.read()
Gets the result stored in the register of a previously issued conversion, e.g. in continuous mode or with a conversion ready interrupt.
#### Syntax
`volt, volt_dec, adc = ads1115.read()`
#### Parameters
none
#### Returns
- `volt` voltage in mV (see note below)
- `volt_dec` voltage decimal (see note below)
- `adc` raw adc value
!!! note
If using float firmware then `volt` is a floating point number. On an integer firmware, the final value has to be concatenated from `volt` and `volt_dec`.
#### Example
```lua
local id, alert_pin, sda, scl = 0, 7, 6, 5
i2c.setup(id, sda, scl, i2c.SLOW)
ads1115.setup(ads1115.ADDR_GND)
-- continuous mode
ads1115.setting(ads1115.GAIN_6_144V, ads1115.DR_128SPS, ads1115.SINGLE_0, ads1115.CONTINUOUS)
-- read adc result with read()
volt, volt_dec, adc = ads1115.read()
print(volt, volt_dec, adc)
-- comparator
ads1115.setting(ads1115.GAIN_6_144V, ads1115.DR_128SPS, ads1115.SINGLE_0, ads1115.CONTINUOUS, ads1115.COMP_1CONV, 1000, 2000)
local function comparator(level, when)
-- read adc result with read() when threshold reached
volt, volt_dec, adc = ads1115.read()
print(volt, volt_dec, adc)
end
gpio.mode(alert_pin, gpio.INT)
gpio.trig(alert_pin, "both", comparator)
-- read adc result with read()
volt, volt_dec, adc = ads1115.read()
print(volt, volt_dec, adc)
```
## ads1115.setting()
Configuration settings for the ADC.
#### Syntax
`ads1115.setting(GAIN, SAMPLES, CHANNEL, MODE[, CONVERSION_RDY][, COMPARATOR, THRESHOLD_LOW, THRESHOLD_HI])`
#### Parameters
- `GAIN` Programmable gain amplifier
* `ads1115.GAIN_6_144V` 2/3x Gain
* `ads1115.GAIN_4_096V` 1x Gain
* `ads1115.GAIN_2_048V` 2x Gain
* `ads1115.GAIN_1_024V` 4x Gain
* `ads1115.GAIN_0_512V` 8x Gain
* `ads1115.GAIN_0_256V` 16x Gain
- `SAMPLES` Data rate in samples per second
* `ads1115.DR_8SPS`
* `ads1115.DR_16SPS`
* `ads1115.DR_32SPS`
* `ads1115.DR_64SPS`
* `ads1115.DR_128SPS`
* `ads1115.DR_250SPS`
* `ads1115.DR_475SPS`
* `ads1115.DR_860SPS`
- `CHANNEL` Input multiplexer for single-ended or differential measurement
* `ads1115.SINGLE_0` channel 0 to GND
* `ads1115.SINGLE_1` channel 1 to GND
* `ads1115.SINGLE_2` channel 2 to GND
* `ads1115.SINGLE_3` channel 3 to GND
* `ads1115.DIFF_0_1` channel 0 to 1
* `ads1115.DIFF_0_3` channel 0 to 3
* `ads1115.DIFF_1_3` channel 1 to 3
* `ads1115.DIFF_2_3` channel 2 to 3
- `MODE` Device operating mode
* `ads1115.SINGLE_SHOT` single-shot mode
* `ads1115.CONTINUOUS` continuous mode
- `CONVERSION_RDY` Number of conversions after conversion ready asserts (optional)
* `ads1115.CONV_RDY_1`
* `ads1115.CONV_RDY_2`
* `ads1115.CONV_RDY_4`
- `COMPARATOR` Number of conversions after comparator asserts (optional)
* `ads1115.COMP_1CONV`
* `ads1115.COMP_2CONV`
* `ads1115.COMP_4CONV`
- `THRESHOLD_LOW`
* `0` - `+ GAIN_MAX` in mV for single-ended inputs
* `- GAIN_MAX` - `+ GAIN_MAX` in mV for differential inputs
- `THRESHOLD_HI`
* `0` - `+ GAIN_MAX` in mV for single-ended inputs
* `- GAIN_MAX` - `+ GAIN_MAX` in mV for differential inputs
#### Returns
`nil`
#### Example
```lua
local id, sda, scl = 0, 6, 5
i2c.setup(id, sda, scl, i2c.SLOW)
ads1115.setup(ads1115.ADDR_GND)
ads1115.setting(ads1115.GAIN_6_144V, ads1115.DR_128SPS, ads1115.SINGLE_0, ads1115.SINGLE_SHOT)
```
## ads1115.setup()
Initializes the device on the defined I²C device address.
#### Syntax
`ads1115.setup(ADDRESS)`
#### Parameters
- `ADDRESS`
* `ads1115.ADDR_GND`
* `ads1115.ADDR_VDD`
* `ads1115.ADDR_SDA`
* `ads1115.ADDR_SCL`
#### Returns
`nil`
#### Example
```lua
local id, sda, scl = 0, 6, 5
i2c.setup(id, sda, scl, i2c.SLOW)
ads1115.setup(ads1115.ADDR_GND)
```
## ads1115.startread()
Starts the ADC reading for single-shot mode and after the conversion is done it will invoke an optional callback function in which the ADC conversion result can be obtained.
#### Syntax
`ads1115.startread([CALLBACK])`
#### Parameters
- `CALLBACK` callback function which will be invoked after the adc conversion is done
* `function(volt, volt_dec, adc) end`
#### Returns
- `nil`
#### Example
```lua
local id, alert_pin, sda, scl = 0, 7, 6, 5
i2c.setup(id, sda, scl, i2c.SLOW)
ads1115.setup(ads1115.ADDR_GND)
-- single shot
ads1115.setting(ads1115.GAIN_6_144V, ads1115.DR_128SPS, ads1115.SINGLE_0, ads1115.SINGLE_SHOT)
-- start adc conversion and get result in callback after conversion is ready
ads1115.startread(function(volt, volt_dec, adc) print(volt, volt_dec, adc) end)
-- conversion ready
ads1115.setting(ads1115.GAIN_6_144V, ads1115.DR_128SPS, ads1115.SINGLE_0, ads1115.SINGLE_SHOT, ads1115.CONV_RDY_1)
local function conversion_ready(level, when)
volt, volt_dec, adc = ads1115.read()
print(volt, volt_dec, adc)
end
gpio.mode(alert_pin, gpio.INT)
gpio.trig(alert_pin, "down", conversion_ready)
-- start conversion and get result with read() after conversion ready pin asserts
ads1115.startread()
```
...@@ -137,27 +137,20 @@ QNH = bme280.qfe2qnh(P, alt) ...@@ -137,27 +137,20 @@ QNH = bme280.qfe2qnh(P, alt)
print(string.format("QNH=%d.%03d", QNH/1000, QNH%1000)) print(string.format("QNH=%d.%03d", QNH/1000, QNH%1000))
H, T = bme280.humi() H, T = bme280.humi()
if T<0 then
print(string.format("T=-%d.%02d", -T/100, -T%100)) local Tsgn = (T < 0 and -1 or 1); T = Tsgn*T
else print(string.format("T=%s%d.%02d", Tsgn<0 and "-" or "", T/100, T%100))
print(string.format("T=%d.%02d", T/100, T%100))
end
print(string.format("humidity=%d.%03d%%", H/1000, H%1000)) print(string.format("humidity=%d.%03d%%", H/1000, H%1000))
D = bme280.dewpoint(H, T) D = bme280.dewpoint(H, T)
if D<0 then local Dsgn = (D < 0 and -1 or 1); D = Dsgn*D
print(string.format("dew_point=-%d.%02d", -D/100, -D%100)) print(string.format("dew_point=%s%d.%02d", Dsgn<0 and "-" or "", D/100, D%100))
else
print(string.format("dew_point=%d.%02d", D/100, D%100))
end
-- altimeter function - calculate altitude based on current sea level pressure (QNH) and measure pressure -- altimeter function - calculate altitude based on current sea level pressure (QNH) and measure pressure
P = bme280.baro() P = bme280.baro()
curAlt = bme280.altitude(P, QNH) curAlt = bme280.altitude(P, QNH)
if curAlt<0 then local curAltsgn = (curAlt < 0 and -1 or 1); curAlt = curAltsgn*curAlt
print(string.format("altitude=-%d.%02d", -curAlt/100, -curAlt%100)) print(string.format("altitude=%s%d.%02d", curAltsgn<0 and "-" or "", curAlt/100, curAlt%100))
else
print(string.format("altitude=%d.%02d", curAlt/100, curAlt%100))
end
``` ```
Or simpler and more efficient Or simpler and more efficient
...@@ -167,29 +160,20 @@ alt=320 -- altitude of the measurement place ...@@ -167,29 +160,20 @@ alt=320 -- altitude of the measurement place
bme280.init(3, 4) bme280.init(3, 4)
T, P, H, QNH = bme280.read(alt) T, P, H, QNH = bme280.read(alt)
if T<0 then local Tsgn = (T < 0 and -1 or 1); T = Tsgn*T
print(string.format("T=-%d.%02d", -T/100, -T%100)) print(string.format("T=%s%d.%02d", Tsgn<0 and "-" or "", T/100, T%100))
else
print(string.format("T=%d.%02d", T/100, T%100))
end
print(string.format("QFE=%d.%03d", P/1000, P%1000)) print(string.format("QFE=%d.%03d", P/1000, P%1000))
print(string.format("QNH=%d.%03d", QNH/1000, QNH%1000)) print(string.format("QNH=%d.%03d", QNH/1000, QNH%1000))
print(string.format("humidity=%d.%03d%%", H/1000, H%1000)) print(string.format("humidity=%d.%03d%%", H/1000, H%1000))
D = bme280.dewpoint(H, T) D = bme280.dewpoint(H, T)
if D<0 then local Dsgn = (D < 0 and -1 or 1); D = Dsgn*D
print(string.format("dew_point=-%d.%02d", -D/100, -D%100)) print(string.format("dew_point=%s%d.%02d", Dsgn<0 and "-" or "", D/100, D%100))
else
print(string.format("dew_point=%d.%02d", D/100, D%100))
end
-- altimeter function - calculate altitude based on current sea level pressure (QNH) and measure pressure -- altimeter function - calculate altitude based on current sea level pressure (QNH) and measure pressure
P = bme280.baro() P = bme280.baro()
curAlt = bme280.altitude(P, QNH) curAlt = bme280.altitude(P, QNH)
if curAlt<0 then local curAltsgn = (curAlt < 0 and -1 or 1); curAlt = curAltsgn*curAlt
print(string.format("altitude=-%d.%02d", -curAlt/100, -curAlt%100)) print(string.format("altitude=%s%d.%02d", curAltsgn<0 and "-" or "", curAlt/100, curAlt%100))
else
print(string.format("altitude=%d.%02d", curAlt/100, curAlt%100))
end
``` ```
Use `bme280.init(sda, scl, 1, 3, 0, 3, 0, 4)` for "game mode" - Oversampling settings pressure ×4, temperature ×1, humidity ×0, sensor mode: normal mode, inactive duration = 0.5 ms, IIR filter settings filter coefficient 16. Use `bme280.init(sda, scl, 1, 3, 0, 3, 0, 4)` for "game mode" - Oversampling settings pressure ×4, temperature ×1, humidity ×0, sensor mode: normal mode, inactive duration = 0.5 ms, IIR filter settings filter coefficient 16.
...@@ -199,11 +183,8 @@ Example of readout in forced mode (asynchronous) ...@@ -199,11 +183,8 @@ Example of readout in forced mode (asynchronous)
bme280.init(3, 4, nil, nil, nil, 0) -- initialize to sleep mode bme280.init(3, 4, nil, nil, nil, 0) -- initialize to sleep mode
bme280.startreadout(0, function () bme280.startreadout(0, function ()
T, P = bme280.read() T, P = bme280.read()
if T<0 then local Tsgn = (T < 0 and -1 or 1); T = Tsgn*T
print(string.format("T=-%d.%02d", -T/100, -T%100)) print(string.format("T=%s%d.%02d", Tsgn<0 and "-" or "", T/100, T%100))
else
print(string.format("T=%d.%02d", T/100, T%100))
end
end) end)
``` ```
......
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