diff --git a/.travis.yml b/.travis.yml index 45aedeab..894f2fd0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,13 +8,21 @@ install: script: - make all - cd bin/ -- file_name="nodemcu-firmware_v${TRAVIS_TAG}.${TRAVIS_BUILD_NUMBER}.bin" -- srec_cat -output ${file_name} -binary 0x00000.bin -binary -fill 0xff 0x00000 0x10000 0x10000.bin -binary -offset 0x10000 +- file_name_float="nodemcu_float_${TRAVIS_TAG}.bin" +- srec_cat -output ${file_name_float} -binary 0x00000.bin -binary -fill 0xff 0x00000 0x10000 0x10000.bin -binary -offset 0x10000 +- cd ../ +- make clean +- make EXTRA_CCFLAGS="-DLUA_NUMBER_INTEGRAL" +- cd bin/ +- file_name_integer="nodemcu_integer_${TRAVIS_TAG}.bin" +- srec_cat -output ${file_name_integer} -binary 0x00000.bin -binary -fill 0xff 0x00000 0x10000 0x10000.bin -binary -offset 0x10000 deploy: provider: releases api_key: secure: Swecz5lWvsuSbchSbVQ1rmCPN9nQIN5p/HlZNIEdEgAgnoLcJxRV4P8poVTB37jiA8Pck+8x2nWXpg74Rqik0i3KlPNvDfg5o4rIazWLNs4bc1Tbcpt44XAzFKKLYnDnWQUGcqjk7BcAXuNAF2X/fPBCVhFbHVg3Z7cDb32RsNw= - file: "$TRAVIS_BUILD_DIR/bin/${file_name}" + file: + - "$TRAVIS_BUILD_DIR/bin/${file_name_float}" + - "$TRAVIS_BUILD_DIR/bin/${file_name_integer}" skip_cleanup: true on: tags: true diff --git a/README.md b/README.md index effb6803..d343075c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ # **NodeMCU** # version 0.9.5 -[![Join the chat at https://gitter.im/nodemcu/nodemcu-firmware](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/nodemcu/nodemcu-firmware?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status](https://travis-ci.org/nodemcu/nodemcu-firmware.svg)](https://travis-ci.org/nodemcu/nodemcu-firmware) +[![Join the chat at https://gitter.im/nodemcu/nodemcu-firmware](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/nodemcu/nodemcu-firmware?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![Build Status](https://travis-ci.org/nodemcu/nodemcu-firmware.svg)](https://travis-ci.org/nodemcu/nodemcu-firmware) [![Download](https://img.shields.io/badge/download-~400k-orange.svg)](https://github.com/nodemcu/nodemcu-firmware/releases/latest) ###A lua based firmware for wifi-soc esp8266 Build on [ESP8266 sdk 0.9.5](http://bbs.espressif.com/viewtopic.php?f=5&t=154)
@@ -33,6 +34,23 @@ Tencent QQ group: 309957875
- cross compiler (done) # Change log +2015-03-15
+bugs fixed: #239, #273.
+reduce coap module memory usage, add coap module to default built. + +2015-03-11
+fix bugs of spiffs.
+build both float and integer version [latest releases](https://github.com/nodemcu/nodemcu-firmware/releases/latest).
+fix tmr.time().
+fix memory leak when DNS fail. + +2015-03-10
+update to the recent spiffs.
+add file.fsinfo() api, usage: remain, used, total = file.fsinfo().
+add Travis CI. please download the latest firmware from [releases](https://github.com/nodemcu/nodemcu-firmware/releases).
+add math lib, partial api work.
+u8g module, ws2812 module default enabled in dev-branch build. + 2015-02-13
add node.compile() api to compile lua text file into lua bytecode file.
this will reduce memory usage noticeably when require modules into NodeMCU.
@@ -400,3 +418,25 @@ They'll be available as `u8g.` in Lua. -- first LED green, second LED white ws2812.writergb(4, string.char(0, 255, 0, 255, 255, 255)) ``` + +####coap client and server +```lua +-- use copper addon for firefox +cs=coap.Server() +cs:listen(5683) + +myvar=1 +cs:var("myvar") -- get coap://192.168.18.103:5683/v1/v/myvar will return the value of myvar: 1 + +-- function should tack one string, return one string. +function myfun(payload) + print("myfun called") + respond = "hello" + return respond +end +cs:func("myfun") -- post coap://192.168.18.103:5683/v1/f/myfun will call myfun + +cc = coap.Client() +cc:get(coap.CON, "coap://192.168.18.100:5683/.well-known/core") +cc:post(coap.NON, "coap://192.168.18.100:5683/", "Hello") +``` \ No newline at end of file diff --git a/app/coap/coap.c b/app/coap/coap.c index 6dded680..af431d76 100644 --- a/app/coap/coap.c +++ b/app/coap/coap.c @@ -76,7 +76,7 @@ int coap_parseToken(coap_buffer_t *tokbuf, const coap_header_t *hdr, const uint8 else if (hdr->tkl <= 8) { - if (4 + hdr->tkl > buflen) + if (4U + hdr->tkl > buflen) return COAP_ERR_TOKEN_TOO_SHORT; // tok bigger than packet tokbuf->p = buf+4; // past header tokbuf->len = hdr->tkl; @@ -93,7 +93,7 @@ int coap_buildToken(const coap_buffer_t *tokbuf, const coap_header_t *hdr, uint8 { // inject token uint8_t *p; - if (buflen < 4 + hdr->tkl) + if (buflen < (4U + hdr->tkl)) return COAP_ERR_BUFFER_TOO_SMALL; p = buf + 4; if ((hdr->tkl > 0) && (hdr->tkl != tokbuf->len)) @@ -102,7 +102,7 @@ int coap_buildToken(const coap_buffer_t *tokbuf, const coap_header_t *hdr, uint8 if (hdr->tkl > 0) c_memcpy(p, tokbuf->p, hdr->tkl); - // http://tools.ietf.org/html/draft-ietf-core-coap-18#section-3.1 + // http://tools.ietf.org/html/rfc7252#section-3.1 // inject options return hdr->tkl; } @@ -111,8 +111,8 @@ int coap_buildToken(const coap_buffer_t *tokbuf, const coap_header_t *hdr, uint8 int coap_parseOption(coap_option_t *option, uint16_t *running_delta, const uint8_t **buf, size_t buflen) { const uint8_t *p = *buf; - uint16_t len, delta; uint8_t headlen = 1; + uint16_t len, delta; if (buflen < headlen) // too small return COAP_ERR_OPTION_TOO_SHORT_FOR_HEADER; @@ -179,14 +179,14 @@ int coap_parseOption(coap_option_t *option, uint16_t *running_delta, const uint8 return 0; } -// http://tools.ietf.org/html/draft-ietf-core-coap-18#section-3.1 +// http://tools.ietf.org/html/rfc7252#section-3.1 int coap_parseOptionsAndPayload(coap_option_t *options, uint8_t *numOptions, coap_buffer_t *payload, const coap_header_t *hdr, const uint8_t *buf, size_t buflen) { size_t optionIndex = 0; + uint16_t delta = 0; const uint8_t *p = buf + 4 + hdr->tkl; const uint8_t *end = buf + buflen; int rc; - uint16_t delta = 0; if (p > end) return COAP_ERR_OPTION_OVERRUNS_PACKET; // out of bounds @@ -215,7 +215,7 @@ int coap_parseOptionsAndPayload(coap_option_t *options, uint8_t *numOptions, coa return 0; } -int coap_buildOptionHeader(unsigned short optDelta, size_t length, uint8_t *buf, size_t buflen) +int coap_buildOptionHeader(uint32_t optDelta, size_t length, uint8_t *buf, size_t buflen) { int n = 0; uint8_t *p = buf; @@ -298,7 +298,7 @@ int coap_parse(coap_packet_t *pkt, const uint8_t *buf, size_t buflen) } // options are always stored consecutively, so can return a block with same option num -const coap_option_t * coap_findOptions(const coap_packet_t *pkt, uint8_t num, uint8_t *count) +const coap_option_t *coap_findOptions(const coap_packet_t *pkt, uint8_t num, uint8_t *count) { // FIXME, options is always sorted, can find faster than this size_t i; @@ -352,7 +352,7 @@ int coap_build(uint8_t *buf, size_t *buflen, const coap_packet_t *pkt) uint16_t optDelta = 0; int rc = 0; - if (p-buf > *buflen) + if (((size_t)(p-buf)) > *buflen) return COAP_ERR_BUFFER_TOO_SMALL; optDelta = pkt->opts[i].num - running_delta; @@ -381,17 +381,17 @@ int coap_build(uint8_t *buf, size_t *buflen, const coap_packet_t *pkt) return 0; } -void coap_option_nibble(uint16_t value, uint8_t *nibble) +void coap_option_nibble(uint32_t value, uint8_t *nibble) { if (value<13) { *nibble = (0xFF & value); } else - if (((uint32_t)value)<=0xFF+13) + if (value<=0xFF+13) { *nibble = 13; - } else if (((uint32_t)value) -269 <=0xFFFF) + } else if (value<=0xFFFF+269) { *nibble = 14; } @@ -405,7 +405,7 @@ int coap_make_response(coap_rw_buffer_t *scratch, coap_packet_t *pkt, const uint pkt->hdr.code = rspcode; pkt->hdr.id[0] = msgid_hi; pkt->hdr.id[1] = msgid_lo; - pkt->numopts = 0; + pkt->numopts = 1; // need token in response if (tok) { @@ -442,7 +442,7 @@ unsigned int coap_encode_var_bytes(unsigned char *buf, unsigned int val) { return n; } -static uint8_t _token_data[4]={'n','o','d','e'}; +static uint8_t _token_data[4] = {'n','o','d','e'}; coap_buffer_t the_token = { _token_data, 4 }; static unsigned short message_id; diff --git a/app/coap/coap.h b/app/coap/coap.h index 1c8827a6..3862f57b 100644 --- a/app/coap/coap.h +++ b/app/coap/coap.h @@ -18,13 +18,15 @@ extern "C" { #define COAP_RESPONSE_CLASS(C) (((C) >> 5) & 0xFF) -// http://tools.ietf.org/html/draft-ietf-core-coap-18#section-3 +//http://tools.ietf.org/html/rfc7252#section-3 typedef struct { - uint8_t ver; - uint8_t t; - uint8_t tkl; - uint8_t code; + uint8_t ver; /* CoAP version number */ + uint8_t t; /* CoAP Message Type */ + uint8_t tkl; /* Token length: indicates length of the Token field */ + uint8_t code; /* CoAP status code. Can be request (0.xx), success reponse (2.xx), + * client error response (4.xx), or rever error response (5.xx) + * For possible values, see http://tools.ietf.org/html/rfc7252#section-12.1 */ uint8_t id[2]; } coap_header_t; @@ -42,23 +44,24 @@ typedef struct typedef struct { - uint8_t num; - coap_buffer_t buf; + uint8_t num; /* Option number. See http://tools.ietf.org/html/rfc7252#section-5.10 */ + coap_buffer_t buf; /* Option value */ } coap_option_t; typedef struct { - coap_header_t hdr; - coap_buffer_t tok; - uint8_t numopts; - coap_option_t opts[MAXOPT]; - coap_buffer_t payload; - coap_rw_buffer_t scratch; // scratch->p = malloc(...) , and free it when done. + coap_header_t hdr; /* Header of the packet */ + coap_buffer_t tok; /* Token value, size as specified by hdr.tkl */ + uint8_t numopts; /* Number of options */ + coap_option_t opts[MAXOPT]; /* Options of the packet. For possible entries see + * http://tools.ietf.org/html/rfc7252#section-5.10 */ + coap_buffer_t payload; /* Payload carried by the packet */ + coap_rw_buffer_t content; // content->p = malloc(...) , and free it when done. } coap_packet_t; ///////////////////////////////////////// -//http://tools.ietf.org/html/draft-ietf-core-coap-18#section-12.2 +//http://tools.ietf.org/html/rfc7252#section-12.2 typedef enum { COAP_OPTION_IF_MATCH = 1, @@ -78,7 +81,7 @@ typedef enum COAP_OPTION_PROXY_SCHEME = 39 } coap_option_num_t; -//http://tools.ietf.org/html/draft-ietf-core-coap-18#section-12.1.1 +//http://tools.ietf.org/html/rfc7252#section-12.1.1 typedef enum { COAP_METHOD_GET = 1, @@ -87,7 +90,7 @@ typedef enum COAP_METHOD_DELETE = 4 } coap_method_t; -//http://tools.ietf.org/html/draft-ietf-core-coap-18#section-12.1.1 +//http://tools.ietf.org/html/rfc7252#section-12.1.1 typedef enum { COAP_TYPE_CON = 0, @@ -96,8 +99,8 @@ typedef enum COAP_TYPE_RESET = 3 } coap_msgtype_t; -//http://tools.ietf.org/html/draft-ietf-core-coap-18#section-5.2 -//http://tools.ietf.org/html/draft-ietf-core-coap-18#section-12.1.2 +//http://tools.ietf.org/html/rfc7252#section-5.2 +//http://tools.ietf.org/html/rfc7252#section-12.1.2 #define MAKE_RSPCODE(clas, det) ((clas << 5) | (det)) typedef enum { @@ -107,7 +110,7 @@ typedef enum COAP_RSPCODE_CHANGED = MAKE_RSPCODE(2, 4) } coap_responsecode_t; -//http://tools.ietf.org/html/draft-ietf-core-coap-18#section-12.3 +//http://tools.ietf.org/html/rfc7252#section-12.3 typedef enum { COAP_CONTENTTYPE_NONE = -1, // bodge to allow us not to send option block @@ -155,15 +158,21 @@ struct coap_luser_entry{ coap_luser_entry *next; }; -struct coap_endpoint_t -{ - coap_method_t method; - coap_endpoint_func handler; - const coap_endpoint_path_t *path; - const char *core_attr; - coap_luser_entry *user_entry; +struct coap_endpoint_t{ + coap_method_t method; /* (i.e. POST, PUT or GET) */ + coap_endpoint_func handler; /* callback function which handles this + * type of endpoint (and calls + * coap_make_response() at some point) */ + const coap_endpoint_path_t *path; /* path towards a resource (i.e. foo/bar/) */ + const char *core_attr; /* the 'ct' attribute, as defined in RFC7252, section 7.2.1.: + * "The Content-Format code "ct" attribute + * provides a hint about the + * Content-Formats this resource returns." + * (Section 12.3. lists possible ct values.) */ + coap_luser_entry *user_entry; }; + /////////////////////// void coap_dumpPacket(coap_packet_t *pkt); int coap_parse(coap_packet_t *pkt, const uint8_t *buf, size_t buflen); @@ -173,11 +182,11 @@ int coap_build(uint8_t *buf, size_t *buflen, const coap_packet_t *pkt); void coap_dump(const uint8_t *buf, size_t buflen, bool bare); int coap_make_response(coap_rw_buffer_t *scratch, coap_packet_t *pkt, const uint8_t *content, size_t content_len, uint8_t msgid_hi, uint8_t msgid_lo, const coap_buffer_t* tok, coap_responsecode_t rspcode, coap_content_type_t content_type); int coap_handle_req(coap_rw_buffer_t *scratch, const coap_packet_t *inpkt, coap_packet_t *outpkt); -void coap_option_nibble(uint16_t value, uint8_t *nibble); +void coap_option_nibble(uint32_t value, uint8_t *nibble); void coap_setup(void); void endpoint_setup(void); -int coap_buildOptionHeader(unsigned short optDelta, size_t length, uint8_t *buf, size_t buflen); +int coap_buildOptionHeader(uint32_t optDelta, size_t length, uint8_t *buf, size_t buflen); #ifdef __cplusplus } diff --git a/app/coap/coap_client.c b/app/coap/coap_client.c index 37a1a5f0..63caf074 100644 --- a/app/coap/coap_client.c +++ b/app/coap/coap_client.c @@ -11,6 +11,8 @@ void coap_client_response_handler(char *data, unsigned short len, unsigned short { NODE_DBG("coap_client_response_handler is called.\n"); coap_packet_t pkt; + pkt.content.p = NULL; + pkt.content.len = 0; int rc; if (0 != (rc = coap_parse(&pkt, data, len))){ diff --git a/app/coap/coap_client.h b/app/coap/coap_client.h index 5559adfe..7f0a457b 100644 --- a/app/coap/coap_client.h +++ b/app/coap/coap_client.h @@ -1,11 +1,11 @@ -#ifndef _COAP_SERVER_H -#define _COAP_SERVER_H 1 +#ifndef _COAP_CLIENT_H +#define _COAP_CLIENT_H 1 #ifdef __cplusplus extern "C" { #endif -size_t coap_server_respond(char *data, unsigned short len, unsigned short size); +void coap_client_response_handler(char *data, unsigned short len, unsigned short size, const uint32_t ip, const uint32_t port); #ifdef __cplusplus } diff --git a/app/coap/coap_server.c b/app/coap/coap_server.c index d87bd8f7..cbec01f9 100644 --- a/app/coap/coap_server.c +++ b/app/coap/coap_server.c @@ -1,53 +1,60 @@ #include "user_config.h" #include "c_types.h" +#include "c_stdlib.h" #include "coap.h" -size_t coap_server_respond(char *data, unsigned short len, unsigned short size) +size_t coap_server_respond(char *req, unsigned short reqlen, char *rsp, unsigned short rsplen) { NODE_DBG("coap_server_respond is called.\n"); - if(len>size){ - NODE_DBG("len:%d, size:%d\n",len,size); - return 0; - } - size_t rsplen = size; + size_t rlen = rsplen; coap_packet_t pkt; + pkt.content.p = NULL; + pkt.content.len = 0; uint8_t scratch_raw[4]; coap_rw_buffer_t scratch_buf = {scratch_raw, sizeof(scratch_raw)}; int rc; #ifdef COAP_DEBUG NODE_DBG("Received: "); - coap_dump(data, len, true); + coap_dump(req, reqlen, true); NODE_DBG("\n"); #endif - if (0 != (rc = coap_parse(&pkt, data, len))){ + if (0 != (rc = coap_parse(&pkt, req, reqlen))){ NODE_DBG("Bad packet rc=%d\n", rc); return 0; } else { coap_packet_t rsppkt; + rsppkt.content.p = NULL; + rsppkt.content.len = 0; #ifdef COAP_DEBUG coap_dumpPacket(&pkt); #endif coap_handle_req(&scratch_buf, &pkt, &rsppkt); - if (0 != (rc = coap_build(data, &rsplen, &rsppkt))){ + if (0 != (rc = coap_build(rsp, &rlen, &rsppkt))){ NODE_DBG("coap_build failed rc=%d\n", rc); - return 0; + // return 0; + rlen = 0; } else { #ifdef COAP_DEBUG NODE_DBG("Responding: "); - coap_dump(data, rsplen, true); + coap_dump(rsp, rlen, true); NODE_DBG("\n"); #endif #ifdef COAP_DEBUG coap_dumpPacket(&rsppkt); #endif } - return rsplen; + if(rsppkt.content.p){ + c_free(rsppkt.content.p); + rsppkt.content.p = NULL; + rsppkt.content.len = 0; + } + return rlen; } } diff --git a/app/coap/coap_server.h b/app/coap/coap_server.h index 5559adfe..7eb023ec 100644 --- a/app/coap/coap_server.h +++ b/app/coap/coap_server.h @@ -5,7 +5,7 @@ extern "C" { #endif -size_t coap_server_respond(char *data, unsigned short len, unsigned short size); +size_t coap_server_respond(char *req, unsigned short reqlen, char *rsp, unsigned short rsplen); #ifdef __cplusplus } diff --git a/app/coap/endpoints.c b/app/coap/endpoints.c index a3d2d9d6..1d7d48bb 100644 --- a/app/coap/endpoints.c +++ b/app/coap/endpoints.c @@ -1,5 +1,6 @@ #include "c_stdio.h" #include "c_string.h" +#include "c_stdlib.h" #include "coap.h" #include "lua.h" @@ -8,20 +9,24 @@ #include "os_type.h" -static char rsp[MAX_PAYLOAD_SIZE] = ""; -const uint16_t rsplen = MAX_PAYLOAD_SIZE; -void build_well_known_rsp(void); +void build_well_known_rsp(char *rsp, uint16_t rsplen); void endpoint_setup(void) { coap_setup(); - build_well_known_rsp(); } static const coap_endpoint_path_t path_well_known_core = {2, {".well-known", "core"}}; static int handle_get_well_known_core(const coap_endpoint_t *ep, coap_rw_buffer_t *scratch, const coap_packet_t *inpkt, coap_packet_t *outpkt, uint8_t id_hi, uint8_t id_lo) { - return coap_make_response(scratch, outpkt, (const uint8_t *)rsp, c_strlen(rsp), id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_CONTENT, COAP_CONTENTTYPE_APPLICATION_LINKFORMAT); + outpkt->content.p = (uint8_t *)c_zalloc(MAX_PAYLOAD_SIZE); // this should be free-ed when outpkt is built in coap_server_respond() + if(outpkt->content.p == NULL){ + NODE_DBG("not enough memory\n"); + return COAP_ERR_BUFFER_TOO_SMALL; + } + outpkt->content.len = MAX_PAYLOAD_SIZE; + build_well_known_rsp(outpkt->content.p, outpkt->content.len); + return coap_make_response(scratch, outpkt, (const uint8_t *)outpkt->content.p, c_strlen(outpkt->content.p), id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_CONTENT, COAP_CONTENTTYPE_APPLICATION_LINKFORMAT); } static const coap_endpoint_path_t path_variable = {2, {"v1", "v"}}; @@ -29,6 +34,7 @@ static int handle_get_variable(const coap_endpoint_t *ep, coap_rw_buffer_t *scra { const coap_option_t *opt; uint8_t count; + int n; if (NULL != (opt = coap_findOptions(inpkt, COAP_OPTION_URI_PATH, &count))) { if ((count != ep->path->count ) && (count != ep->path->count + 1)) // +1 for /f/[function], /v/[variable] @@ -54,14 +60,15 @@ static int handle_get_variable(const coap_endpoint_t *ep, coap_rw_buffer_t *scra return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_NOT_FOUND, COAP_CONTENTTYPE_NONE); if(c_strlen(h->name)) { + n = lua_gettop(h->L); lua_getglobal(h->L, h->name); if (!lua_isnumber(h->L, -1)) { NODE_DBG ("should be a number.\n"); - lua_pop(h->L, 1); + lua_settop(h->L, n); return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_NOT_FOUND, COAP_CONTENTTYPE_NONE); } else { const char *res = lua_tostring(h->L,-1); - lua_pop(h->L, 1); + lua_settop(h->L, n); return coap_make_response(scratch, outpkt, (const uint8_t *)res, c_strlen(res), id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_CONTENT, COAP_CONTENTTYPE_TEXT_PLAIN); } } @@ -84,6 +91,7 @@ static int handle_post_function(const coap_endpoint_t *ep, coap_rw_buffer_t *scr { const coap_option_t *opt; uint8_t count; + int n; if (NULL != (opt = coap_findOptions(inpkt, COAP_OPTION_URI_PATH, &count))) { if ((count != ep->path->count ) && (count != ep->path->count + 1)) // +1 for /f/[function], /v/[variable] @@ -111,10 +119,11 @@ static int handle_post_function(const coap_endpoint_t *ep, coap_rw_buffer_t *scr if(c_strlen(h->name)) { + n = lua_gettop(h->L); lua_getglobal(h->L, h->name); if (lua_type(h->L, -1) != LUA_TFUNCTION) { NODE_DBG ("should be a function\n"); - lua_pop(h->L, 1); + lua_settop(h->L, n); return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_NOT_FOUND, COAP_CONTENTTYPE_NONE); } else { lua_pushlstring(h->L, inpkt->payload.p, inpkt->payload.len); // make sure payload.p is filled with '\0' after payload.len, or use lua_pushlstring @@ -125,14 +134,17 @@ static int handle_post_function(const coap_endpoint_t *ep, coap_rw_buffer_t *scr size_t len = 0; const char *ret = luaL_checklstring( h->L, -1, &len ); if(len > MAX_PAYLOAD_SIZE){ + lua_settop(h->L, n); luaL_error( h->L, "return string:tok, COAP_RSPCODE_NOT_FOUND, COAP_CONTENTTYPE_NONE); } NODE_DBG((char *)ret); NODE_DBG("\n"); + lua_settop(h->L, n); return coap_make_response(scratch, outpkt, ret, len, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_CONTENT, COAP_CONTENTTYPE_TEXT_PLAIN); } } else { + lua_settop(h->L, n); return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_CONTENT, COAP_CONTENTTYPE_TEXT_PLAIN); } } @@ -204,13 +216,13 @@ const coap_endpoint_t endpoints[] = {(coap_method_t)0, NULL, NULL, NULL, NULL} }; -void build_well_known_rsp(void) +void build_well_known_rsp(char *rsp, uint16_t rsplen) { const coap_endpoint_t *ep = endpoints; int i; uint16_t len = rsplen; - c_memset(rsp, 0, sizeof(rsp)); + c_memset(rsp, 0, len); len--; // Null-terminated string diff --git a/app/coap/pdu.c b/app/coap/pdu.c index 8313146c..e32f028d 100644 --- a/app/coap/pdu.c +++ b/app/coap/pdu.c @@ -24,6 +24,8 @@ coap_pdu_t * coap_new_pdu(void) { c_free(pdu); return NULL; } + pdu->pkt->content.p = NULL; + pdu->pkt->content.len = 0; pdu->msg.p = (uint8_t *)c_zalloc(MAX_REQUEST_SIZE+1); // +1 for string '\0' if(!pdu->msg.p){ diff --git a/app/include/user_config.h b/app/include/user_config.h index c1a9805c..a91aa179 100644 --- a/app/include/user_config.h +++ b/app/include/user_config.h @@ -10,6 +10,7 @@ // #define FLASH_8M // #define FLASH_16M #define FLASH_AUTOSIZE +#define FLASH_SAFE_API // #define DEVELOP_VERSION #define FULL_VERSION_FOR_USER diff --git a/app/include/user_modules.h b/app/include/user_modules.h index 0f6b65f8..103b004f 100644 --- a/app/include/user_modules.h +++ b/app/include/user_modules.h @@ -27,7 +27,7 @@ #define LUA_USE_MODULES_OW #define LUA_USE_MODULES_BIT #define LUA_USE_MODULES_MQTT -#define LUA_USE_MODULES_COAP // need about 4k more ram for now +#define LUA_USE_MODULES_COAP #define LUA_USE_MODULES_U8G #define LUA_USE_MODULES_WS2812 #endif /* LUA_USE_MODULES */ diff --git a/app/include/user_version.h b/app/include/user_version.h index 9d9fc7c3..aeb7d0db 100644 --- a/app/include/user_version.h +++ b/app/include/user_version.h @@ -7,6 +7,6 @@ #define NODE_VERSION_INTERNAL 0U #define NODE_VERSION "NodeMCU 0.9.6" -#define BUILD_DATE "build 20150216" +#define BUILD_DATE "build 20150315" #endif /* __USER_VERSION_H__ */ diff --git a/app/libc/c_stdio.c b/app/libc/c_stdio.c index 03e64581..a8e0956f 100644 --- a/app/libc/c_stdio.c +++ b/app/libc/c_stdio.c @@ -63,7 +63,7 @@ int c_stderr = 1001; #define ENDIAN_LITTLE 1234 #define ENDIAN_BIG 4321 #define ENDIAN_PDP 3412 -#define ENDIAN ENDIAN_BIG +#define ENDIAN ENDIAN_LITTLE /* $Id: strichr.c,v 1.1.1.1 2006/08/23 17:03:06 pefo Exp $ */ diff --git a/app/modules/coap.c b/app/modules/coap.c index 2afb9b54..8e1ba6c0 100644 --- a/app/modules/coap.c +++ b/app/modules/coap.c @@ -37,20 +37,20 @@ static void coap_received(void *arg, char *pdata, unsigned short len) struct espconn *pesp_conn = arg; lcoap_userdata *cud = (lcoap_userdata *)pesp_conn->reverse; - static uint8_t buf[MAX_MESSAGE_SIZE+1] = {0}; // +1 for string '\0' + // static uint8_t buf[MAX_MESSAGE_SIZE+1] = {0}; // +1 for string '\0' + uint8_t buf[MAX_MESSAGE_SIZE+1] = {0}; // +1 for string '\0' c_memset(buf, 0, sizeof(buf)); // wipe prev data if (len > MAX_MESSAGE_SIZE) { NODE_DBG("Request Entity Too Large.\n"); // NOTE: should response 4.13 to client... return; - } else { - c_memcpy(buf, pdata, len); } + // c_memcpy(buf, pdata, len); - size_t rsplen = coap_server_respond(buf, len, MAX_MESSAGE_SIZE+1); + size_t rsplen = coap_server_respond(pdata, len, buf, MAX_MESSAGE_SIZE+1); espconn_sent(pesp_conn, (unsigned char *)buf, rsplen); - c_memset(buf, 0, sizeof(buf)); + // c_memset(buf, 0, sizeof(buf)); } static void coap_sent(void *arg) @@ -227,26 +227,19 @@ static void coap_response_handler(void *arg, char *pdata, unsigned short len) struct espconn *pesp_conn = arg; coap_packet_t pkt; - static uint8_t buf[MAX_MESSAGE_SIZE+1] = {0}; // +1 for string '\0' + pkt.content.p = NULL; + pkt.content.len = 0; + // static uint8_t buf[MAX_MESSAGE_SIZE+1] = {0}; // +1 for string '\0' + uint8_t buf[MAX_MESSAGE_SIZE+1] = {0}; // +1 for string '\0' c_memset(buf, 0, sizeof(buf)); // wipe prev data - static int n = 0; int rc; - if ((len == 1460) && (1460 <= MAX_MESSAGE_SIZE)){ - c_memcpy(buf, pdata, len); // max length is 1460, another part of data is coming in next callback - n = len; + if( len > MAX_MESSAGE_SIZE ) + { + NODE_DBG("Request Entity Too Large.\n"); // NOTE: should response 4.13 to client... return; - } else { - if( len > MAX_MESSAGE_SIZE ) - { - NODE_DBG("Request Entity Too Large.\n"); // NOTE: should response 4.13 to client... - c_memset(buf, 0, sizeof(buf)); // wipe prev data - n = 0; - return; - } - c_memcpy(buf + n, pdata, len); - len += n; // more than 1460 } + c_memcpy(buf, pdata, len); if (0 != (rc = coap_parse(&pkt, buf, len))){ NODE_DBG("Bad packet rc=%d\n", rc); @@ -306,8 +299,7 @@ end: if(pesp_conn->proto.udp->remote_port || pesp_conn->proto.udp->local_port) espconn_delete(pesp_conn); } - c_memset(buf, 0, sizeof(buf)); - n = 0; + // c_memset(buf, 0, sizeof(buf)); } // Lua: client:request( [CON], uri, [payload] ) @@ -431,7 +423,6 @@ static int coap_request( lua_State* L, coap_method_t m ) extern coap_luser_entry *variable_entry; extern coap_luser_entry *function_entry; -extern void build_well_known_rsp(void); // Lua: coap:var/func( string ) static int coap_regist( lua_State* L, const char* mt, int isvar ) { @@ -466,8 +457,6 @@ static int coap_regist( lua_State* L, const char* mt, int isvar ) h->L = L; h->name = name; - build_well_known_rsp(); // rebuild .well-known - NODE_DBG("coap_regist is called.\n"); return 0; } diff --git a/app/modules/file.c b/app/modules/file.c index cf10a3a7..117ea128 100644 --- a/app/modules/file.c +++ b/app/modules/file.c @@ -175,6 +175,22 @@ static int file_rename( lua_State* L ) return 1; } +// Lua: fsinfo() +static int file_fsinfo( lua_State* L ) +{ + uint32_t total, used; + SPIFFS_info(&fs, &total, &used); + NODE_DBG("total: %d, used:%d\n", total, used); + if(total>0x7FFFFFFF || used>0x7FFFFFFF || used > total) + { + return luaL_error(L, "file system error");; + } + lua_pushinteger(L, total-used); + lua_pushinteger(L, used); + lua_pushinteger(L, total); + return 3; +} + #endif // g_read() @@ -308,6 +324,7 @@ const LUA_REG_TYPE file_map[] = { LSTRKEY( "flush" ), LFUNCVAL( file_flush ) }, // { LSTRKEY( "check" ), LFUNCVAL( file_check ) }, { LSTRKEY( "rename" ), LFUNCVAL( file_rename ) }, + { LSTRKEY( "fsinfo" ), LFUNCVAL( file_fsinfo ) }, #endif #if LUA_OPTIMIZE_MEMORY > 0 diff --git a/app/modules/net.c b/app/modules/net.c index 5b9dba9d..7fd785ab 100644 --- a/app/modules/net.c +++ b/app/modules/net.c @@ -200,10 +200,15 @@ static void net_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) return; } + if(nud->self_ref == LUA_NOREF){ + NODE_DBG("self_ref null.\n"); + return; + } + if(ipaddr == NULL) { NODE_ERR( "DNS Fail!\n" ); - return; + goto end; } // ipaddr->addr is a uint32_t ip @@ -214,16 +219,12 @@ static void net_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) c_sprintf(ip_str, IPSTR, IP2STR(&(ipaddr->addr))); } - if(nud->self_ref == LUA_NOREF){ - NODE_DBG("self_ref null.\n"); - return; - } - lua_rawgeti(gL, LUA_REGISTRYINDEX, nud->cb_dns_found_ref); // the callback function lua_rawgeti(gL, LUA_REGISTRYINDEX, nud->self_ref); // pass the userdata(conn) to callback func in lua lua_pushstring(gL, ip_str); // the ip para lua_call(gL, 2, 0); +end: if((pesp_conn->type == ESPCONN_TCP && pesp_conn->proto.tcp->remote_port == 0) || (pesp_conn->type == ESPCONN_UDP && pesp_conn->proto.udp->remote_port == 0) ){ lua_gc(gL, LUA_GCSTOP, 0); @@ -597,12 +598,22 @@ static void socket_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) NODE_DBG("pesp_conn null.\n"); return; } - + lnet_userdata *nud = (lnet_userdata *)pesp_conn->reverse; + if(nud == NULL) + return; + if(gL == NULL) + return; if(ipaddr == NULL) { dns_reconn_count++; if( dns_reconn_count >= 5 ){ NODE_ERR( "DNS Fail!\n" ); + lua_gc(gL, LUA_GCSTOP, 0); + if(nud->self_ref != LUA_NOREF){ + luaL_unref(gL, LUA_REGISTRYINDEX, nud->self_ref); + nud->self_ref = LUA_NOREF; // unref this, and the net.socket userdata will delete it self + } + lua_gc(gL, LUA_GCRESTART, 0); return; } NODE_ERR( "DNS retry %d!\n", dns_reconn_count ); diff --git a/app/modules/node.c b/app/modules/node.c index 030ef512..7d69dfda 100644 --- a/app/modules/node.c +++ b/app/modules/node.c @@ -80,9 +80,13 @@ static int node_info( lua_State* L ) lua_pushinteger(L, NODE_VERSION_REVISION); lua_pushinteger(L, system_get_chip_id()); // chip id lua_pushinteger(L, spi_flash_get_id()); // flash id - lua_pushinteger(L, flash_get_size_byte() / 1024); // flash size in KB - lua_pushinteger(L, flash_get_mode()); - lua_pushinteger(L, flash_get_speed()); + #if defined(FLASH_SAFE_API) + lua_pushinteger(L, flash_safe_get_size_byte() / 1024); // flash size in KB + #else + lua_pushinteger(L, flash_rom_get_size_byte() / 1024); // flash size in KB + #endif // defined(FLASH_SAFE_API) + lua_pushinteger(L, flash_rom_get_mode()); + lua_pushinteger(L, flash_rom_get_speed()); return 8; } @@ -122,7 +126,11 @@ static int node_flashsize( lua_State* L ) // flash_set_size_byte(sz); // } //} - uint32_t sz = flash_get_size_byte(); +#if defined(FLASH_SAFE_API) + uint32_t sz = flash_safe_get_size_byte(); +#else + uint32_t sz = flash_rom_get_size_byte(); +#endif // defined(FLASH_SAFE_API) lua_pushinteger( L, sz ); return 1; } diff --git a/app/modules/tmr.c b/app/modules/tmr.c index bd68503e..1a0fa478 100644 --- a/app/modules/tmr.c +++ b/app/modules/tmr.c @@ -148,24 +148,30 @@ static int tmr_wdclr( lua_State* L ) } static os_timer_t rtc_timer_updator; -static uint64_t cur_count = 0; -static uint64_t rtc_us = 0; +static uint32_t cur_count = 0; +static uint32_t rtc_10ms = 0; void rtc_timer_update_cb(void *arg){ - uint64_t t = (uint64_t)system_get_rtc_time(); - uint64_t delta = (t>=cur_count)?(t - cur_count):(0x100000000 + t - cur_count); + uint32_t t = (uint32_t)system_get_rtc_time(); + uint32_t delta = 0; + if(t>=cur_count){ + delta = t-cur_count; + }else{ + delta = 0xFFFFFFF - cur_count + t + 1; + } + // uint64_t delta = (t>=cur_count)?(t - cur_count):(0x100000000 + t - cur_count); // NODE_ERR("%x\n",t); cur_count = t; - unsigned c = system_rtc_clock_cali_proc(); - uint64_t itg = c >> 12; - uint64_t dec = c & 0xFFF; - rtc_us += (delta*itg + ((delta*dec)>>12)); - // TODO: store rtc_us to rtc memory. + uint32_t c = system_rtc_clock_cali_proc(); + uint32_t itg = c >> 12; // ~=5 + uint32_t dec = c & 0xFFF; // ~=2ff + rtc_10ms += (delta*itg + ((delta*dec)>>12)) / 10000; + // TODO: store rtc_10ms to rtc memory. } // Lua: time() , return rtc time in second static int tmr_time( lua_State* L ) { - uint64_t local = rtc_us; - lua_pushinteger( L, ((uint32_t)(local/1000000)) & 0x7FFFFFFF ); + uint32_t local = rtc_10ms; + lua_pushinteger( L, ((uint32_t)(local/100)) & 0x7FFFFFFF ); return 1; } diff --git a/app/platform/cpu_esp8266.h b/app/platform/cpu_esp8266.h index 75a238dc..ec6440a7 100644 --- a/app/platform/cpu_esp8266.h +++ b/app/platform/cpu_esp8266.h @@ -30,7 +30,11 @@ #elif defined(FLASH_16M) #define FLASH_SEC_NUM 0x1000 #elif defined(FLASH_AUTOSIZE) -#define FLASH_SEC_NUM (flash_get_sec_num()) +#if defined(FLASH_SAFE_API) +#define FLASH_SEC_NUM (flash_safe_get_sec_num()) +#else +#define FLASH_SEC_NUM (flash_rom_get_sec_num()) +#endif // defined(FLASH_SAFE_API) #else #define FLASH_SEC_NUM 0x80 #endif @@ -54,8 +58,14 @@ // SpiFlashOpResult spi_flash_erase_sector(uint16 sec); // SpiFlashOpResult spi_flash_write(uint32 des_addr, uint32 *src_addr, uint32 size); // SpiFlashOpResult spi_flash_read(uint32 src_addr, uint32 *des_addr, uint32 size); +#if defined(FLASH_SAFE_API) +#define flash_write flash_safe_write +#define flash_erase flash_safe_erase_sector +#define flash_read flash_safe_read +#else #define flash_write spi_flash_write #define flash_erase spi_flash_erase_sector #define flash_read spi_flash_read +#endif // defined(FLASH_SAFE_API) #endif // #ifndef __CPU_ESP8266_H__ diff --git a/app/platform/flash_api.c b/app/platform/flash_api.c index c4ce6f57..6af6e31c 100644 --- a/app/platform/flash_api.c +++ b/app/platform/flash_api.c @@ -20,77 +20,160 @@ static volatile const uint8_t flash_init_data[128] ICACHE_STORE_ATTR ICACHE_RODA 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; -SPIFlashInfo flash_get_info(void) +uint32_t flash_detect_size_byte(void) { - volatile SPIFlashInfo spi_flash_info ICACHE_STORE_ATTR; - spi_flash_info = *((SPIFlashInfo *)(FLASH_MAP_START_ADDRESS)); - // spi_flash_read(0, (uint32 *)(& spi_flash_info), sizeof(spi_flash_info)); - return spi_flash_info; -} - -uint8_t flash_get_size(void) -{ - return flash_get_info().size; -} - -uint32_t flash_get_size_byte(void) -{ - uint32_t flash_size = 0; - switch (flash_get_info().size) +#define FLASH_BUFFER_SIZE_DETECT 32 + uint32_t dummy_size = FLASH_SIZE_256KBYTE; + uint8_t data_orig[FLASH_BUFFER_SIZE_DETECT] ICACHE_STORE_ATTR = {0}; + uint8_t data_new[FLASH_BUFFER_SIZE_DETECT] ICACHE_STORE_ATTR = {0}; + if (SPI_FLASH_RESULT_OK == flash_safe_read(0, (uint32 *)data_orig, FLASH_BUFFER_SIZE_DETECT)) { - case SIZE_2MBIT: - // 2Mbit, 256kByte - flash_size = 256 * 1024; - break; - case SIZE_4MBIT: - // 4Mbit, 512kByte - flash_size = 512 * 1024; - break; - case SIZE_8MBIT: - // 8Mbit, 1MByte - flash_size = 1 * 1024 * 1024; - break; - case SIZE_16MBIT: - // 16Mbit, 2MByte - flash_size = 2 * 1024 * 1024; - break; - case SIZE_32MBIT: - // 32Mbit, 4MByte - flash_size = 4 * 1024 * 1024; - break; - case SIZE_64MBIT: - // 64Mbit, 8MByte - flash_size = 8 * 1024 * 1024; - break; - case SIZE_128MBIT: - // 128Mbit, 16MByte - flash_size = 16 * 1024 * 1024; - break; - default: - // Unknown flash size, fall back mode. - flash_size = 512 * 1024; - break; + dummy_size = FLASH_SIZE_256KBYTE; + while ((dummy_size < FLASH_SIZE_16MBYTE) && + (SPI_FLASH_RESULT_OK == flash_safe_read(dummy_size, (uint32 *)data_new, FLASH_BUFFER_SIZE_DETECT)) && + (0 != os_memcmp(data_orig, data_new, FLASH_BUFFER_SIZE_DETECT)) + ) + { + dummy_size *= 2; + } + }; + return dummy_size; +#undef FLASH_BUFFER_SIZE_DETECT +} + +uint32_t flash_safe_get_size_byte(void) +{ + static uint32_t flash_size = 0; + if (flash_size == 0) + { + flash_size = flash_detect_size_byte(); } return flash_size; } -bool flash_set_size(uint8_t size) +uint16_t flash_safe_get_sec_num(void) +{ + return (flash_safe_get_size_byte() / (SPI_FLASH_SEC_SIZE)); +} + +SpiFlashOpResult flash_safe_read(uint32 src_addr, uint32 *des_addr, uint32 size) +{ + SpiFlashOpResult result = SPI_FLASH_RESULT_ERR; + FLASH_SAFEMODE_ENTER(); + result = spi_flash_read(src_addr, (uint32 *) des_addr, size); + FLASH_SAFEMODE_LEAVE(); + return result; +} + +SpiFlashOpResult flash_safe_write(uint32 des_addr, uint32 *src_addr, uint32 size) +{ + SpiFlashOpResult result = SPI_FLASH_RESULT_ERR; + FLASH_SAFEMODE_ENTER(); + result = spi_flash_write(des_addr, src_addr, size); + FLASH_SAFEMODE_LEAVE(); + return result; +} + +SpiFlashOpResult flash_safe_erase_sector(uint16 sec) +{ + SpiFlashOpResult result = SPI_FLASH_RESULT_ERR; + FLASH_SAFEMODE_ENTER(); + result = spi_flash_erase_sector(sec); + FLASH_SAFEMODE_LEAVE(); + return result; +} + +SPIFlashInfo flash_rom_getinfo(void) +{ + volatile SPIFlashInfo spi_flash_info ICACHE_STORE_ATTR; + // Don't use it before cache read disabled + // FLASH_DISABLE_CACHE(); + // spi_flash_info = *((SPIFlashInfo *)(FLASH_ADDRESS_START_MAP)); + // FLASH_ENABLE_CACHE(); + // Needn't safe mode. + spi_flash_read(0, (uint32 *)(& spi_flash_info), sizeof(spi_flash_info)); + return spi_flash_info; +} + +uint8_t flash_rom_get_size_type(void) +{ + return flash_rom_getinfo().size; +} + +uint32_t flash_rom_get_size_byte(void) +{ + static uint32_t flash_size = 0; + if (flash_size == 0) + { + switch (flash_rom_getinfo().size) + { + case SIZE_2MBIT: + // 2Mbit, 256kByte + flash_size = 256 * 1024; + break; + case SIZE_4MBIT: + // 4Mbit, 512kByte + flash_size = 512 * 1024; + break; + case SIZE_8MBIT: + // 8Mbit, 1MByte + flash_size = 1 * 1024 * 1024; + break; + case SIZE_16MBIT: + // 16Mbit, 2MByte + flash_size = 2 * 1024 * 1024; + break; + case SIZE_32MBIT: + // 32Mbit, 4MByte + flash_size = 4 * 1024 * 1024; + break; + case SIZE_64MBIT: + // 64Mbit, 8MByte + flash_size = 8 * 1024 * 1024; + break; + case SIZE_128MBIT: + // 128Mbit, 16MByte + flash_size = 16 * 1024 * 1024; + break; + default: + // Unknown flash size, fall back mode. + flash_size = 512 * 1024; + break; + } + } + return flash_size; +} + +bool flash_rom_set_size_type(uint8_t size) { // Dangerous, here are dinosaur infested!!!!! // Reboot required!!! // If you don't know what you're doing, your nodemcu may turn into stone ... +#if defined(FLASH_SAFE_API) + uint8_t data[SPI_FLASH_SEC_SIZE] ICACHE_STORE_ATTR; + flash_safe_read(0, (uint32 *)data, sizeof(data)); + SPIFlashInfo *p_spi_flash_info = (SPIFlashInfo *)(data); + p_spi_flash_info->size = size; + flash_safe_erase_sector(0); + flash_safe_write(0, (uint32 *)data, sizeof(data)); + // TODO: CHECKSUM Firmware + //p_spi_flash_info = flash_rom_getinfo(); + //p_spi_flash_info->size = size; +#else uint8_t data[SPI_FLASH_SEC_SIZE] ICACHE_STORE_ATTR; spi_flash_read(0, (uint32 *)data, sizeof(data)); SPIFlashInfo *p_spi_flash_info = (SPIFlashInfo *)(data); p_spi_flash_info->size = size; spi_flash_erase_sector(0); spi_flash_write(0, (uint32 *)data, sizeof(data)); - //p_spi_flash_info = flash_get_info(); + // TODO: CHECKSUM Firmware + //p_spi_flash_info = flash_rom_getinfo(); //p_spi_flash_info->size = size; +#endif // defined(FLASH_SAFE_API) return true; } -bool flash_set_size_byte(uint32_t size) +bool flash_rom_set_size_byte(uint32_t size) { // Dangerous, here are dinosaur infested!!!!! // Reboot required!!! @@ -102,27 +185,27 @@ bool flash_set_size_byte(uint32_t size) case 256 * 1024: // 2Mbit, 256kByte flash_size = SIZE_2MBIT; - flash_set_size(flash_size); + flash_rom_set_size_type(flash_size); break; case 512 * 1024: // 4Mbit, 512kByte flash_size = SIZE_4MBIT; - flash_set_size(flash_size); + flash_rom_set_size_type(flash_size); break; case 1 * 1024 * 1024: // 8Mbit, 1MByte flash_size = SIZE_8MBIT; - flash_set_size(flash_size); + flash_rom_set_size_type(flash_size); break; case 2 * 1024 * 1024: // 16Mbit, 2MByte flash_size = SIZE_16MBIT; - flash_set_size(flash_size); + flash_rom_set_size_type(flash_size); break; case 4 * 1024 * 1024: // 32Mbit, 4MByte flash_size = SIZE_32MBIT; - flash_set_size(flash_size); + flash_rom_set_size_type(flash_size); break; default: // Unknown flash size. @@ -132,22 +215,22 @@ bool flash_set_size_byte(uint32_t size) return result; } -uint16_t flash_get_sec_num(void) +uint16_t flash_rom_get_sec_num(void) { //static uint16_t sec_num = 0; - // return flash_get_size_byte() / (SPI_FLASH_SEC_SIZE); - // c_printf("\nflash_get_size_byte()=%d\n", ( flash_get_size_byte() / (SPI_FLASH_SEC_SIZE) )); + // return flash_rom_get_size_byte() / (SPI_FLASH_SEC_SIZE); + // c_printf("\nflash_rom_get_size_byte()=%d\n", ( flash_rom_get_size_byte() / (SPI_FLASH_SEC_SIZE) )); // if( sec_num == 0 ) //{ // sec_num = 4 * 1024 * 1024 / (SPI_FLASH_SEC_SIZE); //} //return sec_num; - return ( flash_get_size_byte() / (SPI_FLASH_SEC_SIZE) ); + return ( flash_rom_get_size_byte() / (SPI_FLASH_SEC_SIZE) ); } -uint8_t flash_get_mode(void) +uint8_t flash_rom_get_mode(void) { - SPIFlashInfo spi_flash_info = flash_get_info(); + SPIFlashInfo spi_flash_info = flash_rom_getinfo(); switch (spi_flash_info.mode) { // Reserved for future use @@ -163,10 +246,10 @@ uint8_t flash_get_mode(void) return spi_flash_info.mode; } -uint32_t flash_get_speed(void) +uint32_t flash_rom_get_speed(void) { uint32_t speed = 0; - SPIFlashInfo spi_flash_info = flash_get_info(); + SPIFlashInfo spi_flash_info = flash_rom_getinfo(); switch (spi_flash_info.speed) { case SPEED_40MHZ: @@ -193,7 +276,11 @@ bool flash_init_data_written(void) { // FLASH SEC - 4 uint32_t data[2] ICACHE_STORE_ATTR; - if (SPI_FLASH_RESULT_OK == spi_flash_read((flash_get_sec_num() - 4) * SPI_FLASH_SEC_SIZE, (uint32 *)data, sizeof(data))) +#if defined(FLASH_SAFE_API) + if (SPI_FLASH_RESULT_OK == flash_safe_read((flash_rom_get_sec_num() - 4) * SPI_FLASH_SEC_SIZE, (uint32 *)data, sizeof(data))) +#else + if (SPI_FLASH_RESULT_OK == spi_flash_read((flash_rom_get_sec_num() - 4) * SPI_FLASH_SEC_SIZE, (uint32 *)data, sizeof(data))) +#endif // defined(FLASH_SAFE_API) { if (data[0] == 0xFFFFFFFF && data[1] == 0xFFFFFFFF) { @@ -210,13 +297,23 @@ bool flash_init_data_default(void) // Reboot required!!! // It will init system data to default! bool result = false; - if (SPI_FLASH_RESULT_OK == spi_flash_erase_sector((flash_get_sec_num() - 4))) +#if defined(FLASH_SAFE_API) + if (SPI_FLASH_RESULT_OK == flash_safe_erase_sector((flash_safe_get_sec_num() - 4))) { - if (SPI_FLASH_RESULT_OK == spi_flash_write((flash_get_sec_num() - 4) * SPI_FLASH_SEC_SIZE, (uint32 *)flash_init_data, 128)) + if (SPI_FLASH_RESULT_OK == flash_safe_write((flash_safe_get_sec_num() - 4) * SPI_FLASH_SEC_SIZE, (uint32 *)flash_init_data, 128)) { result = true; } } +#else + if (SPI_FLASH_RESULT_OK == spi_flash_erase_sector((flash_rom_get_sec_num() - 4))) + { + if (SPI_FLASH_RESULT_OK == spi_flash_write((flash_rom_get_sec_num() - 4) * SPI_FLASH_SEC_SIZE, (uint32 *)flash_init_data, 128)) + { + result = true; + } + } +#endif // defined(FLASH_SAFE_API) return result; } @@ -227,8 +324,13 @@ bool flash_init_data_blank(void) // Reboot required!!! // It will init system config to blank! bool result = false; - if ((SPI_FLASH_RESULT_OK == spi_flash_erase_sector((flash_get_sec_num() - 2))) && - (SPI_FLASH_RESULT_OK == spi_flash_erase_sector((flash_get_sec_num() - 1)))) +#if defined(FLASH_SAFE_API) + if ((SPI_FLASH_RESULT_OK == flash_safe_erase_sector((flash_rom_get_sec_num() - 2))) && + (SPI_FLASH_RESULT_OK == flash_safe_erase_sector((flash_rom_get_sec_num() - 1)))) +#else + if ((SPI_FLASH_RESULT_OK == spi_flash_erase_sector((flash_rom_get_sec_num() - 2))) && + (SPI_FLASH_RESULT_OK == spi_flash_erase_sector((flash_rom_get_sec_num() - 1)))) +#endif // defined(FLASH_SAFE_API) { result = true; } diff --git a/app/platform/flash_api.h b/app/platform/flash_api.h index 47c0d4fb..d7ac41ad 100644 --- a/app/platform/flash_api.h +++ b/app/platform/flash_api.h @@ -4,28 +4,55 @@ #include "user_config.h" #include "cpu_esp8266.h" -#define FLASH_MAP_START_ADDRESS (INTERNAL_FLASH_START_ADDRESS) +#define FLASH_ADDRESS_START_MAP (INTERNAL_FLASH_START_ADDRESS) + +#define FLASH_SIZE_2MBIT (2 * 1024 * 1024) +#define FLASH_SIZE_4MBIT (4 * 1024 * 1024) +#define FLASH_SIZE_8MBIT (8 * 1024 * 1024) +#define FLASH_SIZE_16MBIT (16 * 1024 * 1024) +#define FLASH_SIZE_32MBIT (32 * 1024 * 1024) +#define FLASH_SIZE_64MBIT (64 * 1024 * 1024) +#define FLASH_SIZE_128MBIT (128 * 1024 * 1024) + +#define FLASH_SIZE_256KBYTE (FLASH_SIZE_2MBIT / 8) +#define FLASH_SIZE_512KBYTE (FLASH_SIZE_4MBIT / 8) +#define FLASH_SIZE_1MBYTE (FLASH_SIZE_8MBIT / 8) +#define FLASH_SIZE_2MBYTE (FLASH_SIZE_16MBIT / 8) +#define FLASH_SIZE_4MBYTE (FLASH_SIZE_32MBIT / 8) +#define FLASH_SIZE_8MBYTE (FLASH_SIZE_64MBIT / 8) +#define FLASH_SIZE_16MBYTE (FLASH_SIZE_128MBIT/ 8) + +#define FLASH_SAFEMODE_ENTER() \ +do { \ + extern SpiFlashChip * flashchip; \ + flashchip->chip_size = FLASH_SIZE_16MBYTE + + +#define FLASH_SAFEMODE_LEAVE() \ + flashchip->chip_size = flash_rom_get_size_byte(); \ +} while(0) /****************************************************************************** * ROM Function definition * Note: It is unsafe to use ROM function, but it may efficient. * SPIEraseSector - * unknown SPIEraseSector(uint16 sec); + * SpiFlashOpResult SPIEraseSector(uint16 sec); * The 1st parameter is flash sector number. + * Note: Must disable cache read before using it. - * SPIRead (Unsafe) - * unknown SPIRead(uint32_t src_addr, uint32_t *des_addr, uint32_t size); + * SPIRead + * SpiFlashOpResult SPIRead(uint32_t src_addr, uint32_t *des_addr, uint32_t size); * The 1st parameter is source addresses. * The 2nd parameter is destination addresses. * The 3rd parameter is size. - * Note: Sometimes it have no effect, may be need a delay or other option(lock or unlock, etc.) with known reason. + * Note: Must disable cache read before using it. - * SPIWrite (Unsafe) - * unknown SPIWrite(uint32_t des_addr, uint32_t *src_addr, uint32_t size); + * SPIWrite + * SpiFlashOpResult SPIWrite(uint32_t des_addr, uint32_t *src_addr, uint32_t size); * The 1st parameter is destination addresses. * The 2nd parameter is source addresses. * The 3rd parameter is size. - * Note: Sometimes it have no effect, may be need a delay or other option(lock or unlock, etc.) with known reason. + * Note: Must disable cache read before using it. *******************************************************************************/ typedef struct @@ -58,14 +85,20 @@ typedef struct } size : 4; } ICACHE_STORE_TYPEDEF_ATTR SPIFlashInfo; -SPIFlashInfo flash_get_info(void); -uint8_t flash_get_size(void); -uint32_t flash_get_size_byte(void); -bool flash_set_size(uint8_t); -bool flash_set_size_byte(uint32_t); -uint16_t flash_get_sec_num(void); -uint8_t flash_get_mode(void); -uint32_t flash_get_speed(void); +uint32_t flash_detect_size_byte(void); +uint32_t flash_safe_get_size_byte(void); +uint16_t flash_safe_get_sec_num(void); +SpiFlashOpResult flash_safe_read(uint32 src_addr, uint32 *des_addr, uint32 size); +SpiFlashOpResult flash_safe_write(uint32 des_addr, uint32 *src_addr, uint32 size); +SpiFlashOpResult flash_safe_erase_sector(uint16 sec); +SPIFlashInfo flash_rom_getinfo(void); +uint8_t flash_rom_get_size_type(void); +uint32_t flash_rom_get_size_byte(void); +bool flash_rom_set_size_type(uint8_t); +bool flash_rom_set_size_byte(uint32_t); +uint16_t flash_rom_get_sec_num(void); +uint8_t flash_rom_get_mode(void); +uint32_t flash_rom_get_speed(void); bool flash_init_data_written(void); bool flash_init_data_default(void); bool flash_init_data_blank(void); diff --git a/app/spiffs/spiffs.c b/app/spiffs/spiffs.c index df749e09..e96e15a0 100644 --- a/app/spiffs/spiffs.c +++ b/app/spiffs/spiffs.c @@ -160,7 +160,7 @@ int myspiffs_error( int fd ){ return SPIFFS_errno(&fs); } void myspiffs_clearerr( int fd ){ - fs.errno = SPIFFS_OK; + SPIFFS_clearerr(&fs); } int myspiffs_rename( const char *old, const char *newname ){ return SPIFFS_rename(&fs, (char *)old, (char *)newname); diff --git a/app/spiffs/spiffs.h b/app/spiffs/spiffs.h index 23228446..c5a2c1d2 100644 --- a/app/spiffs/spiffs.h +++ b/app/spiffs/spiffs.h @@ -9,6 +9,10 @@ #ifndef SPIFFS_H_ #define SPIFFS_H_ +#if defined(__cplusplus) +extern "C" { +#endif + #include "c_stdio.h" #include "spiffs_config.h" @@ -181,7 +185,7 @@ typedef struct { u32_t fd_count; // last error - s32_t errno; + s32_t err_code; // current number of free blocks u32_t free_blocks; @@ -375,9 +379,9 @@ void SPIFFS_close(spiffs *fs, spiffs_file fh); * Renames a file * @param fs the file system struct * @param old path of file to rename - * @param new new path of file + * @param newPath new path of file */ -s32_t SPIFFS_rename(spiffs *fs, char *old, char *new); +s32_t SPIFFS_rename(spiffs *fs, char *old, char *newPath); /** * Returns last error of last file operation. @@ -385,6 +389,12 @@ s32_t SPIFFS_rename(spiffs *fs, char *old, char *new); */ s32_t SPIFFS_errno(spiffs *fs); +/** + * Clears last error. + * @param fs the file system struct + */ +void SPIFFS_clearerr(spiffs *fs); + /** * Opens a directory stream corresponding to the given name. * The stream is positioned at the first entry in the directory. @@ -416,6 +426,21 @@ struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e); */ s32_t SPIFFS_check(spiffs *fs); + +/** + * Returns number of total bytes available and number of used bytes. + * This is an estimation, and depends on if there a many files with little + * data or few files with much data. + * NB: If used number of bytes exceeds total bytes, a SPIFFS_check should + * run. This indicates a power loss in midst of things. In worst case + * (repeated powerlosses in mending or gc) you might have to delete some files. + * + * @param fs the file system struct + * @param total total number of bytes in filesystem + * @param used used number of bytes in filesystem + */ +s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used); + /** * Check if EOF reached. * @param fs the file system struct @@ -468,4 +493,8 @@ int myspiffs_check( void ); int myspiffs_rename( const char *old, const char *newname ); size_t myspiffs_size( int fd ); +#if defined(__cplusplus) +} +#endif + #endif /* SPIFFS_H_ */ diff --git a/app/spiffs/spiffs_config.h b/app/spiffs/spiffs_config.h index 8a8fc4b3..d80e7d22 100644 --- a/app/spiffs/spiffs_config.h +++ b/app/spiffs/spiffs_config.h @@ -30,19 +30,19 @@ typedef uint8_t u8_t; // Set generic spiffs debug output call. #ifndef SPIFFS_DGB -#define SPIFFS_DBG(...) +#define SPIFFS_DBG(...) //printf(__VA_ARGS__) #endif // Set spiffs debug output call for garbage collecting. #ifndef SPIFFS_GC_DGB -#define SPIFFS_GC_DBG(...) +#define SPIFFS_GC_DBG(...) //printf(__VA_ARGS__) #endif // Set spiffs debug output call for caching. #ifndef SPIFFS_CACHE_DGB -#define SPIFFS_CACHE_DBG(...) +#define SPIFFS_CACHE_DBG(...) //printf(__VA_ARGS__) #endif // Set spiffs debug output call for system consistency checks. #ifndef SPIFFS_CHECK_DGB -#define SPIFFS_CHECK_DBG(...) +#define SPIFFS_CHECK_DBG(...) //printf(__VA_ARGS__) #endif // Enable/disable API functions to determine exact number of bytes @@ -77,7 +77,7 @@ typedef uint8_t u8_t; // Define maximum number of gc runs to perform to reach desired free pages. #ifndef SPIFFS_GC_MAX_RUNS -#define SPIFFS_GC_MAX_RUNS 3 +#define SPIFFS_GC_MAX_RUNS 5 #endif // Enable/disable statistics on gc. Debug/test purpose only. diff --git a/app/spiffs/spiffs_gc.c b/app/spiffs/spiffs_gc.c index 4d6c8971..2ad31d07 100644 --- a/app/spiffs/spiffs_gc.c +++ b/app/spiffs/spiffs_gc.c @@ -119,16 +119,21 @@ s32_t spiffs_gc_check( spiffs *fs, u32_t len) { s32_t res; - u32_t free_pages = - (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * fs->block_count + s32_t free_pages = + (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count-2) - fs->stats_p_allocated - fs->stats_p_deleted; int tries = 0; if (fs->free_blocks > 3 && - len < free_pages * SPIFFS_DATA_PAGE_SIZE(fs)) { + (s32_t)len < free_pages * (s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) { return SPIFFS_OK; } + u32_t needed_pages = (len + SPIFFS_DATA_PAGE_SIZE(fs) - 1) / SPIFFS_DATA_PAGE_SIZE(fs); + if (fs->free_blocks <= 2 && (s32_t)needed_pages > free_pages) { + return SPIFFS_ERR_FULL; + } + //printf("gcing started %i dirty, blocks %i free, want %i bytes\n", fs->stats_p_allocated + fs->stats_p_deleted, fs->free_blocks, len); do { @@ -168,16 +173,22 @@ s32_t spiffs_gc_check( SPIFFS_CHECK_RES(res); free_pages = - (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * fs->block_count + (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2) - fs->stats_p_allocated - fs->stats_p_deleted; } while (++tries < SPIFFS_GC_MAX_RUNS && (fs->free_blocks <= 2 || - len > free_pages*SPIFFS_DATA_PAGE_SIZE(fs))); - SPIFFS_GC_DBG("gc_check: finished\n"); + (s32_t)len > free_pages*(s32_t)SPIFFS_DATA_PAGE_SIZE(fs))); - //printf("gcing finished %i dirty, blocks %i free, %i pages free, %i tries, res %i\n", - // fs->stats_p_allocated + fs->stats_p_deleted, - // fs->free_blocks, free_pages, tries, res); + free_pages = + (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2) + - fs->stats_p_allocated - fs->stats_p_deleted; + if ((s32_t)len > free_pages*(s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) { + res = SPIFFS_ERR_FULL; + } + + SPIFFS_GC_DBG("gc_check: finished, %i dirty, blocks %i free, %i pages free, %i tries, res %i\n", + fs->stats_p_allocated + fs->stats_p_deleted, + fs->free_blocks, free_pages, tries, res); return res; } diff --git a/app/spiffs/spiffs_hydrogen.c b/app/spiffs/spiffs_hydrogen.c index 20e45ecf..3ca01463 100644 --- a/app/spiffs/spiffs_hydrogen.c +++ b/app/spiffs/spiffs_hydrogen.c @@ -103,7 +103,11 @@ void SPIFFS_unmount(spiffs *fs) { } s32_t SPIFFS_errno(spiffs *fs) { - return fs->errno; + return fs->err_code; +} + +void SPIFFS_clearerr(spiffs *fs) { + fs->err_code = SPIFFS_OK; } s32_t SPIFFS_creat(spiffs *fs, char *path, spiffs_mode mode) { @@ -314,8 +318,6 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, u32_t len) { #endif } - SPIFFS_DBG("SPIFFS_write %i %04x offs:%i len %i\n", fh, fd->obj_id, offset, len); - #if SPIFFS_CACHE_WR if ((fd->flags & SPIFFS_DIRECT) == 0) { if (len < (s32_t)SPIFFS_CFG_LOG_PAGE_SZ(fs)) { @@ -328,12 +330,13 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, u32_t len) { offset + len > fd->cache_page->offset + SPIFFS_CFG_LOG_PAGE_SZ(fs)) // writing beyond cache page { // boundary violation, write back cache first and allocate new - SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page %i for fd %i:&04x, boundary viol, offs:%i size:%i\n", + SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page %i for fd %i:%04x, boundary viol, offs:%i size:%i\n", fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size); res = spiffs_hydro_write(fs, fd, spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), fd->cache_page->offset, fd->cache_page->size); spiffs_cache_fd_release(fs, fd->cache_page); + SPIFFS_API_CHECK_RES(fs, res); } else { // writing within cache alloc_cpage = 0; @@ -379,6 +382,7 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, u32_t len) { spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), fd->cache_page->offset, fd->cache_page->size); spiffs_cache_fd_release(fs, fd->cache_page); + SPIFFS_API_CHECK_RES(fs, res); res = spiffs_hydro_write(fs, fd, buf, offset, len); SPIFFS_API_CHECK_RES(fs, res); } @@ -580,7 +584,7 @@ static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh) { spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), fd->cache_page->offset, fd->cache_page->size); if (res < SPIFFS_OK) { - fs->errno = res; + fs->err_code = res; } spiffs_cache_fd_release(fs, fd->cache_page); } @@ -605,7 +609,7 @@ s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh) { void SPIFFS_close(spiffs *fs, spiffs_file fh) { if (!SPIFFS_CHECK_MOUNT(fs)) { - fs->errno = SPIFFS_ERR_NOT_MOUNTED; + fs->err_code = SPIFFS_ERR_NOT_MOUNTED; return; } SPIFFS_LOCK(fs); @@ -661,7 +665,7 @@ s32_t SPIFFS_rename(spiffs *fs, char *old, char *new) { spiffs_DIR *SPIFFS_opendir(spiffs *fs, char *name, spiffs_DIR *d) { (void)name; if (!SPIFFS_CHECK_MOUNT(fs)) { - fs->errno = SPIFFS_ERR_NOT_MOUNTED; + fs->err_code = SPIFFS_ERR_NOT_MOUNTED; return 0; } d->fs = fs; @@ -707,7 +711,7 @@ static s32_t spiffs_read_dir_v( struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e) { if (!SPIFFS_CHECK_MOUNT(d->fs)) { - d->fs->errno = SPIFFS_ERR_NOT_MOUNTED; + d->fs->err_code = SPIFFS_ERR_NOT_MOUNTED; return 0; } SPIFFS_LOCK(fs); @@ -732,7 +736,7 @@ struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e) { d->entry = entry + 1; ret = e; } else { - d->fs->errno = res; + d->fs->err_code = res; } SPIFFS_UNLOCK(fs); return ret; @@ -760,6 +764,29 @@ s32_t SPIFFS_check(spiffs *fs) { return res; } +s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used) { + s32_t res = SPIFFS_OK; + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + u32_t pages_per_block = SPIFFS_PAGES_PER_BLOCK(fs); + u32_t blocks = fs->block_count; + u32_t obj_lu_pages = SPIFFS_OBJ_LOOKUP_PAGES(fs); + u32_t data_page_size = SPIFFS_DATA_PAGE_SIZE(fs); + u32_t total_data_pages = (blocks - 2) * (pages_per_block - obj_lu_pages) + 1; // -2 for spare blocks, +1 for emergency page + + if (total) { + *total = total_data_pages * data_page_size; + } + + if (used) { + *used = fs->stats_p_allocated * data_page_size; + } + + SPIFFS_UNLOCK(fs); + return res; +} + s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh) { SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_LOCK(fs); @@ -878,11 +905,14 @@ s32_t SPIFFS_vis(spiffs *fs) { } // per block spiffs_printf("era_cnt_max: %i\n", fs->max_erase_count); - spiffs_printf("last_errno: %i\n", fs->errno); + spiffs_printf("last_errno: %i\n", fs->err_code); spiffs_printf("blocks: %i\n", fs->block_count); spiffs_printf("free_blocks: %i\n", fs->free_blocks); spiffs_printf("page_alloc: %i\n", fs->stats_p_allocated); spiffs_printf("page_delet: %i\n", fs->stats_p_deleted); + u32_t total, used; + SPIFFS_info(fs, &total, &used); + spiffs_printf("used: %i of %i\n", used, total); SPIFFS_UNLOCK(fs); return res; diff --git a/app/spiffs/spiffs_nucleus.c b/app/spiffs/spiffs_nucleus.c index 74217cd0..4ab63b23 100644 --- a/app/spiffs/spiffs_nucleus.c +++ b/app/spiffs/spiffs_nucleus.c @@ -614,7 +614,7 @@ s32_t spiffs_object_create( spiffs_page_object_ix_header oix_hdr; int entry; - res = spiffs_gc_check(fs, 0); + res = spiffs_gc_check(fs, SPIFFS_DATA_PAGE_SIZE(fs)); SPIFFS_CHECK_RES(res); obj_id |= SPIFFS_OBJ_ID_IX_FLAG; @@ -811,7 +811,17 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { s32_t res = SPIFFS_OK; u32_t written = 0; - res = spiffs_gc_check(fs, len); + SPIFFS_DBG("append: %i bytes @ offs %i of size %i\n", len, offset, fd->size); + + if (offset > fd->size) { + SPIFFS_DBG("append: offset reversed to size\n"); + offset = fd->size; + } + + res = spiffs_gc_check(fs, len + SPIFFS_DATA_PAGE_SIZE(fs)); // add an extra page of data worth for meta + if (res != SPIFFS_OK) { + SPIFFS_DBG("append: gc check fail %i\n", res); + } SPIFFS_CHECK_RES(res); spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; @@ -912,7 +922,7 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &pix); SPIFFS_CHECK_RES(res); } - SPIFFS_DBG("append: %04x found object index at page %04x\n", fd->obj_id, pix); + SPIFFS_DBG("append: %04x found object index at page %04x [fd size %i]\n", fd->obj_id, pix, fd->size); res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); SPIFFS_CHECK_RES(res); @@ -1003,8 +1013,8 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { // update size in object header index page res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, 0, offset+written, &new_objix_hdr_page); - SPIFFS_DBG("append: %04x store new size II %i in objix_hdr, %04x:%04x, written %i\n", fd->obj_id - , offset+written, new_objix_hdr_page, 0, written); + SPIFFS_DBG("append: %04x store new size II %i in objix_hdr, %04x:%04x, written %i, res %i\n", fd->obj_id + , offset+written, new_objix_hdr_page, 0, written, res2); SPIFFS_CHECK_RES(res2); } else { // wrote within object index header page @@ -1042,7 +1052,7 @@ s32_t spiffs_object_modify(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { s32_t res = SPIFFS_OK; u32_t written = 0; - res = spiffs_gc_check(fs, len); + res = spiffs_gc_check(fs, len + SPIFFS_DATA_PAGE_SIZE(fs)); SPIFFS_CHECK_RES(res); spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; @@ -1308,7 +1318,7 @@ s32_t spiffs_object_truncate( s32_t res = SPIFFS_OK; spiffs *fs = fd->fs; - res = spiffs_gc_check(fs, 0); + res = spiffs_gc_check(fs, remove ? 0 : SPIFFS_DATA_PAGE_SIZE(fs)); SPIFFS_CHECK_RES(res); spiffs_page_ix objix_pix = fd->objix_hdr_pix; @@ -1386,13 +1396,26 @@ s32_t spiffs_object_truncate( ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = SPIFFS_OBJ_ID_FREE; } + SPIFFS_DBG("truncate: got data pix %04x\n", data_pix); + if (cur_size - SPIFFS_DATA_PAGE_SIZE(fs) >= new_size) { // delete full data page res = spiffs_page_data_check(fs, fd, data_pix, data_spix); - if (res != SPIFFS_OK) break; + if (res != SPIFFS_ERR_DELETED && res != SPIFFS_OK && res != SPIFFS_ERR_INDEX_REF_FREE) { + SPIFFS_DBG("truncate: err validating data pix %i\n", res); + break; + } + + if (res == SPIFFS_OK) { + res = spiffs_page_delete(fs, data_pix); + if (res != SPIFFS_OK) { + SPIFFS_DBG("truncate: err deleting data pix %i\n", res); + break; + } + } else if (res == SPIFFS_ERR_DELETED || res == SPIFFS_ERR_INDEX_REF_FREE) { + res = SPIFFS_OK; + } - res = spiffs_page_delete(fs, data_pix); - if (res != SPIFFS_OK) break; // update current size if (cur_size % SPIFFS_DATA_PAGE_SIZE(fs) == 0) { cur_size -= SPIFFS_DATA_PAGE_SIZE(fs); diff --git a/app/spiffs/spiffs_nucleus.h b/app/spiffs/spiffs_nucleus.h index 9b10d918..b4a34bcf 100644 --- a/app/spiffs/spiffs_nucleus.h +++ b/app/spiffs/spiffs_nucleus.h @@ -247,19 +247,19 @@ #define SPIFFS_API_CHECK_MOUNT(fs) \ if (!SPIFFS_CHECK_MOUNT((fs))) { \ - (fs)->errno = SPIFFS_ERR_NOT_MOUNTED; \ + (fs)->err_code = SPIFFS_ERR_NOT_MOUNTED; \ return -1; \ } #define SPIFFS_API_CHECK_RES(fs, res) \ if ((res) < SPIFFS_OK) { \ - (fs)->errno = (res); \ + (fs)->err_code = (res); \ return -1; \ } #define SPIFFS_API_CHECK_RES_UNLOCK(fs, res) \ if ((res) < SPIFFS_OK) { \ - (fs)->errno = (res); \ + (fs)->err_code = (res); \ SPIFFS_UNLOCK(fs); \ return -1; \ } diff --git a/app/spiffs/test/main.c b/app/spiffs/test/main.c new file mode 100644 index 00000000..28e9a92b --- /dev/null +++ b/app/spiffs/test/main.c @@ -0,0 +1,7 @@ +#include "testrunner.h" +#include + +int main(int argc, char **args) { + run_tests(argc, args); + exit(EXIT_SUCCESS); +} diff --git a/app/spiffs/test/params_test.h b/app/spiffs/test/params_test.h new file mode 100644 index 00000000..241367f3 --- /dev/null +++ b/app/spiffs/test/params_test.h @@ -0,0 +1,36 @@ +/* + * params_test.h + * + * Created on: May 26, 2013 + * Author: petera + */ + +#ifndef PARAMS_TEST_H_ +#define PARAMS_TEST_H_ + +// total emulated spi flash size +#define PHYS_FLASH_SIZE (16*1024*1024) +// spiffs file system size +#define SPIFFS_FLASH_SIZE (2*1024*1024) +// spiffs file system offset in emulated spi flash +#define SPIFFS_PHYS_ADDR (4*1024*1024) + +#define SECTOR_SIZE 65536 +#define LOG_BLOCK (SECTOR_SIZE*2) +#define LOG_PAGE (SECTOR_SIZE/256) + +#define FD_BUF_SIZE 64*6 +#define CACHE_BUF_SIZE (LOG_PAGE + 32)*8 + +#define ASSERT(c, m) real_assert((c),(m), __FILE__, __LINE__); + +typedef signed int s32_t; +typedef unsigned int u32_t; +typedef signed short s16_t; +typedef unsigned short u16_t; +typedef signed char s8_t; +typedef unsigned char u8_t; + +void real_assert(int c, const char *n, const char *file, int l); + +#endif /* PARAMS_TEST_H_ */ diff --git a/app/spiffs/test/test_bugreports.c b/app/spiffs/test/test_bugreports.c new file mode 100644 index 00000000..b577e6f7 --- /dev/null +++ b/app/spiffs/test/test_bugreports.c @@ -0,0 +1,160 @@ +/* + * test_bugreports.c + * + * Created on: Mar 8, 2015 + * Author: petera + */ + + + +#include "testrunner.h" +#include "test_spiffs.h" +#include "spiffs_nucleus.h" +#include "spiffs.h" +#include +#include +#include +#include +#include + + +SUITE(bug_tests) +void setup() { + _setup_test_only(); +} +void teardown() { + _teardown(); +} + +TEST(nodemcu_full_fs_1) { + fs_reset_specific(0, 4096*20, 4096, 4096, 256); + + int res; + spiffs_file fd; + + printf(" fill up system by writing one byte a lot\n"); + fd = SPIFFS_open(FS, "test1.txt", SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0); + TEST_CHECK(fd > 0); + int i; + spiffs_stat s; + res = SPIFFS_OK; + for (i = 0; i < 100*1000; i++) { + u8_t buf = 'x'; + res = SPIFFS_write(FS, fd, &buf, 1); + } + + int errno = SPIFFS_errno(FS); + int res2 = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res2 == SPIFFS_OK); + printf(" >>> file %s size: %i\n", s.name, s.size); + + TEST_CHECK(errno == SPIFFS_ERR_FULL); + SPIFFS_close(FS, fd); + + printf(" remove big file\n"); + res = SPIFFS_remove(FS, "test1.txt"); + + printf("res:%i errno:%i\n",res, SPIFFS_errno(FS)); + + TEST_CHECK(res == SPIFFS_OK); + res2 = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res2 == -1); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + res2 = SPIFFS_stat(FS, "test1.txt", &s); + TEST_CHECK(res2 == -1); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_NOT_FOUND); + + printf(" create small file\n"); + fd = SPIFFS_open(FS, "test2.txt", SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0); + TEST_CHECK(fd > 0); + res = SPIFFS_OK; + for (i = 0; res >= 0 && i < 1000; i++) { + u8_t buf = 'x'; + res = SPIFFS_write(FS, fd, &buf, 1); + } + TEST_CHECK(res >= SPIFFS_OK); + + res2 = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res2 == SPIFFS_OK); + printf(" >>> file %s size: %i\n", s.name, s.size); + + TEST_CHECK(s.size == 1000); + SPIFFS_close(FS, fd); + + return TEST_RES_OK; + +} TEST_END(nodemcu_full_fs_1) + +TEST(nodemcu_full_fs_2) { + fs_reset_specific(0, 4096*22, 4096, 4096, 256); + + int res; + spiffs_file fd; + + printf(" fill up system by writing one byte a lot\n"); + fd = SPIFFS_open(FS, "test1.txt", SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0); + TEST_CHECK(fd > 0); + int i; + spiffs_stat s; + res = SPIFFS_OK; + for (i = 0; i < 100*1000; i++) { + u8_t buf = 'x'; + res = SPIFFS_write(FS, fd, &buf, 1); + } + + int errno = SPIFFS_errno(FS); + int res2 = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res2 == SPIFFS_OK); + printf(" >>> file %s size: %i\n", s.name, s.size); + + TEST_CHECK(errno == SPIFFS_ERR_FULL); + SPIFFS_close(FS, fd); + + res2 = SPIFFS_stat(FS, "test1.txt", &s); + TEST_CHECK(res2 == SPIFFS_OK); + + SPIFFS_clearerr(FS); + printf(" create small file\n"); + fd = SPIFFS_open(FS, "test2.txt", SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_OK); + TEST_CHECK(fd > 0); + + for (i = 0; i < 1000; i++) { + u8_t buf = 'x'; + res = SPIFFS_write(FS, fd, &buf, 1); + } + + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FULL); + res2 = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res2 == SPIFFS_OK); + printf(" >>> file %s size: %i\n", s.name, s.size); + TEST_CHECK(s.size == 0); + SPIFFS_clearerr(FS); + + printf(" remove files\n"); + res = SPIFFS_remove(FS, "test1.txt"); + TEST_CHECK(res == SPIFFS_OK); + res = SPIFFS_remove(FS, "test2.txt"); + TEST_CHECK(res == SPIFFS_OK); + + printf(" create medium file\n"); + fd = SPIFFS_open(FS, "test3.txt", SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_OK); + TEST_CHECK(fd > 0); + + for (i = 0; i < 20*1000; i++) { + u8_t buf = 'x'; + res = SPIFFS_write(FS, fd, &buf, 1); + } + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_OK); + + res2 = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res2 == SPIFFS_OK); + printf(" >>> file %s size: %i\n", s.name, s.size); + TEST_CHECK(s.size == 20*1000); + + return TEST_RES_OK; + +} TEST_END(nodemcu_full_fs_2) + +SUITE_END(bug_tests) diff --git a/app/spiffs/test/test_check.c b/app/spiffs/test/test_check.c new file mode 100644 index 00000000..ab014dc0 --- /dev/null +++ b/app/spiffs/test/test_check.c @@ -0,0 +1,418 @@ +/* + * test_dev.c + * + * Created on: Jul 14, 2013 + * Author: petera + */ + + +#include "testrunner.h" +#include "test_spiffs.h" +#include "spiffs_nucleus.h" +#include "spiffs.h" +#include +#include +#include +#include +#include + + +SUITE(check_tests) +void setup() { + _setup(); +} +void teardown() { + _teardown(); +} + +TEST(evil_write) { + fs_set_validate_flashing(0); + printf("writing corruption to block 1 data range (leaving lu intact)\n"); + u32_t data_range = SPIFFS_CFG_LOG_BLOCK_SZ(FS) - + SPIFFS_CFG_LOG_PAGE_SZ(FS) * (SPIFFS_OBJ_LOOKUP_PAGES(FS)); + u8_t *corruption = malloc(data_range); + memrand(corruption, data_range); + u32_t addr = 0 * SPIFFS_CFG_LOG_PAGE_SZ(FS) * SPIFFS_OBJ_LOOKUP_PAGES(FS); + area_write(addr, corruption, data_range); + free(corruption); + + int size = SPIFFS_DATA_PAGE_SIZE(FS)*3; + int res = test_create_and_write_file("file", size, size); + + printf("CHECK1-----------------\n"); + SPIFFS_check(FS); + printf("CHECK2-----------------\n"); + SPIFFS_check(FS); + printf("CHECK3-----------------\n"); + SPIFFS_check(FS); + + res = test_create_and_write_file("file2", size, size); + TEST_CHECK(res >= 0); + + return TEST_RES_OK; +} TEST_END(evil_write) + + +TEST(lu_check1) { + int size = SPIFFS_DATA_PAGE_SIZE(FS)*3; + int res = test_create_and_write_file("file", size, size); + TEST_CHECK(res >= 0); + res = read_and_verify("file"); + TEST_CHECK(res >= 0); + + spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0); + TEST_CHECK(fd > 0); + spiffs_stat s; + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd); + + // modify lu entry data page index 1 + spiffs_page_ix pix; + res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 1, 0, &pix); + TEST_CHECK(res >= 0); + + // reset lu entry to being erased, but keep page data + spiffs_obj_id obj_id = SPIFFS_OBJ_ID_DELETED; + spiffs_block_ix bix = SPIFFS_BLOCK_FOR_PAGE(FS, pix); + int entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(FS, pix); + u32_t addr = SPIFFS_BLOCK_TO_PADDR(FS, bix) + entry*sizeof(spiffs_obj_id); + + area_write(addr, (u8_t*)&obj_id, sizeof(spiffs_obj_id)); + +#if SPIFFS_CACHE + spiffs_cache *cache = spiffs_get_cache(FS); + cache->cpage_use_map = 0; +#endif + SPIFFS_check(FS); + + return TEST_RES_OK; +} TEST_END(lu_check1) + + +TEST(page_cons1) { + int size = SPIFFS_DATA_PAGE_SIZE(FS)*3; + int res = test_create_and_write_file("file", size, size); + TEST_CHECK(res >= 0); + res = read_and_verify("file"); + TEST_CHECK(res >= 0); + + spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0); + TEST_CHECK(fd > 0); + spiffs_stat s; + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd); + + // modify object index, find object index header + spiffs_page_ix pix; + res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix); + TEST_CHECK(res >= 0); + + // set object index entry 2 to a bad page + u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + sizeof(spiffs_page_object_ix_header) + 0 * sizeof(spiffs_page_ix); + spiffs_page_ix bad_pix_ref = 0x55; + area_write(addr, (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix)); + area_write(addr+2, (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix)); + + // delete all cache +#if SPIFFS_CACHE + spiffs_cache *cache = spiffs_get_cache(FS); + cache->cpage_use_map = 0; +#endif + + SPIFFS_check(FS); + + res = read_and_verify("file"); + TEST_CHECK(res >= 0); + + return TEST_RES_OK; +} TEST_END(page_cons1) + + +TEST(page_cons2) { + int size = SPIFFS_DATA_PAGE_SIZE(FS)*3; + int res = test_create_and_write_file("file", size, size); + TEST_CHECK(res >= 0); + res = read_and_verify("file"); + TEST_CHECK(res >= 0); + + spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0); + TEST_CHECK(fd > 0); + spiffs_stat s; + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd); + + // modify object index, find object index header + spiffs_page_ix pix; + res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix); + TEST_CHECK(res >= 0); + + // find data page span index 0 + spiffs_page_ix dpix; + res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &dpix); + TEST_CHECK(res >= 0); + + // set object index entry 1+2 to a data page 0 + u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + sizeof(spiffs_page_object_ix_header) + 1 * sizeof(spiffs_page_ix); + spiffs_page_ix bad_pix_ref = dpix; + area_write(addr, (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix)); + area_write(addr+sizeof(spiffs_page_ix), (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix)); + + // delete all cache +#if SPIFFS_CACHE + spiffs_cache *cache = spiffs_get_cache(FS); + cache->cpage_use_map = 0; +#endif + + SPIFFS_check(FS); + + res = read_and_verify("file"); + TEST_CHECK(res >= 0); + + return TEST_RES_OK; +} TEST_END(page_cons2) + + + +TEST(page_cons3) { + int size = SPIFFS_DATA_PAGE_SIZE(FS)*3; + int res = test_create_and_write_file("file", size, size); + TEST_CHECK(res >= 0); + res = read_and_verify("file"); + TEST_CHECK(res >= 0); + + spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0); + TEST_CHECK(fd > 0); + spiffs_stat s; + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd); + + // modify object index, find object index header + spiffs_page_ix pix; + res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix); + TEST_CHECK(res >= 0); + + // set object index entry 1+2 lookup page + u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + sizeof(spiffs_page_object_ix_header) + 1 * sizeof(spiffs_page_ix); + spiffs_page_ix bad_pix_ref = SPIFFS_PAGES_PER_BLOCK(FS) * (*FS.block_count - 2); + area_write(addr, (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix)); + area_write(addr+sizeof(spiffs_page_ix), (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix)); + + // delete all cache +#if SPIFFS_CACHE + spiffs_cache *cache = spiffs_get_cache(FS); + cache->cpage_use_map = 0; +#endif + + SPIFFS_check(FS); + + res = read_and_verify("file"); + TEST_CHECK(res >= 0); + + return TEST_RES_OK; +} TEST_END(page_cons3) + + +TEST(page_cons_final) { + int size = SPIFFS_DATA_PAGE_SIZE(FS)*3; + int res = test_create_and_write_file("file", size, size); + TEST_CHECK(res >= 0); + res = read_and_verify("file"); + TEST_CHECK(res >= 0); + + spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0); + TEST_CHECK(fd > 0); + spiffs_stat s; + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd); + + // modify page header, make unfinalized + spiffs_page_ix pix; + res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 1, 0, &pix); + TEST_CHECK(res >= 0); + + // set page span ix 1 as unfinalized + u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + offsetof(spiffs_page_header, flags); + u8_t flags; + area_read(addr, (u8_t*)&flags, 1); + flags |= SPIFFS_PH_FLAG_FINAL; + area_write(addr, (u8_t*)&flags, 1); + + // delete all cache +#if SPIFFS_CACHE + spiffs_cache *cache = spiffs_get_cache(FS); + cache->cpage_use_map = 0; +#endif + + SPIFFS_check(FS); + + res = read_and_verify("file"); + TEST_CHECK(res >= 0); + + return TEST_RES_OK; +} TEST_END(page_cons_final) + + +TEST(index_cons1) { + int size = SPIFFS_DATA_PAGE_SIZE(FS)*SPIFFS_PAGES_PER_BLOCK(FS); + int res = test_create_and_write_file("file", size, size); + TEST_CHECK(res >= 0); + res = read_and_verify("file"); + TEST_CHECK(res >= 0); + + spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0); + TEST_CHECK(fd > 0); + spiffs_stat s; + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd); + + // modify lu entry data page index header + spiffs_page_ix pix; + res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix); + TEST_CHECK(res >= 0); + + printf(" deleting lu entry pix %04x\n", pix); + // reset lu entry to being erased, but keep page data + spiffs_obj_id obj_id = SPIFFS_OBJ_ID_DELETED; + spiffs_block_ix bix = SPIFFS_BLOCK_FOR_PAGE(FS, pix); + int entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(FS, pix); + u32_t addr = SPIFFS_BLOCK_TO_PADDR(FS, bix) + entry * sizeof(spiffs_obj_id); + + area_write(addr, (u8_t*)&obj_id, sizeof(spiffs_obj_id)); + +#if SPIFFS_CACHE + spiffs_cache *cache = spiffs_get_cache(FS); + cache->cpage_use_map = 0; +#endif + SPIFFS_check(FS); + + res = read_and_verify("file"); + TEST_CHECK(res >= 0); + + return TEST_RES_OK; +} TEST_END(index_cons1) + + +TEST(index_cons2) { + int size = SPIFFS_DATA_PAGE_SIZE(FS)*SPIFFS_PAGES_PER_BLOCK(FS); + int res = test_create_and_write_file("file", size, size); + TEST_CHECK(res >= 0); + res = read_and_verify("file"); + TEST_CHECK(res >= 0); + + spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0); + TEST_CHECK(fd > 0); + spiffs_stat s; + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd); + + // modify lu entry data page index header + spiffs_page_ix pix; + res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix); + TEST_CHECK(res >= 0); + + printf(" writing lu entry for index page, ix %04x, as data page\n", pix); + spiffs_obj_id obj_id = 0x1234; + spiffs_block_ix bix = SPIFFS_BLOCK_FOR_PAGE(FS, pix); + int entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(FS, pix); + u32_t addr = SPIFFS_BLOCK_TO_PADDR(FS, bix) + entry * sizeof(spiffs_obj_id); + + area_write(addr, (u8_t*)&obj_id, sizeof(spiffs_obj_id)); + +#if SPIFFS_CACHE + spiffs_cache *cache = spiffs_get_cache(FS); + cache->cpage_use_map = 0; +#endif + SPIFFS_check(FS); + + res = read_and_verify("file"); + TEST_CHECK(res >= 0); + + return TEST_RES_OK; +} TEST_END(index_cons2) + + +TEST(index_cons3) { + int size = SPIFFS_DATA_PAGE_SIZE(FS)*SPIFFS_PAGES_PER_BLOCK(FS); + int res = test_create_and_write_file("file", size, size); + TEST_CHECK(res >= 0); + res = read_and_verify("file"); + TEST_CHECK(res >= 0); + + spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0); + TEST_CHECK(fd > 0); + spiffs_stat s; + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd); + + // modify lu entry data page index header + spiffs_page_ix pix; + res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix); + TEST_CHECK(res >= 0); + + printf(" setting lu entry pix %04x to another index page\n", pix); + // reset lu entry to being erased, but keep page data + spiffs_obj_id obj_id = 1234 | SPIFFS_OBJ_ID_IX_FLAG; + spiffs_block_ix bix = SPIFFS_BLOCK_FOR_PAGE(FS, pix); + int entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(FS, pix); + u32_t addr = SPIFFS_BLOCK_TO_PADDR(FS, bix) + entry * sizeof(spiffs_obj_id); + + area_write(addr, (u8_t*)&obj_id, sizeof(spiffs_obj_id)); + +#if SPIFFS_CACHE + spiffs_cache *cache = spiffs_get_cache(FS); + cache->cpage_use_map = 0; +#endif + SPIFFS_check(FS); + + res = read_and_verify("file"); + TEST_CHECK(res >= 0); + + return TEST_RES_OK; +} TEST_END(index_cons3) + +TEST(index_cons4) { + int size = SPIFFS_DATA_PAGE_SIZE(FS)*SPIFFS_PAGES_PER_BLOCK(FS); + int res = test_create_and_write_file("file", size, size); + TEST_CHECK(res >= 0); + res = read_and_verify("file"); + TEST_CHECK(res >= 0); + + spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0); + TEST_CHECK(fd > 0); + spiffs_stat s; + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd); + + // modify lu entry data page index header, flags + spiffs_page_ix pix; + res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix); + TEST_CHECK(res >= 0); + + printf(" cue objix hdr deletion in page %04x\n", pix); + // set flags as deleting ix header + u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + offsetof(spiffs_page_header, flags); + u8_t flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_IXDELE); + + area_write(addr, (u8_t*)&flags, 1); + +#if SPIFFS_CACHE + spiffs_cache *cache = spiffs_get_cache(FS); + cache->cpage_use_map = 0; +#endif + SPIFFS_check(FS); + + return TEST_RES_OK; +} TEST_END(index_cons4) + + + +SUITE_END(check_tests) diff --git a/app/spiffs/test/test_dev.c b/app/spiffs/test/test_dev.c new file mode 100644 index 00000000..70a2a94b --- /dev/null +++ b/app/spiffs/test/test_dev.c @@ -0,0 +1,120 @@ +/* + * test_dev.c + * + * Created on: Jul 14, 2013 + * Author: petera + */ + + +#include "testrunner.h" +#include "test_spiffs.h" +#include "spiffs_nucleus.h" +#include "spiffs.h" +#include +#include +#include +#include +#include + + +SUITE(dev_tests) +void setup() { + _setup(); +} +void teardown() { + _teardown(); +} + +TEST(interrupted_write) { + char *name = "interrupt"; + char *name2 = "interrupt2"; + int res; + spiffs_file fd; + + const u32_t sz = SPIFFS_CFG_LOG_PAGE_SZ(FS)*8; + u8_t *buf = malloc(sz); + memrand(buf, sz); + + printf(" create reference file\n"); + fd = SPIFFS_open(FS, name, SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0); + TEST_CHECK(fd > 0); + clear_flash_ops_log(); + res = SPIFFS_write(FS, fd, buf, sz); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd); + + u32_t written = get_flash_ops_log_write_bytes(); + printf(" written bytes: %i\n", written); + + + printf(" create error file\n"); + fd = SPIFFS_open(FS, name2, SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0); + TEST_CHECK(fd > 0); + clear_flash_ops_log(); + invoke_error_after_write_bytes(written/2, 0); + res = SPIFFS_write(FS, fd, buf, sz); + SPIFFS_close(FS, fd); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_TEST); + + clear_flash_ops_log(); + +#if SPIFFS_CACHE + // delete all cache + spiffs_cache *cache = spiffs_get_cache(FS); + cache->cpage_use_map = 0; +#endif + + + printf(" read error file\n"); + fd = SPIFFS_open(FS, name2, SPIFFS_RDONLY, 0); + TEST_CHECK(fd > 0); + + spiffs_stat s; + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res >= 0); + printf(" file size: %i\n", s.size); + + if (s.size > 0) { + u8_t *buf2 = malloc(s.size); + res = SPIFFS_read(FS, fd, buf2, s.size); + TEST_CHECK(res >= 0); + + u32_t ix = 0; + for (ix = 0; ix < s.size; ix += 16) { + int i; + printf(" "); + for (i = 0; i < 16; i++) { + printf("%02x", buf[ix+i]); + } + printf(" "); + for (i = 0; i < 16; i++) { + printf("%02x", buf2[ix+i]); + } + printf("\n"); + } + free(buf2); + } + SPIFFS_close(FS, fd); + + + printf(" FS check\n"); + SPIFFS_check(FS); + + printf(" read error file again\n"); + fd = SPIFFS_open(FS, name2, SPIFFS_APPEND | SPIFFS_RDWR, 0); + TEST_CHECK(fd > 0); + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res >= 0); + printf(" file size: %i\n", s.size); + printf(" write file\n"); + res = SPIFFS_write(FS, fd, buf, sz); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd); + + free(buf); + + return TEST_RES_OK; + +} TEST_END(interrupted_write) + +SUITE_END(dev_tests) diff --git a/app/spiffs/test/test_hydrogen.c b/app/spiffs/test/test_hydrogen.c new file mode 100644 index 00000000..fdba1602 --- /dev/null +++ b/app/spiffs/test/test_hydrogen.c @@ -0,0 +1,1394 @@ +/* + * test_suites.c + * + * Created on: Jun 19, 2013 + * Author: petera + */ + + +#include "testrunner.h" +#include "test_spiffs.h" +#include "spiffs_nucleus.h" +#include "spiffs.h" +#include +#include +#include +#include +#include + +SUITE(hydrogen_tests) +void setup() { + _setup(); +} +void teardown() { + _teardown(); +} + +TEST(info) +{ + u32_t used, total; + int res = SPIFFS_info(FS, &total, &used); + TEST_CHECK(res == SPIFFS_OK); + TEST_CHECK(used == 0); + TEST_CHECK(total < __fs.cfg.phys_size); + return TEST_RES_OK; +} +TEST_END(info) + + +TEST(missing_file) +{ + spiffs_file fd = SPIFFS_open(FS, "this_wont_exist", SPIFFS_RDONLY, 0); + TEST_CHECK(fd < 0); + return TEST_RES_OK; +} +TEST_END(missing_file) + + +TEST(bad_fd) +{ + int res; + spiffs_stat s; + spiffs_file fd = SPIFFS_open(FS, "this_wont_exist", SPIFFS_RDONLY, 0); + TEST_CHECK(fd < 0); + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_BAD_DESCRIPTOR); + res = SPIFFS_fremove(FS, fd); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_BAD_DESCRIPTOR); + res = SPIFFS_lseek(FS, fd, 0, SPIFFS_SEEK_CUR); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_BAD_DESCRIPTOR); + res = SPIFFS_read(FS, fd, 0, 0); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_BAD_DESCRIPTOR); + res = SPIFFS_write(FS, fd, 0, 0); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_BAD_DESCRIPTOR); + return TEST_RES_OK; +} +TEST_END(bad_fd) + + +TEST(closed_fd) +{ + int res; + spiffs_stat s; + res = test_create_file("file"); + spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0); + TEST_CHECK(fd >= 0); + SPIFFS_close(FS, fd); + + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + res = SPIFFS_fremove(FS, fd); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + res = SPIFFS_lseek(FS, fd, 0, SPIFFS_SEEK_CUR); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + res = SPIFFS_read(FS, fd, 0, 0); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + res = SPIFFS_write(FS, fd, 0, 0); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + return TEST_RES_OK; +} +TEST_END(closed_fd) + + +TEST(deleted_same_fd) +{ + int res; + spiffs_stat s; + spiffs_file fd; + res = test_create_file("remove"); + fd = SPIFFS_open(FS, "remove", SPIFFS_RDWR, 0); + TEST_CHECK(fd >= 0); + fd = SPIFFS_open(FS, "remove", SPIFFS_RDWR, 0); + TEST_CHECK(fd >= 0); + res = SPIFFS_fremove(FS, fd); + TEST_CHECK(res >= 0); + + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + res = SPIFFS_fremove(FS, fd); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + res = SPIFFS_lseek(FS, fd, 0, SPIFFS_SEEK_CUR); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + res = SPIFFS_read(FS, fd, 0, 0); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + res = SPIFFS_write(FS, fd, 0, 0); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + + return TEST_RES_OK; +} +TEST_END(deleted_same_fd) + + +TEST(deleted_other_fd) +{ + int res; + spiffs_stat s; + spiffs_file fd, fd_orig; + res = test_create_file("remove"); + fd_orig = SPIFFS_open(FS, "remove", SPIFFS_RDWR, 0); + TEST_CHECK(fd_orig >= 0); + fd = SPIFFS_open(FS, "remove", SPIFFS_RDWR, 0); + TEST_CHECK(fd >= 0); + res = SPIFFS_fremove(FS, fd_orig); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd_orig); + + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + res = SPIFFS_fremove(FS, fd); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + res = SPIFFS_lseek(FS, fd, 0, SPIFFS_SEEK_CUR); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + res = SPIFFS_read(FS, fd, 0, 0); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + res = SPIFFS_write(FS, fd, 0, 0); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + + return TEST_RES_OK; +} +TEST_END(deleted_other_fd) + + +TEST(file_by_open) +{ + int res; + spiffs_stat s; + spiffs_file fd = SPIFFS_open(FS, "filebopen", SPIFFS_CREAT, 0); + TEST_CHECK(fd >= 0); + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res >= 0); + TEST_CHECK(strcmp((char*)s.name, "filebopen") == 0); + TEST_CHECK(s.size == 0); + SPIFFS_close(FS, fd); + + fd = SPIFFS_open(FS, "filebopen", SPIFFS_RDONLY, 0); + TEST_CHECK(fd >= 0); + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res >= 0); + TEST_CHECK(strcmp((char*)s.name, "filebopen") == 0); + TEST_CHECK(s.size == 0); + SPIFFS_close(FS, fd); + return TEST_RES_OK; +} +TEST_END(file_by_open) + + +TEST(file_by_creat) +{ + int res; + res = test_create_file("filebcreat"); + TEST_CHECK(res >= 0); + res = SPIFFS_creat(FS, "filebcreat", 0); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS)==SPIFFS_ERR_CONFLICTING_NAME); + return TEST_RES_OK; +} +TEST_END(file_by_creat) + +TEST(list_dir) +{ + int res; + + char *files[4] = { + "file1", + "file2", + "file3", + "file4" + }; + int file_cnt = sizeof(files)/sizeof(char *); + + int i; + + for (i = 0; i < file_cnt; i++) { + res = test_create_file(files[i]); + TEST_CHECK(res >= 0); + } + + spiffs_DIR d; + struct spiffs_dirent e; + struct spiffs_dirent *pe = &e; + + SPIFFS_opendir(FS, "/", &d); + int found = 0; + while ((pe = SPIFFS_readdir(&d, pe))) { + printf(" %s [%04x] size:%i\n", pe->name, pe->obj_id, pe->size); + for (i = 0; i < file_cnt; i++) { + if (strcmp(files[i], pe->name) == 0) { + found++; + break; + } + } + } + SPIFFS_closedir(&d); + + TEST_CHECK(found == file_cnt); + + return TEST_RES_OK; +} +TEST_END(list_dir) + + +TEST(open_by_dirent) { + int res; + + char *files[4] = { + "file1", + "file2", + "file3", + "file4" + }; + int file_cnt = sizeof(files)/sizeof(char *); + + int i; + int size = SPIFFS_DATA_PAGE_SIZE(FS); + + for (i = 0; i < file_cnt; i++) { + res = test_create_and_write_file(files[i], size, size); + TEST_CHECK(res >= 0); + } + + spiffs_DIR d; + struct spiffs_dirent e; + struct spiffs_dirent *pe = &e; + + int found = 0; + SPIFFS_opendir(FS, "/", &d); + while ((pe = SPIFFS_readdir(&d, pe))) { + spiffs_file fd = SPIFFS_open_by_dirent(FS, pe, SPIFFS_RDWR, 0); + TEST_CHECK(fd >= 0); + res = read_and_verify_fd(fd, pe->name); + TEST_CHECK(res == SPIFFS_OK); + fd = SPIFFS_open_by_dirent(FS, pe, SPIFFS_RDWR, 0); + TEST_CHECK(fd >= 0); + res = SPIFFS_fremove(FS, fd); + TEST_CHECK(res == SPIFFS_OK); + SPIFFS_close(FS, fd); + found++; + } + SPIFFS_closedir(&d); + + TEST_CHECK(found == file_cnt); + + found = 0; + SPIFFS_opendir(FS, "/", &d); + while ((pe = SPIFFS_readdir(&d, pe))) { + found++; + } + SPIFFS_closedir(&d); + + TEST_CHECK(found == 0); + + return TEST_RES_OK; + +} TEST_END(open_by_dirent) + + +TEST(rename) { + int res; + + char *src_name = "baah"; + char *dst_name = "booh"; + char *dst_name2 = "beeh"; + int size = SPIFFS_DATA_PAGE_SIZE(FS); + + res = test_create_and_write_file(src_name, size, size); + TEST_CHECK(res >= 0); + + res = SPIFFS_rename(FS, src_name, dst_name); + TEST_CHECK(res >= 0); + + res = SPIFFS_rename(FS, dst_name, dst_name); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_CONFLICTING_NAME); + + res = SPIFFS_rename(FS, src_name, dst_name2); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_NOT_FOUND); + + return TEST_RES_OK; +} TEST_END(rename) + + +TEST(remove_single_by_path) +{ + int res; + spiffs_file fd; + res = test_create_file("remove"); + TEST_CHECK(res >= 0); + res = SPIFFS_remove(FS, "remove"); + TEST_CHECK(res >= 0); + fd = SPIFFS_open(FS, "remove", SPIFFS_RDONLY, 0); + TEST_CHECK(fd < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_NOT_FOUND); + + return TEST_RES_OK; +} +TEST_END(remove_single_by_path) + + +TEST(remove_single_by_fd) +{ + int res; + spiffs_file fd; + res = test_create_file("remove"); + TEST_CHECK(res >= 0); + fd = SPIFFS_open(FS, "remove", SPIFFS_RDWR, 0); + TEST_CHECK(fd >= 0); + res = SPIFFS_fremove(FS, fd); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd); + fd = SPIFFS_open(FS, "remove", SPIFFS_RDONLY, 0); + TEST_CHECK(fd < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_NOT_FOUND); + + return TEST_RES_OK; +} +TEST_END(remove_single_by_fd) + + +TEST(write_big_file_chunks_page) +{ + int size = ((50*(FS)->cfg.phys_size)/100); + printf(" filesize %i\n", size); + int res = test_create_and_write_file("bigfile", size, SPIFFS_DATA_PAGE_SIZE(FS)); + TEST_CHECK(res >= 0); + res = read_and_verify("bigfile"); + TEST_CHECK(res >= 0); + + return TEST_RES_OK; +} +TEST_END(write_big_file_chunks_page) + + +TEST(write_big_files_chunks_page) +{ + char name[32]; + int f; + int files = 10; + int res; + int size = ((50*(FS)->cfg.phys_size)/100)/files; + printf(" filesize %i\n", size); + for (f = 0; f < files; f++) { + sprintf(name, "bigfile%i", f); + res = test_create_and_write_file(name, size, SPIFFS_DATA_PAGE_SIZE(FS)); + TEST_CHECK(res >= 0); + } + for (f = 0; f < files; f++) { + sprintf(name, "bigfile%i", f); + res = read_and_verify(name); + TEST_CHECK(res >= 0); + } + + return TEST_RES_OK; +} +TEST_END(write_big_files_chunks_page) + + +TEST(write_big_file_chunks_index) +{ + int size = ((50*(FS)->cfg.phys_size)/100); + printf(" filesize %i\n", size); + int res = test_create_and_write_file("bigfile", size, SPIFFS_DATA_PAGE_SIZE(FS) * SPIFFS_OBJ_HDR_IX_LEN(FS)); + TEST_CHECK(res >= 0); + res = read_and_verify("bigfile"); + TEST_CHECK(res >= 0); + + return TEST_RES_OK; +} +TEST_END(write_big_file_chunks_index) + + +TEST(write_big_files_chunks_index) +{ + char name[32]; + int f; + int files = 10; + int res; + int size = ((50*(FS)->cfg.phys_size)/100)/files; + printf(" filesize %i\n", size); + for (f = 0; f < files; f++) { + sprintf(name, "bigfile%i", f); + res = test_create_and_write_file(name, size, SPIFFS_DATA_PAGE_SIZE(FS) * SPIFFS_OBJ_HDR_IX_LEN(FS)); + TEST_CHECK(res >= 0); + } + for (f = 0; f < files; f++) { + sprintf(name, "bigfile%i", f); + res = read_and_verify(name); + TEST_CHECK(res >= 0); + } + + return TEST_RES_OK; +} +TEST_END(write_big_files_chunks_index) + + +TEST(write_big_file_chunks_huge) +{ + int size = (FS_PURE_DATA_PAGES(FS) / 2) * SPIFFS_DATA_PAGE_SIZE(FS); + printf(" filesize %i\n", size); + int res = test_create_and_write_file("bigfile", size, size); + TEST_CHECK(res >= 0); + res = read_and_verify("bigfile"); + TEST_CHECK(res >= 0); + + return TEST_RES_OK; +} +TEST_END(write_big_file_chunks_huge) + + +TEST(write_big_files_chunks_huge) +{ + char name[32]; + int f; + int files = 10; + int res; + int size = ((50*(FS)->cfg.phys_size)/100)/files; + printf(" filesize %i\n", size); + for (f = 0; f < files; f++) { + sprintf(name, "bigfile%i", f); + res = test_create_and_write_file(name, size, size); + TEST_CHECK(res >= 0); + } + for (f = 0; f < files; f++) { + sprintf(name, "bigfile%i", f); + res = read_and_verify(name); + TEST_CHECK(res >= 0); + } + + return TEST_RES_OK; +} +TEST_END(write_big_files_chunks_huge) + + +TEST(truncate_big_file) +{ + int size = (FS_PURE_DATA_PAGES(FS) / 2) * SPIFFS_DATA_PAGE_SIZE(FS); + printf(" filesize %i\n", size); + int res = test_create_and_write_file("bigfile", size, size); + TEST_CHECK(res >= 0); + res = read_and_verify("bigfile"); + TEST_CHECK(res >= 0); + spiffs_file fd = SPIFFS_open(FS, "bigfile", SPIFFS_RDWR, 0); + TEST_CHECK(fd > 0); + res = SPIFFS_fremove(FS, fd); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd); + + fd = SPIFFS_open(FS, "bigfile", SPIFFS_RDWR, 0); + TEST_CHECK(fd < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_NOT_FOUND); + + return TEST_RES_OK; +} +TEST_END(truncate_big_file) + + +TEST(simultaneous_write) { + int res = SPIFFS_creat(FS, "simul1", 0); + TEST_CHECK(res >= 0); + + spiffs_file fd1 = SPIFFS_open(FS, "simul1", SPIFFS_RDWR, 0); + TEST_CHECK(fd1 > 0); + spiffs_file fd2 = SPIFFS_open(FS, "simul1", SPIFFS_RDWR, 0); + TEST_CHECK(fd2 > 0); + spiffs_file fd3 = SPIFFS_open(FS, "simul1", SPIFFS_RDWR, 0); + TEST_CHECK(fd3 > 0); + + u8_t data1 = 1; + u8_t data2 = 2; + u8_t data3 = 3; + + res = SPIFFS_write(FS, fd1, &data1, 1); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd1); + res = SPIFFS_write(FS, fd2, &data2, 1); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd2); + res = SPIFFS_write(FS, fd3, &data3, 1); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd3); + + spiffs_stat s; + res = SPIFFS_stat(FS, "simul1", &s); + TEST_CHECK(res >= 0); + + TEST_CHECK(s.size == 1); + + u8_t rdata; + spiffs_file fd = SPIFFS_open(FS, "simul1", SPIFFS_RDONLY, 0); + TEST_CHECK(fd > 0); + res = SPIFFS_read(FS, fd, &rdata, 1); + TEST_CHECK(res >= 0); + + TEST_CHECK(rdata == data3); + + return TEST_RES_OK; +} +TEST_END(simultaneous_write) + + +TEST(simultaneous_write_append) { + int res = SPIFFS_creat(FS, "simul2", 0); + TEST_CHECK(res >= 0); + + spiffs_file fd1 = SPIFFS_open(FS, "simul2", SPIFFS_RDWR | SPIFFS_APPEND, 0); + TEST_CHECK(fd1 > 0); + spiffs_file fd2 = SPIFFS_open(FS, "simul2", SPIFFS_RDWR | SPIFFS_APPEND, 0); + TEST_CHECK(fd2 > 0); + spiffs_file fd3 = SPIFFS_open(FS, "simul2", SPIFFS_RDWR | SPIFFS_APPEND, 0); + TEST_CHECK(fd3 > 0); + + u8_t data1 = 1; + u8_t data2 = 2; + u8_t data3 = 3; + + res = SPIFFS_write(FS, fd1, &data1, 1); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd1); + res = SPIFFS_write(FS, fd2, &data2, 1); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd2); + res = SPIFFS_write(FS, fd3, &data3, 1); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd3); + + spiffs_stat s; + res = SPIFFS_stat(FS, "simul2", &s); + TEST_CHECK(res >= 0); + + TEST_CHECK(s.size == 3); + + u8_t rdata[3]; + spiffs_file fd = SPIFFS_open(FS, "simul2", SPIFFS_RDONLY, 0); + TEST_CHECK(fd > 0); + res = SPIFFS_read(FS, fd, &rdata, 3); + TEST_CHECK(res >= 0); + + TEST_CHECK(rdata[0] == data1); + TEST_CHECK(rdata[1] == data2); + TEST_CHECK(rdata[2] == data3); + + return TEST_RES_OK; +} +TEST_END(simultaneous_write_append) + + +TEST(file_uniqueness) +{ + int res; + spiffs_file fd; + char fname[32]; + int files = ((SPIFFS_CFG_PHYS_SZ(FS) * 75) / 100) / 2 / SPIFFS_CFG_LOG_PAGE_SZ(FS); + //(FS_PURE_DATA_PAGES(FS) / 2) - SPIFFS_PAGES_PER_BLOCK(FS)*8; + int i; + printf(" creating %i files\n", files); + for (i = 0; i < files; i++) { + char content[20]; + sprintf(fname, "file%i", i); + sprintf(content, "%i", i); + res = test_create_file(fname); + TEST_CHECK(res >= 0); + fd = SPIFFS_open(FS, fname, SPIFFS_APPEND | SPIFFS_RDWR, 0); + TEST_CHECK(fd >= 0); + res = SPIFFS_write(FS, fd, content, strlen(content)+1); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd); + } + printf(" checking %i files\n", files); + for (i = 0; i < files; i++) { + char content[20]; + char ref_content[20]; + sprintf(fname, "file%i", i); + sprintf(content, "%i", i); + fd = SPIFFS_open(FS, fname, SPIFFS_RDONLY, 0); + TEST_CHECK(fd >= 0); + res = SPIFFS_read(FS, fd, ref_content, strlen(content)+1); + TEST_CHECK(res >= 0); + TEST_CHECK(strcmp(ref_content, content) == 0); + SPIFFS_close(FS, fd); + } + printf(" removing %i files\n", files/2); + for (i = 0; i < files; i += 2) { + sprintf(fname, "file%i", i); + res = SPIFFS_remove(FS, fname); + TEST_CHECK(res >= 0); + } + printf(" creating %i files\n", files/2); + for (i = 0; i < files; i += 2) { + char content[20]; + sprintf(fname, "file%i", i); + sprintf(content, "new%i", i); + res = test_create_file(fname); + TEST_CHECK(res >= 0); + fd = SPIFFS_open(FS, fname, SPIFFS_APPEND | SPIFFS_RDWR, 0); + TEST_CHECK(fd >= 0); + res = SPIFFS_write(FS, fd, content, strlen(content)+1); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd); + } + printf(" checking %i files\n", files); + for (i = 0; i < files; i++) { + char content[20]; + char ref_content[20]; + sprintf(fname, "file%i", i); + if ((i & 1) == 0) { + sprintf(content, "new%i", i); + } else { + sprintf(content, "%i", i); + } + fd = SPIFFS_open(FS, fname, SPIFFS_RDONLY, 0); + TEST_CHECK(fd >= 0); + res = SPIFFS_read(FS, fd, ref_content, strlen(content)+1); + TEST_CHECK(res >= 0); + TEST_CHECK(strcmp(ref_content, content) == 0); + SPIFFS_close(FS, fd); + } + + return TEST_RES_OK; +} +TEST_END(file_uniqueness) + +int create_and_read_back(int size, int chunk) { + char *name = "file"; + spiffs_file fd; + s32_t res; + + u8_t *buf = malloc(size); + memrand(buf, size); + + res = test_create_file(name); + CHECK(res >= 0); + fd = SPIFFS_open(FS, name, SPIFFS_APPEND | SPIFFS_RDWR, 0); + CHECK(fd >= 0); + res = SPIFFS_write(FS, fd, buf, size); + CHECK(res >= 0); + + spiffs_stat stat; + res = SPIFFS_fstat(FS, fd, &stat); + CHECK(res >= 0); + CHECK(stat.size == size); + + SPIFFS_close(FS, fd); + + fd = SPIFFS_open(FS, name, SPIFFS_RDONLY, 0); + CHECK(fd >= 0); + + u8_t *rbuf = malloc(size); + int offs = 0; + while (offs < size) { + int len = MIN(size - offs, chunk); + res = SPIFFS_read(FS, fd, &rbuf[offs], len); + CHECK(res >= 0); + CHECK(memcmp(&rbuf[offs], &buf[offs], len) == 0); + + offs += chunk; + } + + CHECK(memcmp(&rbuf[0], &buf[0], size) == 0); + + SPIFFS_close(FS, fd); + + free(rbuf); + free(buf); + + return 0; +} + +TEST(read_chunk_1) +{ + TEST_CHECK(create_and_read_back(SPIFFS_DATA_PAGE_SIZE(FS)*8, 1) == 0); + return TEST_RES_OK; +} +TEST_END(read_chunk_1) + + +TEST(read_chunk_page) +{ + TEST_CHECK(create_and_read_back(SPIFFS_DATA_PAGE_SIZE(FS)*(SPIFFS_PAGES_PER_BLOCK(FS) - SPIFFS_OBJ_LOOKUP_PAGES(FS))*2, + SPIFFS_DATA_PAGE_SIZE(FS)) == 0); + return TEST_RES_OK; +} +TEST_END(read_chunk_page) + + +TEST(read_chunk_index) +{ + TEST_CHECK(create_and_read_back(SPIFFS_DATA_PAGE_SIZE(FS)*(SPIFFS_PAGES_PER_BLOCK(FS) - SPIFFS_OBJ_LOOKUP_PAGES(FS))*4, + SPIFFS_DATA_PAGE_SIZE(FS)*(SPIFFS_PAGES_PER_BLOCK(FS) - SPIFFS_OBJ_LOOKUP_PAGES(FS))) == 0); + return TEST_RES_OK; +} +TEST_END(read_chunk_index) + + +TEST(read_chunk_huge) +{ + int sz = (2*(FS)->cfg.phys_size)/3; + TEST_CHECK(create_and_read_back(sz, sz) == 0); + return TEST_RES_OK; +} +TEST_END(read_chunk_huge) + + +TEST(read_beyond) +{ + char *name = "file"; + spiffs_file fd; + s32_t res; + u32_t size = SPIFFS_DATA_PAGE_SIZE(FS)*2; + + u8_t *buf = malloc(size); + memrand(buf, size); + + res = test_create_file(name); + CHECK(res >= 0); + fd = SPIFFS_open(FS, name, SPIFFS_APPEND | SPIFFS_RDWR, 0); + CHECK(fd >= 0); + res = SPIFFS_write(FS, fd, buf, size); + CHECK(res >= 0); + + spiffs_stat stat; + res = SPIFFS_fstat(FS, fd, &stat); + CHECK(res >= 0); + CHECK(stat.size == size); + + SPIFFS_close(FS, fd); + + fd = SPIFFS_open(FS, name, SPIFFS_RDONLY, 0); + CHECK(fd >= 0); + + u8_t *rbuf = malloc(size+10); + res = SPIFFS_read(FS, fd, rbuf, size+10); + + SPIFFS_close(FS, fd); + + free(rbuf); + free(buf); + + TEST_CHECK(res == size); + + return TEST_RES_OK; +} +TEST_END(read_beyond) + + +TEST(bad_index_1) { + int size = SPIFFS_DATA_PAGE_SIZE(FS)*3; + int res = test_create_and_write_file("file", size, size); + TEST_CHECK(res >= 0); + res = read_and_verify("file"); + TEST_CHECK(res >= 0); + + spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0); + TEST_CHECK(fd > 0); + spiffs_stat s; + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd); + + // modify object index, find object index header + spiffs_page_ix pix; + res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix); + TEST_CHECK(res >= 0); + + // set object index entry 2 to a bad page, free + u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + sizeof(spiffs_page_object_ix_header) + 2 * sizeof(spiffs_page_ix); + spiffs_page_ix bad_pix_ref = (spiffs_page_ix)-1; + area_write(addr, (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix)); + +#if SPIFFS_CACHE + // delete all cache + spiffs_cache *cache = spiffs_get_cache(FS); + cache->cpage_use_map = 0; +#endif + + res = read_and_verify("file"); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_INDEX_REF_FREE); + + return TEST_RES_OK; +} TEST_END(bad_index_1) + + +TEST(bad_index_2) { + int size = SPIFFS_DATA_PAGE_SIZE(FS)*3; + int res = test_create_and_write_file("file", size, size); + TEST_CHECK(res >= 0); + res = read_and_verify("file"); + TEST_CHECK(res >= 0); + + spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0); + TEST_CHECK(fd > 0); + spiffs_stat s; + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd); + + // modify object index, find object index header + spiffs_page_ix pix; + res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix); + TEST_CHECK(res >= 0); + + // set object index entry 2 to a bad page, lu + u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + sizeof(spiffs_page_object_ix_header) + 2 * sizeof(spiffs_page_ix); + spiffs_page_ix bad_pix_ref = SPIFFS_OBJ_LOOKUP_PAGES(FS)-1; + area_write(addr, (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix)); + +#if SPIFFS_CACHE + // delete all cache + spiffs_cache *cache = spiffs_get_cache(FS); + cache->cpage_use_map = 0; +#endif + + res = read_and_verify("file"); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_INDEX_REF_LU); + + return TEST_RES_OK; +} TEST_END(bad_index_2) + + +TEST(lseek_simple_modification) { + int res; + spiffs_file fd; + char *fname = "seekfile"; + int i; + int len = 4096; + fd = SPIFFS_open(FS, fname, SPIFFS_TRUNC | SPIFFS_CREAT | SPIFFS_RDWR, 0); + TEST_CHECK(fd > 0); + int pfd = open(make_test_fname(fname), O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); + u8_t *buf = malloc(len); + memrand(buf, len); + res = SPIFFS_write(FS, fd, buf, len); + TEST_CHECK(res >= 0); + write(pfd, buf, len); + free(buf); + res = read_and_verify(fname); + TEST_CHECK(res >= 0); + + res = SPIFFS_lseek(FS, fd, len/2, SPIFFS_SEEK_SET); + TEST_CHECK(res >= 0); + lseek(pfd, len/2, SEEK_SET); + len = len/4; + buf = malloc(len); + memrand(buf, len); + res = SPIFFS_write(FS, fd, buf, len); + TEST_CHECK(res >= 0); + write(pfd, buf, len); + free(buf); + + res = read_and_verify(fname); + TEST_CHECK(res >= 0); + + SPIFFS_close(FS, fd); + close(pfd); + + return TEST_RES_OK; +} +TEST_END(lseek_simple_modification) + + +TEST(lseek_modification_append) { + int res; + spiffs_file fd; + char *fname = "seekfile"; + int i; + int len = 4096; + fd = SPIFFS_open(FS, fname, SPIFFS_TRUNC | SPIFFS_CREAT | SPIFFS_RDWR, 0); + TEST_CHECK(fd > 0); + int pfd = open(make_test_fname(fname), O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); + u8_t *buf = malloc(len); + memrand(buf, len); + res = SPIFFS_write(FS, fd, buf, len); + TEST_CHECK(res >= 0); + write(pfd, buf, len); + free(buf); + res = read_and_verify(fname); + TEST_CHECK(res >= 0); + + res = SPIFFS_lseek(FS, fd, len/2, SPIFFS_SEEK_SET); + TEST_CHECK(res >= 0); + lseek(pfd, len/2, SEEK_SET); + + buf = malloc(len); + memrand(buf, len); + res = SPIFFS_write(FS, fd, buf, len); + TEST_CHECK(res >= 0); + write(pfd, buf, len); + free(buf); + + res = read_and_verify(fname); + TEST_CHECK(res >= 0); + + SPIFFS_close(FS, fd); + close(pfd); + + return TEST_RES_OK; +} +TEST_END(lseek_modification_append) + + +TEST(lseek_modification_append_multi) { + int res; + spiffs_file fd; + char *fname = "seekfile"; + int len = 1024; + int runs = (FS_PURE_DATA_PAGES(FS) / 2) * SPIFFS_DATA_PAGE_SIZE(FS) / (len/2); + + fd = SPIFFS_open(FS, fname, SPIFFS_TRUNC | SPIFFS_CREAT | SPIFFS_RDWR, 0); + TEST_CHECK(fd > 0); + int pfd = open(make_test_fname(fname), O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); + u8_t *buf = malloc(len); + memrand(buf, len); + res = SPIFFS_write(FS, fd, buf, len); + TEST_CHECK(res >= 0); + write(pfd, buf, len); + free(buf); + res = read_and_verify(fname); + TEST_CHECK(res >= 0); + + while (runs--) { + res = SPIFFS_lseek(FS, fd, -len/2, SPIFFS_SEEK_END); + TEST_CHECK(res >= 0); + lseek(pfd, -len/2, SEEK_END); + + buf = malloc(len); + memrand(buf, len); + res = SPIFFS_write(FS, fd, buf, len); + TEST_CHECK(res >= 0); + write(pfd, buf, len); + free(buf); + + res = read_and_verify(fname); + TEST_CHECK(res >= 0); + } + + SPIFFS_close(FS, fd); + close(pfd); + + return TEST_RES_OK; +} +TEST_END(lseek_modification_append_multi) + + +TEST(lseek_read) { + int res; + spiffs_file fd; + char *fname = "seekfile"; + int len = (FS_PURE_DATA_PAGES(FS) / 2) * SPIFFS_DATA_PAGE_SIZE(FS); + int runs = 100000; + + fd = SPIFFS_open(FS, fname, SPIFFS_TRUNC | SPIFFS_CREAT | SPIFFS_RDWR, 0); + TEST_CHECK(fd > 0); + u8_t *refbuf = malloc(len); + memrand(refbuf, len); + res = SPIFFS_write(FS, fd, refbuf, len); + TEST_CHECK(res >= 0); + + int offs = 0; + res = SPIFFS_lseek(FS, fd, 0, SPIFFS_SEEK_SET); + TEST_CHECK(res >= 0); + + while (runs--) { + int i; + u8_t buf[64]; + if (offs + 41 + sizeof(buf) >= len) { + offs = (offs + 41 + sizeof(buf)) % len; + res = SPIFFS_lseek(FS, fd, offs, SPIFFS_SEEK_SET); + TEST_CHECK(res >= 0); + } + res = SPIFFS_lseek(FS, fd, 41, SPIFFS_SEEK_CUR); + TEST_CHECK(res >= 0); + offs += 41; + res = SPIFFS_read(FS, fd, buf, sizeof(buf)); + TEST_CHECK(res >= 0); + for (i = 0; i < sizeof(buf); i++) { + if (buf[i] != refbuf[offs+i]) { + printf(" mismatch at offs %i\n", offs); + } + TEST_CHECK(buf[i] == refbuf[offs+i]); + } + offs += sizeof(buf); + + res = SPIFFS_lseek(FS, fd, -((u32_t)sizeof(buf)+11), SPIFFS_SEEK_CUR); + TEST_CHECK(res >= 0); + offs -= (sizeof(buf)+11); + res = SPIFFS_read(FS, fd, buf, sizeof(buf)); + TEST_CHECK(res >= 0); + for (i = 0; i < sizeof(buf); i++) { + if (buf[i] != refbuf[offs+i]) { + printf(" mismatch at offs %i\n", offs); + } + TEST_CHECK(buf[i] == refbuf[offs+i]); + } + offs += sizeof(buf); + } + + free(refbuf); + SPIFFS_close(FS, fd); + + return TEST_RES_OK; +} +TEST_END(lseek_read) + + +TEST(write_small_file_chunks_1) +{ + int res = test_create_and_write_file("smallfile", 256, 1); + TEST_CHECK(res >= 0); + res = read_and_verify("smallfile"); + TEST_CHECK(res >= 0); + + return TEST_RES_OK; +} +TEST_END(write_small_file_chunks_1) + + +TEST(write_small_files_chunks_1) +{ + char name[32]; + int f; + int size = 512; + int files = ((20*(FS)->cfg.phys_size)/100)/size; + int res; + for (f = 0; f < files; f++) { + sprintf(name, "smallfile%i", f); + res = test_create_and_write_file(name, size, 1); + TEST_CHECK(res >= 0); + } + for (f = 0; f < files; f++) { + sprintf(name, "smallfile%i", f); + res = read_and_verify(name); + TEST_CHECK(res >= 0); + } + + return TEST_RES_OK; +} +TEST_END(write_small_files_chunks_1) + +TEST(write_big_file_chunks_1) +{ + int size = ((50*(FS)->cfg.phys_size)/100); + printf(" filesize %i\n", size); + int res = test_create_and_write_file("bigfile", size, 1); + TEST_CHECK(res >= 0); + res = read_and_verify("bigfile"); + TEST_CHECK(res >= 0); + + return TEST_RES_OK; +} +TEST_END(write_big_file_chunks_1) + +TEST(write_big_files_chunks_1) +{ + char name[32]; + int f; + int files = 10; + int res; + int size = ((50*(FS)->cfg.phys_size)/100)/files; + printf(" filesize %i\n", size); + for (f = 0; f < files; f++) { + sprintf(name, "bigfile%i", f); + res = test_create_and_write_file(name, size, 1); + TEST_CHECK(res >= 0); + } + for (f = 0; f < files; f++) { + sprintf(name, "bigfile%i", f); + res = read_and_verify(name); + TEST_CHECK(res >= 0); + } + + return TEST_RES_OK; +} +TEST_END(write_big_files_chunks_1) + + +TEST(long_run_config_many_small_one_long) +{ + tfile_conf cfgs[] = { + { .tsize = LARGE, .ttype = MODIFIED, .tlife = LONG + }, + { .tsize = SMALL, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = LONG + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = LONG + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = LONG + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = LONG + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = LONG + }, + }; + + int res = run_file_config(sizeof(cfgs)/sizeof(cfgs[0]), &cfgs[0], 206, 5, 0); + TEST_CHECK(res >= 0); + return TEST_RES_OK; +} +TEST_END(long_run_config_many_small_one_long) + +TEST(long_run_config_many_medium) +{ + tfile_conf cfgs[] = { + { .tsize = MEDIUM, .ttype = MODIFIED, .tlife = LONG + }, + { .tsize = MEDIUM, .ttype = APPENDED, .tlife = LONG + }, + { .tsize = LARGE, .ttype = MODIFIED, .tlife = LONG + }, + { .tsize = MEDIUM, .ttype = APPENDED, .tlife = LONG + }, + { .tsize = MEDIUM, .ttype = MODIFIED, .tlife = LONG + }, + { .tsize = MEDIUM, .ttype = MODIFIED, .tlife = LONG + }, + { .tsize = MEDIUM, .ttype = APPENDED, .tlife = LONG + }, + { .tsize = LARGE, .ttype = MODIFIED, .tlife = LONG + }, + { .tsize = MEDIUM, .ttype = APPENDED, .tlife = LONG + }, + { .tsize = MEDIUM, .ttype = MODIFIED, .tlife = LONG + }, + { .tsize = MEDIUM, .ttype = MODIFIED, .tlife = LONG + }, + { .tsize = MEDIUM, .ttype = APPENDED, .tlife = LONG + }, + { .tsize = LARGE, .ttype = MODIFIED, .tlife = LONG + }, + { .tsize = MEDIUM, .ttype = APPENDED, .tlife = LONG + }, + { .tsize = MEDIUM, .ttype = MODIFIED, .tlife = LONG + }, + }; + + int res = run_file_config(sizeof(cfgs)/sizeof(cfgs[0]), &cfgs[0], 305, 5, 0); + TEST_CHECK(res >= 0); + return TEST_RES_OK; +} +TEST_END(long_run_config_many_medium) + + +TEST(long_run_config_many_small) +{ + tfile_conf cfgs[] = { + { .tsize = SMALL, .ttype = APPENDED, .tlife = LONG + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = APPENDED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = UNTAMPERED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = APPENDED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = APPENDED, .tlife = LONG + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = UNTAMPERED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = APPENDED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = UNTAMPERED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = APPENDED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = UNTAMPERED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = MEDIUM + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT + }, + }; + + int res = run_file_config(sizeof(cfgs)/sizeof(cfgs[0]), &cfgs[0], 115, 6, 0); + TEST_CHECK(res >= 0); + return TEST_RES_OK; +} +TEST_END(long_run_config_many_small) + + +TEST(long_run) +{ + tfile_conf cfgs[] = { + { .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = SHORT + }, + { .tsize = MEDIUM, .ttype = MODIFIED, .tlife = SHORT + }, + { .tsize = MEDIUM, .ttype = APPENDED, .tlife = SHORT + }, + }; + + int macro_runs = 500; + printf(" "); + u32_t clob_size = SPIFFS_CFG_PHYS_SZ(FS)/4; + int res = test_create_and_write_file("long_clobber", clob_size, clob_size); + TEST_CHECK(res >= 0); + + res = read_and_verify("long_clobber"); + TEST_CHECK(res >= 0); + + while (macro_runs--) { + //printf(" ---- run %i ----\n", macro_runs); + if ((macro_runs % 20) == 0) { + printf("."); + fflush(stdout); + } + res = run_file_config(sizeof(cfgs)/sizeof(cfgs[0]), &cfgs[0], 11, 2, 0); + TEST_CHECK(res >= 0); + } + printf("\n"); + + res = read_and_verify("long_clobber"); + TEST_CHECK(res >= 0); + + res = SPIFFS_check(FS); + TEST_CHECK(res >= 0); + + return TEST_RES_OK; +} +TEST_END(long_run) + +SUITE_END(hydrogen_tests) + diff --git a/app/spiffs/test/test_spiffs.c b/app/spiffs/test/test_spiffs.c new file mode 100644 index 00000000..20335663 --- /dev/null +++ b/app/spiffs/test/test_spiffs.c @@ -0,0 +1,746 @@ +/* + * test_spiffs.c + * + * Created on: Jun 19, 2013 + * Author: petera + */ + + +#include +#include +#include + +#include "params_test.h" +#include "spiffs.h" +#include "spiffs_nucleus.h" + +#include "testrunner.h" + +#include "test_spiffs.h" + +#include +#include +#include +#include +#include + +static unsigned char area[PHYS_FLASH_SIZE]; + +static int erases[256]; +static char _path[256]; +static u32_t bytes_rd = 0; +static u32_t bytes_wr = 0; +static u32_t reads = 0; +static u32_t writes = 0; +static u32_t error_after_bytes_written = 0; +static u32_t error_after_bytes_read = 0; +static char error_after_bytes_written_once_only = 0; +static char error_after_bytes_read_once_only = 0; +static char log_flash_ops = 1; +static u32_t fs_check_fixes = 0; + +spiffs __fs; +static u8_t _work[LOG_PAGE*2]; +static u8_t _fds[FD_BUF_SIZE]; +static u8_t _cache[CACHE_BUF_SIZE]; + +static int check_valid_flash = 1; + +#define TEST_PATH "test_data/" + +char *make_test_fname(const char *name) { + sprintf(_path, "%s%s", TEST_PATH, name); + return _path; +} + +void clear_test_path() { + DIR *dp; + struct dirent *ep; + dp = opendir(TEST_PATH); + + if (dp != NULL) { + while ((ep = readdir(dp))) { + if (ep->d_name[0] != '.') { + sprintf(_path, "%s%s", TEST_PATH, ep->d_name); + remove(_path); + } + } + closedir(dp); + } +} + +static s32_t _read(u32_t addr, u32_t size, u8_t *dst) { + if (log_flash_ops) { + bytes_rd += size; + reads++; + if (error_after_bytes_read > 0 && bytes_rd >= error_after_bytes_read) { + if (error_after_bytes_read_once_only) { + error_after_bytes_read = 0; + } + return SPIFFS_ERR_TEST; + } + } + if (addr < __fs.cfg.phys_addr) { + printf("FATAL read addr too low %08x < %08x\n", addr, SPIFFS_PHYS_ADDR); + exit(0); + } + if (addr + size > __fs.cfg.phys_addr + __fs.cfg.phys_size) { + printf("FATAL read addr too high %08x + %08x > %08x\n", addr, size, SPIFFS_PHYS_ADDR + SPIFFS_FLASH_SIZE); + exit(0); + } + memcpy(dst, &area[addr], size); + return 0; +} + +static s32_t _write(u32_t addr, u32_t size, u8_t *src) { + int i; + //printf("wr %08x %i\n", addr, size); + if (log_flash_ops) { + bytes_wr += size; + writes++; + if (error_after_bytes_written > 0 && bytes_wr >= error_after_bytes_written) { + if (error_after_bytes_written_once_only) { + error_after_bytes_written = 0; + } + return SPIFFS_ERR_TEST; + } + } + + if (addr < __fs.cfg.phys_addr) { + printf("FATAL write addr too low %08x < %08x\n", addr, SPIFFS_PHYS_ADDR); + exit(0); + } + if (addr + size > __fs.cfg.phys_addr + __fs.cfg.phys_size) { + printf("FATAL write addr too high %08x + %08x > %08x\n", addr, size, SPIFFS_PHYS_ADDR + SPIFFS_FLASH_SIZE); + exit(0); + } + + for (i = 0; i < size; i++) { + if (((addr + i) & (__fs.cfg.log_page_size-1)) != offsetof(spiffs_page_header, flags)) { + if (check_valid_flash && ((area[addr + i] ^ src[i]) & src[i])) { + printf("trying to write %02x to %02x at addr %08x\n", src[i], area[addr + i], addr+i); + spiffs_page_ix pix = (addr + i) / LOG_PAGE; + dump_page(&__fs, pix); + return -1; + } + } + area[addr + i] &= src[i]; + } + return 0; +} + +static s32_t _erase(u32_t addr, u32_t size) { + if (addr & (__fs.cfg.phys_erase_block-1)) { + printf("trying to erase at addr %08x, out of boundary\n", addr); + return -1; + } + if (size & (__fs.cfg.phys_erase_block-1)) { + printf("trying to erase at with size %08x, out of boundary\n", size); + return -1; + } + erases[(addr-__fs.cfg.phys_addr)/__fs.cfg.phys_erase_block]++; + memset(&area[addr], 0xff, size); + return 0; +} + +void hexdump_mem(u8_t *b, u32_t len) { + while (len--) { + if ((((intptr_t)b)&0x1f) == 0) { + printf("\n"); + } + printf("%02x", *b++); + } + printf("\n"); +} + +void hexdump(u32_t addr, u32_t len) { + int remainder = (addr % 32) == 0 ? 0 : 32 - (addr % 32); + u32_t a; + for (a = addr - remainder; a < addr+len; a++) { + if ((a & 0x1f) == 0) { + if (a != addr) { + printf(" "); + int j; + for (j = 0; j < 32; j++) { + if (a-32+j < addr) + printf(" "); + else { + printf("%c", (area[a-32+j] < 32 || area[a-32+j] >= 0x7f) ? '.' : area[a-32+j]); + } + } + } + printf("%s %08x: ", a<=addr ? "":"\n", a); + } + if (a < addr) { + printf(" "); + } else { + printf("%02x", area[a]); + } + } + int j; + printf(" "); + for (j = 0; j < 32; j++) { + if (a-32+j < addr) + printf(" "); + else { + printf("%c", (area[a-32+j] < 32 || area[a-32+j] >= 0x7f) ? '.' : area[a-32+j]); + } + } + printf("\n"); +} + +void dump_page(spiffs *fs, spiffs_page_ix p) { + printf("page %04x ", p); + u32_t addr = SPIFFS_PAGE_TO_PADDR(fs, p); + if (p % SPIFFS_PAGES_PER_BLOCK(fs) < SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + // obj lu page + printf("OBJ_LU"); + } else { + u32_t obj_id_addr = SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs , p)) + + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, p) * sizeof(spiffs_obj_id); + spiffs_obj_id obj_id = *((spiffs_obj_id *)&area[obj_id_addr]); + // data page + spiffs_page_header *ph = (spiffs_page_header *)&area[addr]; + printf("DATA %04x:%04x ", obj_id, ph->span_ix); + printf("%s", ((ph->flags & SPIFFS_PH_FLAG_FINAL) == 0) ? "FIN " : "fin "); + printf("%s", ((ph->flags & SPIFFS_PH_FLAG_DELET) == 0) ? "DEL " : "del "); + printf("%s", ((ph->flags & SPIFFS_PH_FLAG_INDEX) == 0) ? "IDX " : "idx "); + printf("%s", ((ph->flags & SPIFFS_PH_FLAG_USED) == 0) ? "USD " : "usd "); + printf("%s ", ((ph->flags & SPIFFS_PH_FLAG_IXDELE) == 0) ? "IDL " : "idl "); + if (obj_id & SPIFFS_OBJ_ID_IX_FLAG) { + // object index + printf("OBJ_IX"); + if (ph->span_ix == 0) { + printf("_HDR "); + spiffs_page_object_ix_header *oix_hdr = (spiffs_page_object_ix_header *)&area[addr]; + printf("'%s' %i bytes type:%02x", oix_hdr->name, oix_hdr->size, oix_hdr->type); + } + } else { + // data page + printf("CONTENT"); + } + } + printf("\n"); + u32_t len = fs->cfg.log_page_size; + hexdump(addr, len); +} + +void area_write(u32_t addr, u8_t *buf, u32_t size) { + int i; + for (i = 0; i < size; i++) { + area[addr + i] = *buf++; + } +} + +void area_read(u32_t addr, u8_t *buf, u32_t size) { + int i; + for (i = 0; i < size; i++) { + *buf++ = area[addr + i]; + } +} + +void dump_erase_counts(spiffs *fs) { + spiffs_block_ix bix; + printf(" BLOCK |\n"); + printf(" AGE COUNT|\n"); + for (bix = 0; bix < fs->block_count; bix++) { + printf("----%3i ----|", bix); + } + printf("\n"); + for (bix = 0; bix < fs->block_count; bix++) { + spiffs_obj_id erase_mark; + _spiffs_rd(fs, 0, 0, SPIFFS_ERASE_COUNT_PADDR(fs, bix), sizeof(spiffs_obj_id), (u8_t *)&erase_mark); + if (erases[bix] == 0) { + printf(" |"); + } else { + printf("%7i %4i|", (fs->max_erase_count - erase_mark), erases[bix]); + } + } + printf("\n"); +} + +void dump_flash_access_stats() { + printf(" RD: %10i reads %10i bytes %10i avg bytes/read\n", reads, bytes_rd, reads == 0 ? 0 : (bytes_rd / reads)); + printf(" WR: %10i writes %10i bytes %10i avg bytes/write\n", writes, bytes_wr, writes == 0 ? 0 : (bytes_wr / writes)); +} + + +static u32_t old_perc = 999; +static void spiffs_check_cb_f(spiffs_check_type type, spiffs_check_report report, + u32_t arg1, u32_t arg2) { +/* if (report == SPIFFS_CHECK_PROGRESS && old_perc != arg1) { + old_perc = arg1; + printf("CHECK REPORT: "); + switch(type) { + case SPIFFS_CHECK_LOOKUP: + printf("LU "); break; + case SPIFFS_CHECK_INDEX: + printf("IX "); break; + case SPIFFS_CHECK_PAGE: + printf("PA "); break; + } + printf("%i%%\n", arg1 * 100 / 256); + }*/ + if (report != SPIFFS_CHECK_PROGRESS) { + if (report != SPIFFS_CHECK_ERROR) fs_check_fixes++; + printf(" check: "); + switch (type) { + case SPIFFS_CHECK_INDEX: + printf("INDEX "); break; + case SPIFFS_CHECK_LOOKUP: + printf("LOOKUP "); break; + case SPIFFS_CHECK_PAGE: + printf("PAGE "); break; + default: + printf("???? "); break; + } + if (report == SPIFFS_CHECK_ERROR) { + printf("ERROR %i", arg1); + } else if (report == SPIFFS_CHECK_DELETE_BAD_FILE) { + printf("DELETE BAD FILE %04x", arg1); + } else if (report == SPIFFS_CHECK_DELETE_ORPHANED_INDEX) { + printf("DELETE ORPHANED INDEX %04x", arg1); + } else if (report == SPIFFS_CHECK_DELETE_PAGE) { + printf("DELETE PAGE %04x", arg1); + } else if (report == SPIFFS_CHECK_FIX_INDEX) { + printf("FIX INDEX %04x:%04x", arg1, arg2); + } else if (report == SPIFFS_CHECK_FIX_LOOKUP) { + printf("FIX INDEX %04x:%04x", arg1, arg2); + } else { + printf("??"); + } + printf("\n"); + } +} + +void fs_reset_specific(u32_t phys_addr, u32_t phys_size, + u32_t phys_sector_size, + u32_t log_block_size, u32_t log_page_size) { + memset(area, 0xcc, sizeof(area)); + memset(&area[phys_addr], 0xff, phys_size); + + spiffs_config c; + c.hal_erase_f = _erase; + c.hal_read_f = _read; + c.hal_write_f = _write; + c.log_block_size = log_block_size; + c.log_page_size = log_page_size; + c.phys_addr = phys_addr; + c.phys_erase_block = phys_sector_size; + c.phys_size = phys_size; + + memset(erases,0,sizeof(erases)); + memset(_cache,0,sizeof(_cache)); + + SPIFFS_mount(&__fs, &c, _work, _fds, sizeof(_fds), _cache, sizeof(_cache), spiffs_check_cb_f); + + clear_flash_ops_log(); + log_flash_ops = 1; + fs_check_fixes = 0; +} + +void fs_reset() { + fs_reset_specific(SPIFFS_PHYS_ADDR, SPIFFS_FLASH_SIZE, SECTOR_SIZE, LOG_BLOCK, LOG_PAGE); +} + +void set_flash_ops_log(int enable) { + log_flash_ops = enable; +} + +void clear_flash_ops_log() { + bytes_rd = 0; + bytes_wr = 0; + reads = 0; + writes = 0; + error_after_bytes_read = 0; + error_after_bytes_written = 0; +} + +u32_t get_flash_ops_log_read_bytes() { + return bytes_rd; +} + +u32_t get_flash_ops_log_write_bytes() { + return bytes_wr; +} + +void invoke_error_after_read_bytes(u32_t b, char once_only) { + error_after_bytes_read = b; + error_after_bytes_read_once_only = once_only; +} +void invoke_error_after_write_bytes(u32_t b, char once_only) { + error_after_bytes_written = b; + error_after_bytes_written_once_only = once_only; +} + +void fs_set_validate_flashing(int i) { + check_valid_flash = i; +} + +void real_assert(int c, const char *n, const char *file, int l) { + if (c == 0) { + printf("ASSERT: %s %s @ %i\n", (n ? n : ""), file, l); + printf("fs errno:%i\n", __fs.err_code); + exit(0); + } +} + +int read_and_verify(char *name) { + s32_t res; + int fd = SPIFFS_open(&__fs, name, SPIFFS_RDONLY, 0); + if (fd < 0) { + printf(" read_and_verify: could not open file %s\n", name); + return fd; + } + return read_and_verify_fd(fd, name); +} + +int read_and_verify_fd(spiffs_file fd, char *name) { + s32_t res; + int pfd = open(make_test_fname(name), O_RDONLY); + spiffs_stat s; + res = SPIFFS_fstat(&__fs, fd, &s); + if (res < 0) { + printf(" read_and_verify: could not stat file %s\n", name); + return res; + } + if (s.size == 0) { + SPIFFS_close(&__fs, fd); + close(pfd); + return 0; + } + + //printf("verifying %s, len %i\n", name, s.size); + int offs = 0; + u8_t buf_d[256]; + u8_t buf_v[256]; + while (offs < s.size) { + int read_len = MIN(s.size - offs, sizeof(buf_d)); + res = SPIFFS_read(&__fs, fd, buf_d, read_len); + if (res < 0) { + printf(" read_and_verify: could not read file %s offs:%i len:%i filelen:%i\n", name, offs, read_len, s.size); + return res; + } + int pres = read(pfd, buf_v, read_len); + (void)pres; + //printf("reading offs:%i len:%i spiffs_res:%i posix_res:%i\n", offs, read_len, res, pres); + int i; + int veri_ok = 1; + for (i = 0; veri_ok && i < read_len; i++) { + if (buf_d[i] != buf_v[i]) { + printf("file verification mismatch @ %i, %02x %c != %02x %c\n", offs+i, buf_d[i], buf_d[i], buf_v[i], buf_v[i]); + int j = MAX(0, i-16); + int k = MIN(sizeof(buf_d), i+16); + k = MIN(s.size-offs, k); + int l; + for (l = j; l < k; l++) { + printf("%c", buf_d[l] > 31 ? buf_d[l] : '.'); + } + printf("\n"); + for (l = j; l < k; l++) { + printf("%c", buf_v[l] > 31 ? buf_v[l] : '.'); + } + printf("\n"); + veri_ok = 0; + } + } + if (!veri_ok) { + SPIFFS_close(&__fs, fd); + close(pfd); + printf("data mismatch\n"); + return -1; + } + + offs += read_len; + } + + SPIFFS_close(&__fs, fd); + close(pfd); + + return 0; +} + +static void test_on_stop(test *t) { + printf(" spiffs errno:%i\n", SPIFFS_errno(&__fs)); +#if SPIFFS_TEST_VISUALISATION + SPIFFS_vis(FS); +#endif + +} + +void memrand(u8_t *b, int len) { + int i; + for (i = 0; i < len; i++) { + b[i] = rand(); + } +} + +int test_create_file(char *name) { + spiffs_stat s; + spiffs_file fd; + int res = SPIFFS_creat(FS, name, 0); + CHECK_RES(res); + fd = SPIFFS_open(FS, name, SPIFFS_RDONLY, 0); + CHECK(fd >= 0); + res = SPIFFS_fstat(FS, fd, &s); + CHECK_RES(res); + CHECK(strcmp((char*)s.name, name) == 0); + CHECK(s.size == 0); + SPIFFS_close(FS, fd); + return 0; +} + +int test_create_and_write_file(char *name, int size, int chunk_size) { + int res; + spiffs_file fd; + printf(" create and write %s", name); + res = test_create_file(name); + if (res < 0) { + printf(" failed creation, %i\n",res); + } + CHECK(res >= 0); + fd = SPIFFS_open(FS, name, SPIFFS_APPEND | SPIFFS_RDWR, 0); + if (res < 0) { + printf(" failed open, %i\n",res); + } + CHECK(fd >= 0); + int pfd = open(make_test_fname(name), O_APPEND | O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); + int offset = 0; + int mark = 0; + while (offset < size) { + int len = MIN(size-offset, chunk_size); + if (offset > mark) { + mark += size/16; + printf("."); + fflush(stdout); + } + u8_t *buf = malloc(len); + memrand(buf, len); + res = SPIFFS_write(FS, fd, buf, len); + write(pfd, buf, len); + free(buf); + if (res < 0) { + printf("\n error @ offset %i, res %i\n", offset, res); + } + offset += len; + CHECK(res >= 0); + } + printf("\n"); + close(pfd); + + spiffs_stat stat; + res = SPIFFS_fstat(FS, fd, &stat); + if (res < 0) { + printf(" failed fstat, %i\n",res); + } + CHECK(res >= 0); + if (stat.size != size) { + printf(" failed size, %i != %i\n", stat.size, size); + } + CHECK(stat.size == size); + + SPIFFS_close(FS, fd); + return 0; +} + +#if SPIFFS_CACHE +#if SPIFFS_CACHE_STATS +static u32_t chits_tot = 0; +static u32_t cmiss_tot = 0; +#endif +#endif + +void _setup_test_only() { + fs_set_validate_flashing(1); + test_init(test_on_stop); +} + +void _setup() { + fs_reset(); + _setup_test_only(); +} + +void _teardown() { + printf(" free blocks : %i of %i\n", (FS)->free_blocks, (FS)->block_count); + printf(" pages allocated : %i\n", (FS)->stats_p_allocated); + printf(" pages deleted : %i\n", (FS)->stats_p_deleted); +#if SPIFFS_GC_STATS + printf(" gc runs : %i\n", (FS)->stats_gc_runs); +#endif +#if SPIFFS_CACHE +#if SPIFFS_CACHE_STATS + chits_tot += (FS)->cache_hits; + cmiss_tot += (FS)->cache_misses; + printf(" cache hits : %i (sum %i)\n", (FS)->cache_hits, chits_tot); + printf(" cache misses : %i (sum %i)\n", (FS)->cache_misses, cmiss_tot); + printf(" cache utiliz : %f\n", ((float)chits_tot/(float)(chits_tot + cmiss_tot))); +#endif +#endif + dump_flash_access_stats(); + clear_flash_ops_log(); +#if SPIFFS_GC_STATS + if ((FS)->stats_gc_runs > 0) +#endif + dump_erase_counts(FS); + printf(" fs consistency check:\n"); + SPIFFS_check(FS); + clear_test_path(); + + //hexdump_mem(&area[SPIFFS_PHYS_ADDR - 16], 32); + //hexdump_mem(&area[SPIFFS_PHYS_ADDR + SPIFFS_FLASH_SIZE - 16], 32); +} + +u32_t tfile_get_size(tfile_size s) { + switch (s) { + case EMPTY: + return 0; + case SMALL: + return SPIFFS_DATA_PAGE_SIZE(FS)/2; + case MEDIUM: + return SPIFFS_DATA_PAGE_SIZE(FS) * (SPIFFS_PAGES_PER_BLOCK(FS) - SPIFFS_OBJ_LOOKUP_PAGES(FS)); + case LARGE: + return (FS)->cfg.phys_size/3; + } + return 0; +} + +int run_file_config(int cfg_count, tfile_conf* cfgs, int max_runs, int max_concurrent_files, int dbg) { + int res; + tfile *tfiles = malloc(sizeof(tfile) * max_concurrent_files); + memset(tfiles, 0, sizeof(tfile) * max_concurrent_files); + int run = 0; + int cur_config_ix = 0; + char name[32]; + while (run < max_runs) { + if (dbg) printf(" run %i/%i\n", run, max_runs); + int i; + for (i = 0; i < max_concurrent_files; i++) { + sprintf(name, "file%i_%i", (1+run), i); + tfile *tf = &tfiles[i]; + if (tf->state == 0 && cur_config_ix < cfg_count) { +// create a new file + strcpy(tf->name, name); + tf->state = 1; + tf->cfg = cfgs[cur_config_ix]; + int size = tfile_get_size(tf->cfg.tsize); + if (dbg) printf(" create new %s with cfg %i/%i, size %i\n", name, (1+cur_config_ix), cfg_count, size); + + if (tf->cfg.tsize == EMPTY) { + res = SPIFFS_creat(FS, name, 0); + CHECK_RES(res); + int pfd = open(make_test_fname(name), O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); + close(pfd); + int extra_flags = tf->cfg.ttype == APPENDED ? SPIFFS_APPEND : 0; + spiffs_file fd = SPIFFS_open(FS, name, extra_flags | SPIFFS_RDWR, 0); + CHECK(fd > 0); + tf->fd = fd; + } else { + int extra_flags = tf->cfg.ttype == APPENDED ? SPIFFS_APPEND : 0; + spiffs_file fd = SPIFFS_open(FS, name, extra_flags | SPIFFS_TRUNC | SPIFFS_CREAT | SPIFFS_RDWR, 0); + CHECK(fd > 0); + extra_flags = tf->cfg.ttype == APPENDED ? O_APPEND : 0; + int pfd = open(make_test_fname(name), extra_flags | O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); + tf->fd = fd; + u8_t *buf = malloc(size); + memrand(buf, size); + res = SPIFFS_write(FS, fd, buf, size); + CHECK_RES(res); + write(pfd, buf, size); + close(pfd); + free(buf); + res = read_and_verify(name); + CHECK_RES(res); + } + + cur_config_ix++; + } else if (tf->state > 0) { +// hande file lifecycle + switch (tf->cfg.ttype) { + case UNTAMPERED: { + break; + } + case APPENDED: { + if (dbg) printf(" appending %s\n", tf->name); + int size = SPIFFS_DATA_PAGE_SIZE(FS)*3; + u8_t *buf = malloc(size); + memrand(buf, size); + res = SPIFFS_write(FS, tf->fd, buf, size); + CHECK_RES(res); + int pfd = open(make_test_fname(tf->name), O_APPEND | O_RDWR); + write(pfd, buf, size); + close(pfd); + free(buf); + res = read_and_verify(tf->name); + CHECK_RES(res); + break; + } + case MODIFIED: { + if (dbg) printf(" modify %s\n", tf->name); + spiffs_stat stat; + res = SPIFFS_fstat(FS, tf->fd, &stat); + CHECK_RES(res); + int size = stat.size / tf->cfg.tlife + SPIFFS_DATA_PAGE_SIZE(FS)/3; + int offs = (stat.size / tf->cfg.tlife) * tf->state; + res = SPIFFS_lseek(FS, tf->fd, offs, SPIFFS_SEEK_SET); + CHECK_RES(res); + u8_t *buf = malloc(size); + memrand(buf, size); + res = SPIFFS_write(FS, tf->fd, buf, size); + CHECK_RES(res); + int pfd = open(make_test_fname(tf->name), O_RDWR); + lseek(pfd, offs, SEEK_SET); + write(pfd, buf, size); + close(pfd); + free(buf); + res = read_and_verify(tf->name); + CHECK_RES(res); + break; + } + case REWRITTEN: { + if (tf->fd > 0) { + SPIFFS_close(FS, tf->fd); + } + if (dbg) printf(" rewriting %s\n", tf->name); + spiffs_file fd = SPIFFS_open(FS, tf->name, SPIFFS_TRUNC | SPIFFS_CREAT | SPIFFS_RDWR, 0); + CHECK(fd > 0); + int pfd = open(make_test_fname(tf->name), O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); + tf->fd = fd; + int size = tfile_get_size(tf->cfg.tsize); + u8_t *buf = malloc(size); + memrand(buf, size); + res = SPIFFS_write(FS, fd, buf, size); + CHECK_RES(res); + write(pfd, buf, size); + close(pfd); + free(buf); + res = read_and_verify(tf->name); + CHECK_RES(res); + break; + } + } + tf->state++; + if (tf->state > tf->cfg.tlife) { +// file outlived its time, kill it + if (tf->fd > 0) { + SPIFFS_close(FS, tf->fd); + } + if (dbg) printf(" removing %s\n", tf->name); + res = read_and_verify(tf->name); + CHECK_RES(res); + res = SPIFFS_remove(FS, tf->name); + CHECK_RES(res); + remove(make_test_fname(tf->name)); + memset(tf, 0, sizeof(tf)); + } + + } + } + + run++; + } + free(tfiles); + return 0; +} + + + diff --git a/app/spiffs/test/test_spiffs.h b/app/spiffs/test/test_spiffs.h new file mode 100644 index 00000000..14c603f0 --- /dev/null +++ b/app/spiffs/test/test_spiffs.h @@ -0,0 +1,90 @@ +/* + * test_spiffs.h + * + * Created on: Jun 19, 2013 + * Author: petera + */ + +#ifndef TEST_SPIFFS_H_ +#define TEST_SPIFFS_H_ + +#include "spiffs.h" + +#define FS &__fs + +extern spiffs __fs; + + +#define CHECK(r) if (!(r)) return -1; +#define CHECK_RES(r) if (r < 0) return -1; +#define FS_PURE_DATA_PAGES(fs) \ + ((fs)->cfg.phys_size / (fs)->cfg.log_page_size - (fs)->block_count * SPIFFS_OBJ_LOOKUP_PAGES(fs)) +#define FS_PURE_DATA_SIZE(fs) \ + FS_PURE_DATA_PAGES(fs) * SPIFFS_DATA_PAGE_SIZE(fs) + +typedef enum { + EMPTY, + SMALL, + MEDIUM, + LARGE, +} tfile_size; + +typedef enum { + UNTAMPERED, + APPENDED, + MODIFIED, + REWRITTEN, +} tfile_type; + +typedef enum { + SHORT = 4, + NORMAL = 20, + LONG = 100, +} tfile_life; + +typedef struct { + tfile_size tsize; + tfile_type ttype; + tfile_life tlife; +} tfile_conf; + +typedef struct { + int state; + spiffs_file fd; + tfile_conf cfg; + char name[32]; +} tfile; + + +void fs_reset(); +void fs_reset_specific(u32_t phys_addr, u32_t phys_size, + u32_t phys_sector_size, + u32_t log_block_size, u32_t log_page_size); +int read_and_verify(char *name); +int read_and_verify_fd(spiffs_file fd, char *name); +void dump_page(spiffs *fs, spiffs_page_ix p); +void hexdump(u32_t addr, u32_t len); +char *make_test_fname(const char *name); +void clear_test_path(); +void area_write(u32_t addr, u8_t *buf, u32_t size); +void area_read(u32_t addr, u8_t *buf, u32_t size); +void dump_erase_counts(spiffs *fs); +void dump_flash_access_stats(); +void set_flash_ops_log(int enable); +void clear_flash_ops_log(); +u32_t get_flash_ops_log_read_bytes(); +u32_t get_flash_ops_log_write_bytes(); +void invoke_error_after_read_bytes(u32_t b, char once_only); +void invoke_error_after_write_bytes(u32_t b, char once_only); + +void memrand(u8_t *b, int len); +int test_create_file(char *name); +int test_create_and_write_file(char *name, int size, int chunk_size); +void _setup(); +void _setup_test_only(); +void _teardown(); +u32_t tfile_get_size(tfile_size s); +int run_file_config(int cfg_count, tfile_conf* cfgs, int max_runs, int max_concurrent_files, int dbg); + + +#endif /* TEST_SPIFFS_H_ */ diff --git a/app/spiffs/test/testrunner.c b/app/spiffs/test/testrunner.c new file mode 100644 index 00000000..ad4fd1fb --- /dev/null +++ b/app/spiffs/test/testrunner.c @@ -0,0 +1,175 @@ +/* + * testrunner.c + * + * Created on: Jun 18, 2013 + * Author: petera + */ + + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "testrunner.h" + +static struct { + test *tests; + test *_last_test; + int test_count; + void (*on_stop)(test *t); + test_res *failed; + test_res *failed_last; + test_res *stopped; + test_res *stopped_last; + FILE *spec; +} test_main; + +void test_init(void (*on_stop)(test *t)) { + test_main.on_stop = on_stop; +} + +static char check_spec(char *name) { + if (test_main.spec) { + fseek(test_main.spec, 0, SEEK_SET); + char *line = NULL; + size_t sz; + ssize_t read; + while ((read = getline(&line, &sz, test_main.spec)) != -1) { + if (strncmp(line, name, strlen(name)) == 0) { + free(line); + return 1; + } + } + free(line); + return 0; + } else { + return 1; + } +} + +void add_test(test_f f, char *name, void (*setup)(test *t), void (*teardown)(test *t)) { + if (f == 0) return; + if (!check_spec(name)) return; + DBGT("adding test %s\n", name); + test *t = malloc(sizeof(test)); + memset(t, 0, sizeof(test)); + t->f = f; + strcpy(t->name, name); + t->setup = setup; + t->teardown = teardown; + if (test_main.tests == 0) { + test_main.tests = t; + } else { + test_main._last_test->_next = t; + } + test_main._last_test = t; + test_main.test_count++; +} + +static void add_res(test *t, test_res **head, test_res **last) { + test_res *tr = malloc(sizeof(test_res)); + memset(tr,0,sizeof(test_res)); + strcpy(tr->name, t->name); + if (*head == 0) { + *head = tr; + } else { + (*last)->_next = tr; + } + *last = tr; +} + +static void dump_res(test_res **head) { + test_res *tr = (*head); + while (tr) { + test_res *next_tr = tr->_next; + printf(" %s\n", tr->name); + free(tr); + tr = next_tr; + } +} + +void run_tests(int argc, char **args) { + memset(&test_main, 0, sizeof(test_main)); + if (argc > 1) { + printf("running tests from %s\n", args[1]); + FILE *fd = fopen(args[1], "r"); + if (fd == NULL) { + printf("%s not found\n", args[1]); + exit(EXIT_FAILURE); + } + test_main.spec = fd; + } + + DBGT("adding suites...\n"); + add_suites(); + DBGT("%i tests added\n", test_main.test_count); + if (test_main.spec) { + fclose(test_main.spec); + } + + if (test_main.test_count == 0) { + printf("No tests to run\n"); + return; + } + + int fd_success = open("_tests_ok", O_APPEND | O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); + int fd_bad = open("_tests_fail", O_APPEND | O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); + + DBGT("running tests...\n"); + int ok = 0; + int failed = 0; + int stopped = 0; + test *cur_t = test_main.tests; + int i = 1; + while (cur_t) { + cur_t->setup(cur_t); + test *next_test = cur_t->_next; + DBGT("TEST %i/%i : running test %s\n", i, test_main.test_count, cur_t->name); + i++; + int res = cur_t->f(cur_t); + cur_t->teardown(cur_t); + int fd = res == TEST_RES_OK ? fd_success : fd_bad; + write(fd, cur_t->name, strlen(cur_t->name)); + write(fd, "\n", 1); + switch (res) { + case TEST_RES_OK: + ok++; + printf(" .. ok\n"); + break; + case TEST_RES_FAIL: + failed++; + printf(" .. FAILED\n"); + if (test_main.on_stop) test_main.on_stop(cur_t); + add_res(cur_t, &test_main.failed, &test_main.failed_last); + break; + case TEST_RES_ASSERT: + stopped++; + printf(" .. ABORTED\n"); + if (test_main.on_stop) test_main.on_stop(cur_t); + add_res(cur_t, &test_main.stopped, &test_main.stopped_last); + break; + } + free(cur_t); + cur_t = next_test; + } + close(fd_success); + close(fd_bad); + DBGT("ran %i tests\n", test_main.test_count); + printf("Test report, %i tests\n", test_main.test_count); + printf("%i succeeded\n", ok); + printf("%i failed\n", failed); + dump_res(&test_main.failed); + printf("%i stopped\n", stopped); + dump_res(&test_main.stopped); + if (ok < test_main.test_count) { + printf("\nFAILED\n"); + } else { + printf("\nALL TESTS OK\n"); + } +} diff --git a/app/spiffs/test/testrunner.h b/app/spiffs/test/testrunner.h new file mode 100644 index 00000000..20ae606c --- /dev/null +++ b/app/spiffs/test/testrunner.h @@ -0,0 +1,110 @@ +/* + * testrunner.h + * + * Created on: Jun 19, 2013 + * Author: petera + */ + +/* + +SUITE(mysuite) + +void setup(test *t) {} + +void teardown(test *t) {} + +TEST(mytest) { + printf("mytest runs now..\n"); + return 0; +} TEST_END(mytest) + +SUITE_END(mysuite) + + + +SUITE(mysuite2) + +void setup(test *t) {} + +void teardown(test *t) {} + +TEST(mytest2a) { + printf("mytest2a runs now..\n"); + return 0; +} TEST_END(mytest2a) + +TEST(mytest2b) { + printf("mytest2b runs now..\n"); + return 0; +} TEST_END(mytest2b) + +SUITE_END(mysuite2) + + + +void add_suites() { + ADD_SUITE(mysuite); + ADD_SUITE(mysuite2); +} + */ + +#ifndef TESTS_H_ +#define TESTS_H_ + +#define TEST_RES_OK 0 +#define TEST_RES_FAIL -1 +#define TEST_RES_ASSERT -2 + +struct test_s; + +typedef int (*test_f)(struct test_s *t); + +typedef struct test_s { + test_f f; + char name[256]; + void *data; + void (*setup)(struct test_s *t); + void (*teardown)(struct test_s *t); + struct test_s *_next; +} test; + +typedef struct test_res_s { + char name[256]; + struct test_res_s *_next; +} test_res; + +#define TEST_CHECK(x) if (!(x)) { \ + printf(" TEST FAIL %s:%i\n", __FILE__, __LINE__); \ + goto __fail_stop; \ +} +#define TEST_ASSERT(x) if (!(x)) { \ + printf(" TEST ASSERT %s:%i\n", __FILE__, __LINE__); \ + goto __fail_assert; \ +} + +#define DBGT(...) printf(__VA_ARGS__) + +#define str(s) #s + +#define SUITE(sui) \ + extern void __suite_##sui() { +#define SUITE_END(sui) \ + } +#define ADD_SUITE(sui) \ + __suite_##sui(); +#define TEST(tf) \ + int tf(struct test_s *t) { do +#define TEST_END(tf) \ + while(0); \ + __fail_stop: return TEST_RES_FAIL; \ + __fail_assert: return TEST_RES_ASSERT; \ + } \ + add_test(tf, str(tf), setup, teardown); + + +void add_suites(); +void test_init(void (*on_stop)(test *t)); +void add_test(test_f f, char *name, void (*setup)(test *t), void (*teardown)(test *t)); +void run_tests(int argc, char **args); + +#endif /* TESTS_H_ */ diff --git a/app/spiffs/test/testsuites.c b/app/spiffs/test/testsuites.c new file mode 100644 index 00000000..7627db06 --- /dev/null +++ b/app/spiffs/test/testsuites.c @@ -0,0 +1,15 @@ +/* + * testsuites.c + * + * Created on: Jun 19, 2013 + * Author: petera + */ + +#include "testrunner.h" + +void add_suites() { + //ADD_SUITE(dev_tests); + ADD_SUITE(check_tests); + ADD_SUITE(hydrogen_tests) + ADD_SUITE(bug_tests) +} diff --git a/examples/fragment.lua b/examples/fragment.lua index c3b980dc..a56e423a 100644 --- a/examples/fragment.lua +++ b/examples/fragment.lua @@ -355,11 +355,29 @@ cs:listen(5683) myvar=1 cs:var("myvar") -- get coap://192.168.18.103:5683/v1/v/myvar will return the value of myvar: 1 -function myfun() +-- function should tack one string, return one string. +function myfun(payload) print("myfun called") + respond = "hello" + return respond end cs:func("myfun") -- post coap://192.168.18.103:5683/v1/f/myfun will call myfun cc = coap.Client() cc:get(coap.CON, "coap://192.168.18.100:5683/.well-known/core") cc:post(coap.NON, "coap://192.168.18.100:5683/", "Hello") + + +file.open("test1.txt", "a+") for i = 1, 100*1000 do file.write("x") end file.close() print("Done.") +for n,s in pairs(file.list()) do print(n.." size: "..s) end +file.remove("test1.txt") +for n,s in pairs(file.list()) do print(n.." size: "..s) end +file.open("test2.txt", "a+") for i = 1, 1*1000 do file.write("x") end file.close() print("Done.") + + +function TestDNSLeak() + c=net.createConnection(net.TCP, 0) + c:connect(80, "bad-name.tlddfdf") + tmr.alarm(1, 3000, 0, function() print("hack socket close, MEM: "..node.heap()) c:close() end) -- socket timeout hack + print("MEM: "..node.heap()) +end diff --git a/lua_modules/ds18b20/ds18b20.lua b/lua_modules/ds18b20/ds18b20.lua index c31addcf..138c10da 100644 --- a/lua_modules/ds18b20/ds18b20.lua +++ b/lua_modules/ds18b20/ds18b20.lua @@ -3,6 +3,7 @@ -- NODEMCU TEAM -- LICENCE: http://opensource.org/licenses/MIT -- Vowstar +-- 2015/02/14 sza2 Fix for negative values -------------------------------------------------------------------------------- -- Set module name as parameter of require @@ -96,12 +97,16 @@ function readNumber(addr, unit) crc = ow.crc8(string.sub(data,1,8)) -- print("CRC="..crc) if (crc == data:byte(9)) then + t = (data:byte(1) + data:byte(2) * 256) + if (t > 32767) then + t = t - 65536 + end if(unit == nil or unit == C) then - t = (data:byte(1) + data:byte(2) * 256) * 625 + t = t * 625 elseif(unit == F) then - t = (data:byte(1) + data:byte(2) * 256) * 1125 + 320000 + t = t * 1125 + 320000 elseif(unit == K) then - t = (data:byte(1) + data:byte(2) * 256) * 625 + 2731500 + t = t * 625 + 2731500 else return nil end diff --git a/lua_modules/hdc1000/HDC1000-example.lua b/lua_modules/hdc1000/HDC1000-example.lua new file mode 100644 index 00000000..fe301dcb --- /dev/null +++ b/lua_modules/hdc1000/HDC1000-example.lua @@ -0,0 +1,13 @@ +HDC1000 = require("HDC1000") + +sda = 1 +scl = 2 +drdyn = false + +HDC1000.init(sda, scl, drdyn) +HDC1000.config() -- default values are used if called with no arguments. prototype is config(address, resolution, heater) + +print(string.format("Temperature: %.2f °C\nHumidity: %.2f %%", HDC1000.getTemp(), HDC1000.getHumi())) + +HDC1000 = nil +package.loaded["HDC1000"]=nil \ No newline at end of file diff --git a/lua_modules/hdc1000/HDC1000.lua b/lua_modules/hdc1000/HDC1000.lua new file mode 100644 index 00000000..51e9a44c --- /dev/null +++ b/lua_modules/hdc1000/HDC1000.lua @@ -0,0 +1,109 @@ +------------------------------------------------------- + +-- This library was written for the Texas Instruments +-- HDC1000 temperature and humidity sensor. +-- It should work for the HDC1008 too. +-- Written by Francesco Truzzi (francesco@truzzi.me) +-- Released under GNU GPL v2.0 license. + +------------------------------------------------------- + +-------------- NON-DEFAULT CONFIG VALUES -------------- +------------- config() optional arguments ------------- + +-- HDC1000_HEAT_OFF 0x00 (heater) +-- HDC1000_TEMP_11BIT 0x40 (resolution) +-- HDC1000_HUMI_11BIT 0x01 (resolution) +-- HDC1000_HUMI_8BIT 0x20 (resolution) + +------------------------------------------------------- + +local modname = ... +local M = {} +_G[modname] = M + +local id = 0 +local i2c = i2c +local delay = 20000 +local _drdyn_pin + +local HDC1000_ADDR = 0x40 + +local HDC1000_TEMP = 0x00 +local HDC1000_HUMI = 0x01 +local HDC1000_CONFIG = 0x02 + +local HDC1000_HEAT_ON = 0x20 +local HDC1000_TEMP_HUMI_14BIT = 0x00 + +-- reads 16bits from the sensor +local function read16() + i2c.start(id) + i2c.address(id, HDC1000_ADDR, i2c.RECEIVER) + data_temp = i2c.read(0, 2) + i2c.stop(id) + data = bit.lshift(string.byte(data_temp, 1, 1), 8) + string.byte(data_temp, 2, 2) + return data +end + +-- sets the register to read next +local function setReadRegister(register) + i2c.start(id) + i2c.address(id, HDC1000_ADDR, i2c.TRANSMITTER) + i2c.write(id, register) + i2c.stop(id) +end + +-- writes the 2 configuration bytes +local function writeConfig(config) + i2c.start(id) + i2c.address(id, HDC1000_ADDR, i2c.TRANSMITTER) + i2c.write(id, HDC1000_CONFIG, config, 0x00) + i2c.stop(id) +end + +-- returns true if battery voltage is < 2.7V, false otherwise +function M.batteryDead() + setReadRegister(HDC1000_CONFIG) + return(bit.isset(read16(), 11)) + +end + +-- initalize i2c +function M.init(sda, scl, drdyn_pin) + _drdyn_pin = drdyn_pin + i2c.setup(id, sda, scl, i2c.SLOW) +end + +function M.config(addr, resolution, heater) + -- default values are set if the function is called with no arguments + HDC1000_ADDR = addr or HDC1000_ADDR + resolution = resolution or HDC1000_TEMP_HUMI_14BIT + heater = heater or HDC1000_HEAT_ON + writeConfig(bit.bor(resolution, heater)) +end + +-- outputs temperature in Celsius degrees +function M.getHumi() + setReadRegister(HDC1000_HUMI) + if(_drdyn_pin ~= false) then + gpio.mode(_drdyn_pin, gpio.INPUT) + while(gpio.read(_drdyn_pin)==1) do + end + else tmr.delay(delay) end + return(read16()/65535.0*100) +end + +-- outputs humidity in %RH +function M.getTemp() + setReadRegister(HDC1000_TEMP) + if(_drdyn_pin ~= false) then + gpio.mode(_drdyn_pin, gpio.INPUT) + while(gpio.read(_drdyn_pin)==1) do + end + else tmr.delay(delay) end + return(read16()/65535.0*165-40) +end + +return M + diff --git a/lua_modules/hdc1000/README.md b/lua_modules/hdc1000/README.md new file mode 100644 index 00000000..aa04897d --- /dev/null +++ b/lua_modules/hdc1000/README.md @@ -0,0 +1,43 @@ +HDC1000 NodeMCU module +======================= + +Here's my NodeMCU module for the TI HDC1000 temperature and humidity sensor. It should work with the HDC1008 too but I haven't tested it. + +### Setup your sensor: +First, require it: + +`HDC1000 = require("HDC1000")` + +Then, initialize it: + +`HDC1000.init(sda, scl, drdyn)` + +If you don't want to use the DRDYn pin, set it to false: a 20ms delay will be automatically set after each read request. + +`HDC1000.init(sda, scl, false)` + +Configure it: + +`HDC1000.config()` + +Default options set the address to 0x40 and enable both temperature and humidity readings at 14-bit resolution, with the integrated heater on. You can change them by initializing your sensor like this: + +`HDC1000.config(address, resolution, heater);` + +"resolution" can be set to 14 bits for both temperature and humidity (0x00 - default) 11 bits for temperature (0x40), 11 bits for humidity (0x01), 8 bits for humidity (0x20) +"heater" can be set to ON (0x20 - default) or OFF (0x00) + +### Read some values +You can read temperature and humidity by using the following commands: + +`temperature = HDC1000.getTemp()` in Celsius degrees. + +`humidity = HDC1000.getHumi()` in % + +### Check your battery + +The following code returns true if the battery voltage is <2.8V, false otherwise. + +`isDead = HDC1000.batteryDead();` + +Happy making! Also, check out my Breakout Board and Arduino library for this chip: http://b.truzzi.me/hdc1000-temperature-and-humidity-sensor-breakout-with-arduino-library/. diff --git a/pre_build/README.md b/pre_build/README.md new file mode 100644 index 00000000..630fa53e --- /dev/null +++ b/pre_build/README.md @@ -0,0 +1 @@ +[Downloads](https://github.com/nodemcu/nodemcu-firmware/releases/latest) \ No newline at end of file