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