+
+
+/* Mac appends a `_' before C function names */
+#undef POF
+#define POF "_" LUA_POF
+
+
+static void pusherror (lua_State *L) {
+ const char *err_str;
+ const char *err_file;
+ NSLinkEditErrors err;
+ int err_num;
+ NSLinkEditError(&err, &err_num, &err_file, &err_str);
+ lua_pushstring(L, err_str);
+}
+
+
+static const char *errorfromcode (NSObjectFileImageReturnCode ret) {
+ switch (ret) {
+ case NSObjectFileImageInappropriateFile:
+ return "file is not a bundle";
+ case NSObjectFileImageArch:
+ return "library is for wrong CPU type";
+ case NSObjectFileImageFormat:
+ return "bad format";
+ case NSObjectFileImageAccess:
+ return "cannot access file";
+ case NSObjectFileImageFailure:
+ default:
+ return "unable to load library";
+ }
+}
+
+
+static void ll_unloadlib (void *lib) {
+ NSUnLinkModule((NSModule)lib, NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES);
+}
+
+
+static void *ll_load (lua_State *L, const char *path) {
+ NSObjectFileImage img;
+ NSObjectFileImageReturnCode ret;
+ /* this would be a rare case, but prevents crashing if it happens */
+ if(!_dyld_present()) {
+ lua_pushliteral(L, "dyld not present");
+ return NULL;
+ }
+ ret = NSCreateObjectFileImageFromFile(path, &img);
+ if (ret == NSObjectFileImageSuccess) {
+ NSModule mod = NSLinkModule(img, path, NSLINKMODULE_OPTION_PRIVATE |
+ NSLINKMODULE_OPTION_RETURN_ON_ERROR);
+ NSDestroyObjectFileImage(img);
+ if (mod == NULL) pusherror(L);
+ return mod;
+ }
+ lua_pushstring(L, errorfromcode(ret));
+ return NULL;
+}
+
+
+static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) {
+ NSSymbol nss = NSLookupSymbolInModule((NSModule)lib, sym);
+ if (nss == NULL) {
+ lua_pushfstring(L, "symbol " LUA_QS " not found", sym);
+ return NULL;
+ }
+ return (lua_CFunction)NSAddressOfSymbol(nss);
+}
+
+/* }====================================================== */
+
+
+
+#else
+/*
+** {======================================================
+** Fallback for other systems
+** =======================================================
+*/
+
+#undef LIB_FAIL
+#define LIB_FAIL "absent"
+
+
+#define DLMSG "dynamic libraries not enabled; check your Lua installation"
+
+
+static void ICACHE_FLASH_ATTR ll_unloadlib (void *lib) {
+ (void)lib; /* to avoid warnings */
+}
+
+
+static void * ICACHE_FLASH_ATTR ll_load (lua_State *L, const char *path) {
+ (void)path; /* to avoid warnings */
+ lua_pushliteral(L, DLMSG);
+ return NULL;
+}
+
+
+static lua_CFunction ICACHE_FLASH_ATTR ll_sym (lua_State *L, void *lib, const char *sym) {
+ (void)lib; (void)sym; /* to avoid warnings */
+ lua_pushliteral(L, DLMSG);
+ return NULL;
+}
+
+/* }====================================================== */
+#endif
+
+
+
+static void ** ICACHE_FLASH_ATTR ll_register (lua_State *L, const char *path) {
+ void **plib;
+ lua_pushfstring(L, "%s%s", LIBPREFIX, path);
+ lua_gettable(L, LUA_REGISTRYINDEX); /* check library in registry? */
+ if (!lua_isnil(L, -1)) /* is there an entry? */
+ plib = (void **)lua_touserdata(L, -1);
+ else { /* no entry yet; create one */
+ lua_pop(L, 1);
+ plib = (void **)lua_newuserdata(L, sizeof(const void *));
+ *plib = NULL;
+ luaL_getmetatable(L, "_LOADLIB");
+ lua_setmetatable(L, -2);
+ lua_pushfstring(L, "%s%s", LIBPREFIX, path);
+ lua_pushvalue(L, -2);
+ lua_settable(L, LUA_REGISTRYINDEX);
+ }
+ return plib;
+}
+
+
+/*
+** __gc tag method: calls library's `ll_unloadlib' function with the lib
+** handle
+*/
+static int ICACHE_FLASH_ATTR gctm (lua_State *L) {
+ void **lib = (void **)luaL_checkudata(L, 1, "_LOADLIB");
+ if (*lib) ll_unloadlib(*lib);
+ *lib = NULL; /* mark library as closed */
+ return 0;
+}
+
+
+static int ICACHE_FLASH_ATTR ll_loadfunc (lua_State *L, const char *path, const char *sym) {
+ void **reg = ll_register(L, path);
+ if (*reg == NULL) *reg = ll_load(L, path);
+ if (*reg == NULL)
+ return ERRLIB; /* unable to load library */
+ else {
+ lua_CFunction f = ll_sym(L, *reg, sym);
+ if (f == NULL)
+ return ERRFUNC; /* unable to find function */
+ lua_pushcfunction(L, f);
+ return 0; /* return function */
+ }
+}
+
+
+static int ICACHE_FLASH_ATTR ll_loadlib (lua_State *L) {
+ const char *path = luaL_checkstring(L, 1);
+ const char *init = luaL_checkstring(L, 2);
+ int stat = ll_loadfunc(L, path, init);
+ if (stat == 0) /* no errors? */
+ return 1; /* return the loaded function */
+ else { /* error; error message is on stack top */
+ lua_pushnil(L);
+ lua_insert(L, -2);
+ lua_pushstring(L, (stat == ERRLIB) ? LIB_FAIL : "init");
+ return 3; /* return nil, error message, and where */
+ }
+}
+
+
+
+/*
+** {======================================================
+** 'require' function
+** =======================================================
+*/
+#if 0
+static int readable (const char *filename) {
+ FILE *f = c_fopen(filename, "r"); /* try to open file */
+ if (f == NULL) return 0; /* open failed */
+ c_fclose(f);
+ return 1;
+}
+#else
+static int ICACHE_FLASH_ATTR readable (const char *filename) {
+ int f = fs_open(filename, FS_RDONLY); /* try to open file */
+ if (f < FS_OPEN_OK) return 0; /* open failed */
+ fs_close(f);
+ return 1;
+}
+#endif
+
+static const char * ICACHE_FLASH_ATTR pushnexttemplate (lua_State *L, const char *path) {
+ const char *l;
+ while (*path == *LUA_PATHSEP) path++; /* skip separators */
+ if (*path == '\0') return NULL; /* no more templates */
+ l = c_strchr(path, *LUA_PATHSEP); /* find next separator */
+ if (l == NULL) l = path + c_strlen(path);
+ lua_pushlstring(L, path, l - path); /* template */
+ return l;
+}
+
+
+static const char * ICACHE_FLASH_ATTR findfile (lua_State *L, const char *name,
+ const char *pname) {
+ const char *path;
+ name = luaL_gsub(L, name, ".", LUA_DIRSEP);
+ lua_getfield(L, LUA_ENVIRONINDEX, pname);
+ path = lua_tostring(L, -1);
+ if (path == NULL)
+ luaL_error(L, LUA_QL("package.%s") " must be a string", pname);
+ lua_pushliteral(L, ""); /* error accumulator */
+ while ((path = pushnexttemplate(L, path)) != NULL) {
+ const char *filename;
+ filename = luaL_gsub(L, lua_tostring(L, -1), LUA_PATH_MARK, name);
+ lua_remove(L, -2); /* remove path template */
+ if (readable(filename)) /* does file exist and is readable? */
+ return filename; /* return that file name */
+ lua_pushfstring(L, "\n\tno file " LUA_QS, filename);
+ lua_remove(L, -2); /* remove file name */
+ lua_concat(L, 2); /* add entry to possible error message */
+ }
+ return NULL; /* not found */
+}
+
+
+static void ICACHE_FLASH_ATTR loaderror (lua_State *L, const char *filename) {
+ luaL_error(L, "error loading module " LUA_QS " from file " LUA_QS ":\n\t%s",
+ lua_tostring(L, 1), filename, lua_tostring(L, -1));
+}
+
+
+static int ICACHE_FLASH_ATTR loader_Lua (lua_State *L) {
+ const char *filename;
+ const char *name = luaL_checkstring(L, 1);
+ filename = findfile(L, name, "path");
+ if (filename == NULL) return 1; /* library not found in this path */
+#if 0
+ if (luaL_loadfile(L, filename) != 0)
+#else
+ if (luaL_loadfsfile(L, filename) != 0)
+#endif
+ loaderror(L, filename);
+ return 1; /* library loaded successfully */
+}
+
+
+static const char *mkfuncname (lua_State *L, const char *modname) {
+ const char *funcname;
+ const char *mark = c_strchr(modname, *LUA_IGMARK);
+ if (mark) modname = mark + 1;
+ funcname = luaL_gsub(L, modname, ".", LUA_OFSEP);
+ funcname = lua_pushfstring(L, POF"%s", funcname);
+ lua_remove(L, -2); /* remove 'gsub' result */
+ return funcname;
+}
+
+
+static int ICACHE_FLASH_ATTR loader_C (lua_State *L) {
+ const char *funcname;
+ const char *name = luaL_checkstring(L, 1);
+ const char *filename = findfile(L, name, "cpath");
+ if (filename == NULL) return 1; /* library not found in this path */
+ funcname = mkfuncname(L, name);
+ if (ll_loadfunc(L, filename, funcname) != 0)
+ loaderror(L, filename);
+ return 1; /* library loaded successfully */
+}
+
+
+static int ICACHE_FLASH_ATTR loader_Croot (lua_State *L) {
+ const char *funcname;
+ const char *filename;
+ const char *name = luaL_checkstring(L, 1);
+ const char *p = c_strchr(name, '.');
+ int stat;
+ if (p == NULL) return 0; /* is root */
+ lua_pushlstring(L, name, p - name);
+ filename = findfile(L, lua_tostring(L, -1), "cpath");
+ if (filename == NULL) return 1; /* root not found */
+ funcname = mkfuncname(L, name);
+ if ((stat = ll_loadfunc(L, filename, funcname)) != 0) {
+ if (stat != ERRFUNC) loaderror(L, filename); /* real error */
+ lua_pushfstring(L, "\n\tno module " LUA_QS " in file " LUA_QS,
+ name, filename);
+ return 1; /* function not found */
+ }
+ return 1;
+}
+
+
+static int ICACHE_FLASH_ATTR loader_preload (lua_State *L) {
+ const char *name = luaL_checkstring(L, 1);
+ lua_getfield(L, LUA_ENVIRONINDEX, "preload");
+ if (!lua_istable(L, -1))
+ luaL_error(L, LUA_QL("package.preload") " must be a table");
+ lua_getfield(L, -1, name);
+ if (lua_isnil(L, -1)) /* not found? */
+ lua_pushfstring(L, "\n\tno field package.preload['%s']", name);
+ return 1;
+}
+
+
+static const int sentinel_ = 0;
+#define sentinel ((void *)&sentinel_)
+
+
+static int ICACHE_FLASH_ATTR ll_require (lua_State *L) {
+ const char *name = luaL_checkstring(L, 1);
+ int i;
+ lua_settop(L, 1); /* _LOADED table will be at index 2 */
+ lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
+ lua_getfield(L, 2, name);
+ if (lua_toboolean(L, -1)) { /* is it there? */
+ if (lua_touserdata(L, -1) == sentinel) /* check loops */
+ luaL_error(L, "loop or previous error loading module " LUA_QS, name);
+ return 1; /* package is already loaded */
+ }
+ /* Is this a readonly table? */
+ void *res = luaR_findglobal(name, c_strlen(name));
+ if (res) {
+ lua_pushrotable(L, res);
+ return 1;
+ }
+ /* else must load it; iterate over available loaders */
+ lua_getfield(L, LUA_ENVIRONINDEX, "loaders");
+ if (!lua_istable(L, -1))
+ luaL_error(L, LUA_QL("package.loaders") " must be a table");
+ lua_pushliteral(L, ""); /* error message accumulator */
+ for (i=1; ; i++) {
+ lua_rawgeti(L, -2, i); /* get a loader */
+ if (lua_isnil(L, -1))
+ luaL_error(L, "module " LUA_QS " not found:%s",
+ name, lua_tostring(L, -2));
+ lua_pushstring(L, name);
+ lua_call(L, 1, 1); /* call it */
+ if (lua_isfunction(L, -1)) /* did it find module? */
+ break; /* module loaded successfully */
+ else if (lua_isstring(L, -1)) /* loader returned error message? */
+ lua_concat(L, 2); /* accumulate it */
+ else
+ lua_pop(L, 1);
+ }
+ lua_pushlightuserdata(L, sentinel);
+ lua_setfield(L, 2, name); /* _LOADED[name] = sentinel */
+ lua_pushstring(L, name); /* pass name as argument to module */
+ lua_call(L, 1, 1); /* run loaded module */
+ if (!lua_isnil(L, -1)) /* non-nil return? */
+ lua_setfield(L, 2, name); /* _LOADED[name] = returned value */
+ lua_getfield(L, 2, name);
+ if (lua_touserdata(L, -1) == sentinel) { /* module did not set a value? */
+ lua_pushboolean(L, 1); /* use true as result */
+ lua_pushvalue(L, -1); /* extra copy to be returned */
+ lua_setfield(L, 2, name); /* _LOADED[name] = true */
+ }
+ return 1;
+}
+
+/* }====================================================== */
+
+
+
+/*
+** {======================================================
+** 'module' function
+** =======================================================
+*/
+
+
+static void ICACHE_FLASH_ATTR setfenv (lua_State *L) {
+ lua_Debug ar;
+ if (lua_getstack(L, 1, &ar) == 0 ||
+ lua_getinfo(L, "f", &ar) == 0 || /* get calling function */
+ lua_iscfunction(L, -1))
+ luaL_error(L, LUA_QL("module") " not called from a Lua function");
+ lua_pushvalue(L, -2);
+ lua_setfenv(L, -2);
+ lua_pop(L, 1);
+}
+
+
+static void ICACHE_FLASH_ATTR dooptions (lua_State *L, int n) {
+ int i;
+ for (i = 2; i <= n; i++) {
+ lua_pushvalue(L, i); /* get option (a function) */
+ lua_pushvalue(L, -2); /* module */
+ lua_call(L, 1, 0);
+ }
+}
+
+
+static void ICACHE_FLASH_ATTR modinit (lua_State *L, const char *modname) {
+ const char *dot;
+ lua_pushvalue(L, -1);
+ lua_setfield(L, -2, "_M"); /* module._M = module */
+ lua_pushstring(L, modname);
+ lua_setfield(L, -2, "_NAME");
+ dot = c_strrchr(modname, '.'); /* look for last dot in module name */
+ if (dot == NULL) dot = modname;
+ else dot++;
+ /* set _PACKAGE as package name (full module name minus last part) */
+ lua_pushlstring(L, modname, dot - modname);
+ lua_setfield(L, -2, "_PACKAGE");
+}
+
+
+static int ICACHE_FLASH_ATTR ll_module (lua_State *L) {
+ const char *modname = luaL_checkstring(L, 1);
+ if (luaR_findglobal(modname, c_strlen(modname)))
+ return 0;
+ int loaded = lua_gettop(L) + 1; /* index of _LOADED table */
+ lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
+ lua_getfield(L, loaded, modname); /* get _LOADED[modname] */
+ if (!lua_istable(L, -1)) { /* not found? */
+ lua_pop(L, 1); /* remove previous result */
+ /* try global variable (and create one if it does not exist) */
+ if (luaL_findtable(L, LUA_GLOBALSINDEX, modname, 1) != NULL)
+ return luaL_error(L, "name conflict for module " LUA_QS, modname);
+ lua_pushvalue(L, -1);
+ lua_setfield(L, loaded, modname); /* _LOADED[modname] = new table */
+ }
+ /* check whether table already has a _NAME field */
+ lua_getfield(L, -1, "_NAME");
+ if (!lua_isnil(L, -1)) /* is table an initialized module? */
+ lua_pop(L, 1);
+ else { /* no; initialize it */
+ lua_pop(L, 1);
+ modinit(L, modname);
+ }
+ lua_pushvalue(L, -1);
+ setfenv(L);
+ dooptions(L, loaded - 1);
+ return 0;
+}
+
+
+static int ll_seeall (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TTABLE);
+ if (!lua_getmetatable(L, 1)) {
+ lua_createtable(L, 0, 1); /* create new metatable */
+ lua_pushvalue(L, -1);
+ lua_setmetatable(L, 1);
+ }
+ lua_pushvalue(L, LUA_GLOBALSINDEX);
+ lua_setfield(L, -2, "__index"); /* mt.__index = _G */
+ return 0;
+}
+
+
+/* }====================================================== */
+
+
+
+/* auxiliary mark (for internal use) */
+#define AUXMARK "\1"
+
+static void ICACHE_FLASH_ATTR setpath (lua_State *L, const char *fieldname, const char *envname,
+ const char *def) {
+ const char *path = c_getenv(envname);
+ if (path == NULL) /* no environment variable? */
+ lua_pushstring(L, def); /* use default */
+ else {
+ /* replace ";;" by ";AUXMARK;" and then AUXMARK by default path */
+ path = luaL_gsub(L, path, LUA_PATHSEP LUA_PATHSEP,
+ LUA_PATHSEP AUXMARK LUA_PATHSEP);
+ luaL_gsub(L, path, AUXMARK, def);
+ lua_remove(L, -2);
+ }
+ setprogdir(L);
+ lua_setfield(L, -2, fieldname);
+}
+
+
+static const luaL_Reg pk_funcs[] = {
+ {"loadlib", ll_loadlib},
+ {"seeall", ll_seeall},
+ {NULL, NULL}
+};
+
+
+static const luaL_Reg ll_funcs[] = {
+ {"module", ll_module},
+ {"require", ll_require},
+ {NULL, NULL}
+};
+
+
+static const lua_CFunction loaders[] =
+ {loader_preload, loader_Lua, loader_C, loader_Croot, NULL};
+
+#if LUA_OPTIMIZE_MEMORY > 0
+const luaR_entry lmt[] = {
+ {LRO_STRKEY("__gc"), LRO_FUNCVAL(gctm)},
+ {LRO_NILKEY, LRO_NILVAL}
+};
+#endif
+
+LUALIB_API int ICACHE_FLASH_ATTR luaopen_package (lua_State *L) {
+ int i;
+ /* create new type _LOADLIB */
+#if LUA_OPTIMIZE_MEMORY == 0
+ luaL_newmetatable(L, "_LOADLIB");
+ lua_pushlightfunction(L, gctm);
+ lua_setfield(L, -2, "__gc");
+#else
+ luaL_rometatable(L, "_LOADLIB", (void*)lmt);
+#endif
+ /* create `package' table */
+ luaL_register_light(L, LUA_LOADLIBNAME, pk_funcs);
+#if defined(LUA_COMPAT_LOADLIB)
+ lua_getfield(L, -1, "loadlib");
+ lua_setfield(L, LUA_GLOBALSINDEX, "loadlib");
+#endif
+ lua_pushvalue(L, -1);
+ lua_replace(L, LUA_ENVIRONINDEX);
+ /* create `loaders' table */
+ lua_createtable(L, sizeof(loaders)/sizeof(loaders[0]) - 1, 0);
+ /* fill it with pre-defined loaders */
+ for (i=0; loaders[i] != NULL; i++) {
+ lua_pushcfunction(L, loaders[i]);
+ lua_rawseti(L, -2, i+1);
+ }
+ lua_setfield(L, -2, "loaders"); /* put it in field `loaders' */
+ setpath(L, "path", LUA_PATH, LUA_PATH_DEFAULT); /* set field `path' */
+ setpath(L, "cpath", LUA_CPATH, LUA_CPATH_DEFAULT); /* set field `cpath' */
+ /* store config information */
+ lua_pushliteral(L, LUA_DIRSEP "\n" LUA_PATHSEP "\n" LUA_PATH_MARK "\n"
+ LUA_EXECDIR "\n" LUA_IGMARK);
+ lua_setfield(L, -2, "config");
+ /* set field `loaded' */
+ luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 2);
+ lua_setfield(L, -2, "loaded");
+ /* set field `preload' */
+ lua_newtable(L);
+ lua_setfield(L, -2, "preload");
+ lua_pushvalue(L, LUA_GLOBALSINDEX);
+ luaL_register(L, NULL, ll_funcs); /* open lib into global table */
+ lua_pop(L, 1);
+ return 1; /* return 'package' table */
+}
+
diff --git a/app/lua/lobject.c b/app/lua/lobject.c
new file mode 100644
index 00000000..d8fd8344
--- /dev/null
+++ b/app/lua/lobject.c
@@ -0,0 +1,216 @@
+/*
+** $Id: lobject.c,v 2.22.1.1 2007/12/27 13:02:25 roberto Exp $
+** Some generic functions over Lua objects
+** See Copyright Notice in lua.h
+*/
+
+#include "c_ctype.h"
+#include "c_stdarg.h"
+#include "c_stdio.h"
+#include "c_stdlib.h"
+#include "c_string.h"
+
+#define lobject_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "ldo.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "lvm.h"
+
+
+
+const TValue luaO_nilobject_ = {LUA_TVALUE_NIL};
+
+
+/*
+** converts an integer to a "floating point byte", represented as
+** (eeeeexxx), where the real value is (1xxx) * 2^(eeeee - 1) if
+** eeeee != 0 and (xxx) otherwise.
+*/
+int ICACHE_FLASH_ATTR luaO_int2fb (unsigned int x) {
+ int e = 0; /* expoent */
+ while (x >= 16) {
+ x = (x+1) >> 1;
+ e++;
+ }
+ if (x < 8) return x;
+ else return ((e+1) << 3) | (cast_int(x) - 8);
+}
+
+
+/* converts back */
+int ICACHE_FLASH_ATTR luaO_fb2int (int x) {
+ int e = (x >> 3) & 31;
+ if (e == 0) return x;
+ else return ((x & 7)+8) << (e - 1);
+}
+
+
+int ICACHE_FLASH_ATTR luaO_log2 (unsigned int x) {
+ static const lu_byte log_2[256] = {
+ 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8
+ };
+ int l = -1;
+ while (x >= 256) { l += 8; x >>= 8; }
+ return l + log_2[x];
+
+}
+
+
+int ICACHE_FLASH_ATTR luaO_rawequalObj (const TValue *t1, const TValue *t2) {
+ if (ttype(t1) != ttype(t2)) return 0;
+ else switch (ttype(t1)) {
+ case LUA_TNIL:
+ return 1;
+ case LUA_TNUMBER:
+ return luai_numeq(nvalue(t1), nvalue(t2));
+ case LUA_TBOOLEAN:
+ return bvalue(t1) == bvalue(t2); /* boolean true must be 1 !! */
+ case LUA_TLIGHTUSERDATA:
+ case LUA_TROTABLE:
+ case LUA_TLIGHTFUNCTION:
+ return pvalue(t1) == pvalue(t2);
+ default:
+ lua_assert(iscollectable(t1));
+ return gcvalue(t1) == gcvalue(t2);
+ }
+}
+
+
+int ICACHE_FLASH_ATTR luaO_str2d (const char *s, lua_Number *result) {
+ char *endptr;
+ *result = lua_str2number(s, &endptr);
+ if (endptr == s) return 0; /* conversion failed */
+ if (*endptr == 'x' || *endptr == 'X') /* maybe an hexadecimal constant? */
+ *result = cast_num(c_strtoul(s, &endptr, 16));
+ if (*endptr == '\0') return 1; /* most common case */
+ while (isspace(cast(unsigned char, *endptr))) endptr++;
+ if (*endptr != '\0') return 0; /* invalid trailing characters? */
+ return 1;
+}
+
+
+
+static void ICACHE_FLASH_ATTR pushstr (lua_State *L, const char *str) {
+ setsvalue2s(L, L->top, luaS_new(L, str));
+ incr_top(L);
+}
+
+
+/* this function handles only `%d', `%c', %f, %p, and `%s' formats */
+const char *ICACHE_FLASH_ATTR luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) {
+ int n = 1;
+ pushstr(L, "");
+ for (;;) {
+ const char *e = c_strchr(fmt, '%');
+ if (e == NULL) break;
+ setsvalue2s(L, L->top, luaS_newlstr(L, fmt, e-fmt));
+ incr_top(L);
+ switch (*(e+1)) {
+ case 's': {
+ const char *s = va_arg(argp, char *);
+ if (s == NULL) s = "(null)";
+ pushstr(L, s);
+ break;
+ }
+ case 'c': {
+ char buff[2];
+ buff[0] = cast(char, va_arg(argp, int));
+ buff[1] = '\0';
+ pushstr(L, buff);
+ break;
+ }
+ case 'd': {
+ setnvalue(L->top, cast_num(va_arg(argp, int)));
+ incr_top(L);
+ break;
+ }
+ case 'f': {
+ setnvalue(L->top, cast_num(va_arg(argp, l_uacNumber)));
+ incr_top(L);
+ break;
+ }
+ case 'p': {
+ char buff[4*sizeof(void *) + 8]; /* should be enough space for a `%p' */
+ c_sprintf(buff, "%p", va_arg(argp, void *));
+ pushstr(L, buff);
+ break;
+ }
+ case '%': {
+ pushstr(L, "%");
+ break;
+ }
+ default: {
+ char buff[3];
+ buff[0] = '%';
+ buff[1] = *(e+1);
+ buff[2] = '\0';
+ pushstr(L, buff);
+ break;
+ }
+ }
+ n += 2;
+ fmt = e+2;
+ }
+ pushstr(L, fmt);
+ luaV_concat(L, n+1, cast_int(L->top - L->base) - 1);
+ L->top -= n;
+ return svalue(L->top - 1);
+}
+
+
+const char *ICACHE_FLASH_ATTR luaO_pushfstring (lua_State *L, const char *fmt, ...) {
+ const char *msg;
+ va_list argp;
+ va_start(argp, fmt);
+ msg = luaO_pushvfstring(L, fmt, argp);
+ va_end(argp);
+ return msg;
+}
+
+
+void ICACHE_FLASH_ATTR luaO_chunkid (char *out, const char *source, size_t bufflen) {
+ if (*source == '=') {
+ c_strncpy(out, source+1, bufflen); /* remove first char */
+ out[bufflen-1] = '\0'; /* ensures null termination */
+ }
+ else { /* out = "source", or "...source" */
+ if (*source == '@') {
+ size_t l;
+ source++; /* skip the `@' */
+ bufflen -= sizeof(" '...' ");
+ l = c_strlen(source);
+ c_strcpy(out, "");
+ if (l > bufflen) {
+ source += (l-bufflen); /* get last part of file name */
+ c_strcat(out, "...");
+ }
+ c_strcat(out, source);
+ }
+ else { /* out = [string "string"] */
+ size_t len = c_strcspn(source, "\n\r"); /* stop at first newline */
+ bufflen -= sizeof(" [string \"...\"] ");
+ if (len > bufflen) len = bufflen;
+ c_strcpy(out, "[string \"");
+ if (source[len] != '\0') { /* must truncate? */
+ c_strncat(out, source, len);
+ c_strcat(out, "...");
+ }
+ else
+ c_strcat(out, source);
+ c_strcat(out, "\"]");
+ }
+ }
+}
diff --git a/app/lua/lobject.h b/app/lua/lobject.h
new file mode 100644
index 00000000..0ebc5abf
--- /dev/null
+++ b/app/lua/lobject.h
@@ -0,0 +1,551 @@
+/*
+** $Id: lobject.h,v 2.20.1.2 2008/08/06 13:29:48 roberto Exp $
+** Type definitions for Lua objects
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef lobject_h
+#define lobject_h
+
+
+#include "c_stdarg.h"
+
+
+#include "llimits.h"
+#include "lua.h"
+
+
+/* tags for values visible from Lua */
+#define LAST_TAG LUA_TTHREAD
+
+#define NUM_TAGS (LAST_TAG+1)
+
+/* mask for 'read-only' objects. must match READONLYBIT in lgc.h' */
+#define READONLYMASK 128
+
+
+/*
+** Extra tags for non-values
+*/
+#define LUA_TPROTO (LAST_TAG+1)
+#define LUA_TUPVAL (LAST_TAG+2)
+#define LUA_TDEADKEY (LAST_TAG+3)
+
+
+/*
+** Union of all collectable objects
+*/
+typedef union GCObject GCObject;
+
+
+/*
+** Common Header for all collectable objects (in macro form, to be
+** included in other objects)
+*/
+#define CommonHeader GCObject *next; lu_byte tt; lu_byte marked
+
+
+/*
+** Common header in struct form
+*/
+typedef struct GCheader {
+ CommonHeader;
+} GCheader;
+
+
+
+
+/*
+** Union of all Lua values
+*/
+#if defined( LUA_PACK_VALUE ) && defined( ELUA_ENDIAN_BIG )
+typedef union {
+ struct {
+ int _pad0;
+ GCObject *gc;
+ };
+ struct {
+ int _pad1;
+ void *p;
+ };
+ lua_Number n;
+ struct {
+ int _pad2;
+ int b;
+ };
+} Value;
+#else // #if defined( LUA_PACK_VALUE ) && defined( ELUA_ENDIAN_BIG )
+typedef union {
+ GCObject *gc;
+ void *p;
+ lua_Number n;
+ int b;
+} Value;
+#endif // #if defined( LUA_PACK_VALUE ) && defined( ELUA_ENDIAN_BIG )
+
+/*
+** Tagged Values
+*/
+
+#ifndef LUA_PACK_VALUE
+#define TValuefields Value value; int tt
+#define LUA_TVALUE_NIL {NULL}, LUA_TNIL
+
+typedef struct lua_TValue {
+ TValuefields;
+} TValue;
+#else // #ifndef LUA_PACK_VALUE
+#ifdef ELUA_ENDIAN_LITTLE
+#define TValuefields union { \
+ struct { \
+ int _pad0; \
+ int tt_sig; \
+ } _ts; \
+ struct { \
+ int _pad; \
+ short tt; \
+ short sig; \
+ } _t; \
+ Value value; \
+}
+#define LUA_TVALUE_NIL {0, add_sig(LUA_TNIL)}
+#else // #ifdef ELUA_ENDIAN_LITTLE
+#define TValuefields union { \
+ struct { \
+ int tt_sig; \
+ int _pad0; \
+ } _ts; \
+ struct { \
+ short sig; \
+ short tt; \
+ int _pad; \
+ } _t; \
+ Value value; \
+}
+#define LUA_TVALUE_NIL {add_sig(LUA_TNIL), 0}
+#endif // #ifdef ELUA_ENDIAN_LITTLE
+#define LUA_NOTNUMBER_SIG (-1)
+#define add_sig(tt) ( 0xffff0000 | (tt) )
+
+typedef TValuefields TValue;
+#endif // #ifndef LUA_PACK_VALUE
+
+/* Macros to test type */
+#ifndef LUA_PACK_VALUE
+#define ttisnil(o) (ttype(o) == LUA_TNIL)
+#define ttisnumber(o) (ttype(o) == LUA_TNUMBER)
+#define ttisstring(o) (ttype(o) == LUA_TSTRING)
+#define ttistable(o) (ttype(o) == LUA_TTABLE)
+#define ttisfunction(o) (ttype(o) == LUA_TFUNCTION)
+#define ttisboolean(o) (ttype(o) == LUA_TBOOLEAN)
+#define ttisuserdata(o) (ttype(o) == LUA_TUSERDATA)
+#define ttisthread(o) (ttype(o) == LUA_TTHREAD)
+#define ttislightuserdata(o) (ttype(o) == LUA_TLIGHTUSERDATA)
+#define ttisrotable(o) (ttype(o) == LUA_TROTABLE)
+#define ttislightfunction(o) (ttype(o) == LUA_TLIGHTFUNCTION)
+#else // #ifndef LUA_PACK_VALUE
+#define ttisnil(o) (ttype_sig(o) == add_sig(LUA_TNIL))
+#define ttisnumber(o) ((o)->_t.sig != LUA_NOTNUMBER_SIG)
+#define ttisstring(o) (ttype_sig(o) == add_sig(LUA_TSTRING))
+#define ttistable(o) (ttype_sig(o) == add_sig(LUA_TTABLE))
+#define ttisfunction(o) (ttype_sig(o) == add_sig(LUA_TFUNCTION))
+#define ttisboolean(o) (ttype_sig(o) == add_sig(LUA_TBOOLEAN))
+#define ttisuserdata(o) (ttype_sig(o) == add_sig(LUA_TUSERDATA))
+#define ttisthread(o) (ttype_sig(o) == add_sig(LUA_TTHREAD))
+#define ttislightuserdata(o) (ttype_sig(o) == add_sig(LUA_TLIGHTUSERDATA))
+#define ttisrotable(o) (ttype_sig(o) == add_sig(LUA_TROTABLE))
+#define ttislightfunction(o) (ttype_sig(o) == add_sig(LUA_TLIGHTFUNCTION))
+#endif // #ifndef LUA_PACK_VALUE
+
+/* Macros to access values */
+#ifndef LUA_PACK_VALUE
+#define ttype(o) ((o)->tt)
+#else // #ifndef LUA_PACK_VALUE
+#define ttype(o) ((o)->_t.sig == LUA_NOTNUMBER_SIG ? (o)->_t.tt : LUA_TNUMBER)
+#define ttype_sig(o) ((o)->_ts.tt_sig)
+#endif // #ifndef LUA_PACK_VALUE
+#define gcvalue(o) check_exp(iscollectable(o), (o)->value.gc)
+#define pvalue(o) check_exp(ttislightuserdata(o), (o)->value.p)
+#define rvalue(o) check_exp(ttisrotable(o), (o)->value.p)
+#define fvalue(o) check_exp(ttislightfunction(o), (o)->value.p)
+#define nvalue(o) check_exp(ttisnumber(o), (o)->value.n)
+#define rawtsvalue(o) check_exp(ttisstring(o), &(o)->value.gc->ts)
+#define tsvalue(o) (&rawtsvalue(o)->tsv)
+#define rawuvalue(o) check_exp(ttisuserdata(o), &(o)->value.gc->u)
+#define uvalue(o) (&rawuvalue(o)->uv)
+#define clvalue(o) check_exp(ttisfunction(o), &(o)->value.gc->cl)
+#define hvalue(o) check_exp(ttistable(o), &(o)->value.gc->h)
+#define bvalue(o) check_exp(ttisboolean(o), (o)->value.b)
+#define thvalue(o) check_exp(ttisthread(o), &(o)->value.gc->th)
+
+#define l_isfalse(o) (ttisnil(o) || (ttisboolean(o) && bvalue(o) == 0))
+
+/*
+** for internal debug only
+*/
+#ifndef LUA_PACK_VALUE
+#define checkconsistency(obj) \
+ lua_assert(!iscollectable(obj) || (ttype(obj) == (obj)->value.gc->gch.tt))
+
+#define checkliveness(g,obj) \
+ lua_assert(!iscollectable(obj) || \
+ ((ttype(obj) == (obj)->value.gc->gch.tt) && !isdead(g, (obj)->value.gc)))
+#else // #ifndef LUA_PACK_VALUE
+#define checkconsistency(obj) \
+ lua_assert(!iscollectable(obj) || (ttype(obj) == (obj)->value.gc->gch._t.tt))
+
+#define checkliveness(g,obj) \
+ lua_assert(!iscollectable(obj) || \
+ ((ttype(obj) == (obj)->value.gc->gch._t.tt) && !isdead(g, (obj)->value.gc)))
+#endif // #ifndef LUA_PACK_VALUE
+
+/* Macros to set values */
+#ifndef LUA_PACK_VALUE
+#define setnilvalue(obj) ((obj)->tt=LUA_TNIL)
+
+#define setnvalue(obj,x) \
+ { lua_Number i_x = (x); TValue *i_o=(obj); i_o->value.n=i_x; i_o->tt=LUA_TNUMBER; }
+
+#define setpvalue(obj,x) \
+ { void *i_x = (x); TValue *i_o=(obj); i_o->value.p=i_x; i_o->tt=LUA_TLIGHTUSERDATA; }
+
+#define setrvalue(obj,x) \
+ { void *i_x = (x); TValue *i_o=(obj); i_o->value.p=i_x; i_o->tt=LUA_TROTABLE; }
+
+#define setfvalue(obj,x) \
+ { void *i_x = (x); TValue *i_o=(obj); i_o->value.p=i_x; i_o->tt=LUA_TLIGHTFUNCTION; }
+
+#define setbvalue(obj,x) \
+ { int i_x = (x); TValue *i_o=(obj); i_o->value.b=i_x; i_o->tt=LUA_TBOOLEAN; }
+
+#define setsvalue(L,obj,x) \
+ { GCObject *i_x = cast(GCObject *, (x)); \
+ TValue *i_o=(obj); \
+ i_o->value.gc=i_x; i_o->tt=LUA_TSTRING; \
+ checkliveness(G(L),i_o); }
+
+#define setuvalue(L,obj,x) \
+ { GCObject *i_x = cast(GCObject *, (x)); \
+ TValue *i_o=(obj); \
+ i_o->value.gc=i_x; i_o->tt=LUA_TUSERDATA; \
+ checkliveness(G(L),i_o); }
+
+#define setthvalue(L,obj,x) \
+ { GCObject *i_x = cast(GCObject *, (x)); \
+ TValue *i_o=(obj); \
+ i_o->value.gc=i_x; i_o->tt=LUA_TTHREAD; \
+ checkliveness(G(L),i_o); }
+
+#define setclvalue(L,obj,x) \
+ { GCObject *i_x = cast(GCObject *, (x)); \
+ TValue *i_o=(obj); \
+ i_o->value.gc=i_x; i_o->tt=LUA_TFUNCTION; \
+ checkliveness(G(L),i_o); }
+
+#define sethvalue(L,obj,x) \
+ { GCObject *i_x = cast(GCObject *, (x)); \
+ TValue *i_o=(obj); \
+ i_o->value.gc=i_x; i_o->tt=LUA_TTABLE; \
+ checkliveness(G(L),i_o); }
+
+#define setptvalue(L,obj,x) \
+ { GCObject *i_x = cast(GCObject *, (x)); \
+ TValue *i_o=(obj); \
+ i_o->value.gc=i_x; i_o->tt=LUA_TPROTO; \
+ checkliveness(G(L),i_o); }
+
+
+
+
+#define setobj(L,obj1,obj2) \
+ { const TValue *o2=(obj2); TValue *o1=(obj1); \
+ o1->value = o2->value; o1->tt=o2->tt; \
+ checkliveness(G(L),o1); }
+#else // #ifndef LUA_PACK_VALUE
+#define setnilvalue(obj) ( ttype_sig(obj) = add_sig(LUA_TNIL) )
+
+#define setnvalue(obj,x) \
+ { TValue *i_o=(obj); i_o->value.n=(x); }
+
+#define setpvalue(obj,x) \
+ { TValue *i_o=(obj); i_o->value.p=(x); i_o->_ts.tt_sig=add_sig(LUA_TLIGHTUSERDATA);}
+
+#define setrvalue(obj,x) \
+ { TValue *i_o=(obj); i_o->value.p=(x); i_o->_ts.tt_sig=add_sig(LUA_TROTABLE);}
+
+#define setfvalue(obj,x) \
+ { TValue *i_o=(obj); i_o->value.p=(x); i_o->_ts.tt_sig=add_sig(LUA_TLIGHTFUNCTION);}
+
+#define setbvalue(obj,x) \
+ { TValue *i_o=(obj); i_o->value.b=(x); i_o->_ts.tt_sig=add_sig(LUA_TBOOLEAN);}
+
+#define setsvalue(L,obj,x) \
+ { TValue *i_o=(obj); \
+ i_o->value.gc=cast(GCObject *, (x)); i_o->_ts.tt_sig=add_sig(LUA_TSTRING); \
+ checkliveness(G(L),i_o); }
+
+#define setuvalue(L,obj,x) \
+ { TValue *i_o=(obj); \
+ i_o->value.gc=cast(GCObject *, (x)); i_o->_ts.tt_sig=add_sig(LUA_TUSERDATA); \
+ checkliveness(G(L),i_o); }
+
+#define setthvalue(L,obj,x) \
+ { TValue *i_o=(obj); \
+ i_o->value.gc=cast(GCObject *, (x)); i_o->_ts.tt_sig=add_sig(LUA_TTHREAD); \
+ checkliveness(G(L),i_o); }
+
+#define setclvalue(L,obj,x) \
+ { TValue *i_o=(obj); \
+ i_o->value.gc=cast(GCObject *, (x)); i_o->_ts.tt_sig=add_sig(LUA_TFUNCTION); \
+ checkliveness(G(L),i_o); }
+
+#define sethvalue(L,obj,x) \
+ { TValue *i_o=(obj); \
+ i_o->value.gc=cast(GCObject *, (x)); i_o->_ts.tt_sig=add_sig(LUA_TTABLE); \
+ checkliveness(G(L),i_o); }
+
+#define setptvalue(L,obj,x) \
+ { TValue *i_o=(obj); \
+ i_o->value.gc=cast(GCObject *, (x)); i_o->_ts.tt_sig=add_sig(LUA_TPROTO); \
+ checkliveness(G(L),i_o); }
+
+
+
+
+#define setobj(L,obj1,obj2) \
+ { const TValue *o2=(obj2); TValue *o1=(obj1); \
+ o1->value = o2->value; \
+ checkliveness(G(L),o1); }
+#endif // #ifndef LUA_PACK_VALUE
+
+/*
+** different types of sets, according to destination
+*/
+
+/* from stack to (same) stack */
+#define setobjs2s setobj
+/* to stack (not from same stack) */
+#define setobj2s setobj
+#define setsvalue2s setsvalue
+#define sethvalue2s sethvalue
+#define setptvalue2s setptvalue
+/* from table to same table */
+#define setobjt2t setobj
+/* to table */
+#define setobj2t setobj
+/* to new object */
+#define setobj2n setobj
+#define setsvalue2n setsvalue
+
+#ifndef LUA_PACK_VALUE
+#define setttype(obj, tt) (ttype(obj) = (tt))
+#else // #ifndef LUA_PACK_VALUE
+/* considering it used only in lgc to set LUA_TDEADKEY */
+/* we could define it this way */
+#define setttype(obj, _tt) ( ttype_sig(obj) = add_sig(_tt) )
+#endif // #ifndef LUA_PACK_VALUE
+
+#define iscollectable(o) (ttype(o) >= LUA_TSTRING)
+
+
+
+typedef TValue *StkId; /* index to stack elements */
+
+
+/*
+** String headers for string table
+*/
+typedef union TString {
+ L_Umaxalign dummy; /* ensures maximum alignment for strings */
+ struct {
+ CommonHeader;
+ unsigned int hash;
+ size_t len;
+ } tsv;
+} TString;
+
+
+#define getstr(ts) (((ts)->tsv.marked & READONLYMASK) ? cast(const char *, *(const char**)((ts) + 1)) : cast(const char *, (ts) + 1))
+#define svalue(o) getstr(rawtsvalue(o))
+
+
+
+typedef union Udata {
+ L_Umaxalign dummy; /* ensures maximum alignment for `local' udata */
+ struct {
+ CommonHeader;
+ struct Table *metatable;
+ struct Table *env;
+ size_t len;
+ } uv;
+} Udata;
+
+
+
+
+/*
+** Function Prototypes
+*/
+typedef struct Proto {
+ CommonHeader;
+ TValue *k; /* constants used by the function */
+ Instruction *code;
+ struct Proto **p; /* functions defined inside the function */
+ int *lineinfo; /* map from opcodes to source lines */
+ struct LocVar *locvars; /* information about local variables */
+ TString **upvalues; /* upvalue names */
+ TString *source;
+ int sizeupvalues;
+ int sizek; /* size of `k' */
+ int sizecode;
+ int sizelineinfo;
+ int sizep; /* size of `p' */
+ int sizelocvars;
+ int linedefined;
+ int lastlinedefined;
+ GCObject *gclist;
+ lu_byte nups; /* number of upvalues */
+ lu_byte numparams;
+ lu_byte is_vararg;
+ lu_byte maxstacksize;
+} Proto;
+
+
+/* masks for new-style vararg */
+#define VARARG_HASARG 1
+#define VARARG_ISVARARG 2
+#define VARARG_NEEDSARG 4
+
+
+typedef struct LocVar {
+ TString *varname;
+ int startpc; /* first point where variable is active */
+ int endpc; /* first point where variable is dead */
+} LocVar;
+
+
+
+/*
+** Upvalues
+*/
+
+typedef struct UpVal {
+ CommonHeader;
+ TValue *v; /* points to stack or to its own value */
+ union {
+ TValue value; /* the value (when closed) */
+ struct { /* double linked list (when open) */
+ struct UpVal *prev;
+ struct UpVal *next;
+ } l;
+ } u;
+} UpVal;
+
+
+/*
+** Closures
+*/
+
+#define ClosureHeader \
+ CommonHeader; lu_byte isC; lu_byte nupvalues; GCObject *gclist; \
+ struct Table *env
+
+typedef struct CClosure {
+ ClosureHeader;
+ lua_CFunction f;
+ TValue upvalue[1];
+} CClosure;
+
+
+typedef struct LClosure {
+ ClosureHeader;
+ struct Proto *p;
+ UpVal *upvals[1];
+} LClosure;
+
+
+typedef union Closure {
+ CClosure c;
+ LClosure l;
+} Closure;
+
+
+#define iscfunction(o) ((ttype(o) == LUA_TFUNCTION && clvalue(o)->c.isC)||(ttype(o)==LUA_TLIGHTFUNCTION))
+#define isLfunction(o) (ttype(o) == LUA_TFUNCTION && !clvalue(o)->c.isC)
+
+
+/*
+** Tables
+*/
+
+#ifndef LUA_PACK_VALUE
+typedef union TKey {
+ struct {
+ TValuefields;
+ struct Node *next; /* for chaining */
+ } nk;
+ TValue tvk;
+} TKey;
+
+#define LUA_TKEY_NIL {LUA_TVALUE_NIL, NULL}
+#else // #ifndef LUA_PACK_VALUE
+typedef struct TKey {
+ TValue tvk;
+ struct {
+ struct Node *next; /* for chaining */
+ } nk;
+} TKey;
+
+#define LUA_TKEY_NIL {LUA_TVALUE_NIL}, {NULL}
+#endif // #ifndef LUA_PACK_VALUE
+
+typedef struct Node {
+ TValue i_val;
+ TKey i_key;
+} Node;
+
+
+typedef struct Table {
+ CommonHeader;
+ lu_byte flags; /* 1<lsizenode))
+
+
+#define luaO_nilobject (&luaO_nilobject_)
+
+LUAI_DATA const TValue luaO_nilobject_;
+
+#define ceillog2(x) (luaO_log2((x)-1) + 1)
+
+LUAI_FUNC int luaO_log2 (unsigned int x);
+LUAI_FUNC int luaO_int2fb (unsigned int x);
+LUAI_FUNC int luaO_fb2int (int x);
+LUAI_FUNC int luaO_rawequalObj (const TValue *t1, const TValue *t2);
+LUAI_FUNC int luaO_str2d (const char *s, lua_Number *result);
+LUAI_FUNC const char *luaO_pushvfstring (lua_State *L, const char *fmt,
+ va_list argp);
+LUAI_FUNC const char *luaO_pushfstring (lua_State *L, const char *fmt, ...);
+LUAI_FUNC void luaO_chunkid (char *out, const char *source, size_t len);
+
+
+#endif
+
diff --git a/app/lua/lopcodes.c b/app/lua/lopcodes.c
new file mode 100644
index 00000000..4cc74523
--- /dev/null
+++ b/app/lua/lopcodes.c
@@ -0,0 +1,102 @@
+/*
+** $Id: lopcodes.c,v 1.37.1.1 2007/12/27 13:02:25 roberto Exp $
+** See Copyright Notice in lua.h
+*/
+
+
+#define lopcodes_c
+#define LUA_CORE
+
+
+#include "lopcodes.h"
+
+
+/* ORDER OP */
+
+const char *const luaP_opnames[NUM_OPCODES+1] = {
+ "MOVE",
+ "LOADK",
+ "LOADBOOL",
+ "LOADNIL",
+ "GETUPVAL",
+ "GETGLOBAL",
+ "GETTABLE",
+ "SETGLOBAL",
+ "SETUPVAL",
+ "SETTABLE",
+ "NEWTABLE",
+ "SELF",
+ "ADD",
+ "SUB",
+ "MUL",
+ "DIV",
+ "MOD",
+ "POW",
+ "UNM",
+ "NOT",
+ "LEN",
+ "CONCAT",
+ "JMP",
+ "EQ",
+ "LT",
+ "LE",
+ "TEST",
+ "TESTSET",
+ "CALL",
+ "TAILCALL",
+ "RETURN",
+ "FORLOOP",
+ "FORPREP",
+ "TFORLOOP",
+ "SETLIST",
+ "CLOSE",
+ "CLOSURE",
+ "VARARG",
+ NULL
+};
+
+
+#define opmode(t,a,b,c,m) (((t)<<7) | ((a)<<6) | ((b)<<4) | ((c)<<2) | (m))
+
+const lu_byte luaP_opmodes[NUM_OPCODES] = {
+/* T A B C mode opcode */
+ opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_MOVE */
+ ,opmode(0, 1, OpArgK, OpArgN, iABx) /* OP_LOADK */
+ ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_LOADBOOL */
+ ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_LOADNIL */
+ ,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_GETUPVAL */
+ ,opmode(0, 1, OpArgK, OpArgN, iABx) /* OP_GETGLOBAL */
+ ,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_GETTABLE */
+ ,opmode(0, 0, OpArgK, OpArgN, iABx) /* OP_SETGLOBAL */
+ ,opmode(0, 0, OpArgU, OpArgN, iABC) /* OP_SETUPVAL */
+ ,opmode(0, 0, OpArgK, OpArgK, iABC) /* OP_SETTABLE */
+ ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_NEWTABLE */
+ ,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_SELF */
+ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_ADD */
+ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_SUB */
+ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_MUL */
+ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_DIV */
+ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_MOD */
+ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_POW */
+ ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_UNM */
+ ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_NOT */
+ ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_LEN */
+ ,opmode(0, 1, OpArgR, OpArgR, iABC) /* OP_CONCAT */
+ ,opmode(0, 0, OpArgR, OpArgN, iAsBx) /* OP_JMP */
+ ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_EQ */
+ ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LT */
+ ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LE */
+ ,opmode(1, 1, OpArgR, OpArgU, iABC) /* OP_TEST */
+ ,opmode(1, 1, OpArgR, OpArgU, iABC) /* OP_TESTSET */
+ ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_CALL */
+ ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_TAILCALL */
+ ,opmode(0, 0, OpArgU, OpArgN, iABC) /* OP_RETURN */
+ ,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_FORLOOP */
+ ,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_FORPREP */
+ ,opmode(1, 0, OpArgN, OpArgU, iABC) /* OP_TFORLOOP */
+ ,opmode(0, 0, OpArgU, OpArgU, iABC) /* OP_SETLIST */
+ ,opmode(0, 0, OpArgN, OpArgN, iABC) /* OP_CLOSE */
+ ,opmode(0, 1, OpArgU, OpArgN, iABx) /* OP_CLOSURE */
+ ,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_VARARG */
+};
+
diff --git a/app/lua/lopcodes.h b/app/lua/lopcodes.h
new file mode 100644
index 00000000..41224d6e
--- /dev/null
+++ b/app/lua/lopcodes.h
@@ -0,0 +1,268 @@
+/*
+** $Id: lopcodes.h,v 1.125.1.1 2007/12/27 13:02:25 roberto Exp $
+** Opcodes for Lua virtual machine
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lopcodes_h
+#define lopcodes_h
+
+#include "llimits.h"
+
+
+/*===========================================================================
+ We assume that instructions are unsigned numbers.
+ All instructions have an opcode in the first 6 bits.
+ Instructions can have the following fields:
+ `A' : 8 bits
+ `B' : 9 bits
+ `C' : 9 bits
+ `Bx' : 18 bits (`B' and `C' together)
+ `sBx' : signed Bx
+
+ A signed argument is represented in excess K; that is, the number
+ value is the unsigned value minus K. K is exactly the maximum value
+ for that argument (so that -max is represented by 0, and +max is
+ represented by 2*max), which is half the maximum for the corresponding
+ unsigned argument.
+===========================================================================*/
+
+
+enum OpMode {iABC, iABx, iAsBx}; /* basic instruction format */
+
+
+/*
+** size and position of opcode arguments.
+*/
+#define SIZE_C 9
+#define SIZE_B 9
+#define SIZE_Bx (SIZE_C + SIZE_B)
+#define SIZE_A 8
+
+#define SIZE_OP 6
+
+#define POS_OP 0
+#define POS_A (POS_OP + SIZE_OP)
+#define POS_C (POS_A + SIZE_A)
+#define POS_B (POS_C + SIZE_C)
+#define POS_Bx POS_C
+
+
+/*
+** limits for opcode arguments.
+** we use (signed) int to manipulate most arguments,
+** so they must fit in LUAI_BITSINT-1 bits (-1 for sign)
+*/
+#if SIZE_Bx < LUAI_BITSINT-1
+#define MAXARG_Bx ((1<>1) /* `sBx' is signed */
+#else
+#define MAXARG_Bx MAX_INT
+#define MAXARG_sBx MAX_INT
+#endif
+
+
+#define MAXARG_A ((1<>POS_OP) & MASK1(SIZE_OP,0)))
+#define SET_OPCODE(i,o) ((i) = (((i)&MASK0(SIZE_OP,POS_OP)) | \
+ ((cast(Instruction, o)<>POS_A) & MASK1(SIZE_A,0)))
+#define SETARG_A(i,u) ((i) = (((i)&MASK0(SIZE_A,POS_A)) | \
+ ((cast(Instruction, u)<>POS_B) & MASK1(SIZE_B,0)))
+#define SETARG_B(i,b) ((i) = (((i)&MASK0(SIZE_B,POS_B)) | \
+ ((cast(Instruction, b)<>POS_C) & MASK1(SIZE_C,0)))
+#define SETARG_C(i,b) ((i) = (((i)&MASK0(SIZE_C,POS_C)) | \
+ ((cast(Instruction, b)<>POS_Bx) & MASK1(SIZE_Bx,0)))
+#define SETARG_Bx(i,b) ((i) = (((i)&MASK0(SIZE_Bx,POS_Bx)) | \
+ ((cast(Instruction, b)< C) then pc++ */
+OP_TESTSET,/* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */
+
+OP_CALL,/* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */
+OP_TAILCALL,/* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */
+OP_RETURN,/* A B return R(A), ... ,R(A+B-2) (see note) */
+
+OP_FORLOOP,/* A sBx R(A)+=R(A+2);
+ if R(A) = R(A+1) then { pc+=sBx; R(A+3)=R(A) }*/
+OP_FORPREP,/* A sBx R(A)-=R(A+2); pc+=sBx */
+
+OP_TFORLOOP,/* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2));
+ if R(A+3) ~= nil then R(A+2)=R(A+3) else pc++ */
+OP_SETLIST,/* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */
+
+OP_CLOSE,/* A close all variables in the stack up to (>=) R(A)*/
+OP_CLOSURE,/* A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n)) */
+
+OP_VARARG/* A B R(A), R(A+1), ..., R(A+B-1) = vararg */
+} OpCode;
+
+
+#define NUM_OPCODES (cast(int, OP_VARARG) + 1)
+
+
+
+/*===========================================================================
+ Notes:
+ (*) In OP_CALL, if (B == 0) then B = top. C is the number of returns - 1,
+ and can be 0: OP_CALL then sets `top' to last_result+1, so
+ next open instruction (OP_CALL, OP_RETURN, OP_SETLIST) may use `top'.
+
+ (*) In OP_VARARG, if (B == 0) then use actual number of varargs and
+ set top (like in OP_CALL with C == 0).
+
+ (*) In OP_RETURN, if (B == 0) then return up to `top'
+
+ (*) In OP_SETLIST, if (B == 0) then B = `top';
+ if (C == 0) then next `instruction' is real C
+
+ (*) For comparisons, A specifies what condition the test should accept
+ (true or false).
+
+ (*) All `skips' (pc++) assume that next instruction is a jump
+===========================================================================*/
+
+
+/*
+** masks for instruction properties. The format is:
+** bits 0-1: op mode
+** bits 2-3: C arg mode
+** bits 4-5: B arg mode
+** bit 6: instruction set register A
+** bit 7: operator is a test
+*/
+
+enum OpArgMask {
+ OpArgN, /* argument is not used */
+ OpArgU, /* argument is used */
+ OpArgR, /* argument is a register or a jump offset */
+ OpArgK /* argument is a constant or register/constant */
+};
+
+LUAI_DATA const lu_byte luaP_opmodes[NUM_OPCODES];
+
+#define getOpMode(m) (cast(enum OpMode, luaP_opmodes[m] & 3))
+#define getBMode(m) (cast(enum OpArgMask, (luaP_opmodes[m] >> 4) & 3))
+#define getCMode(m) (cast(enum OpArgMask, (luaP_opmodes[m] >> 2) & 3))
+#define testAMode(m) (luaP_opmodes[m] & (1 << 6))
+#define testTMode(m) (luaP_opmodes[m] & (1 << 7))
+
+
+LUAI_DATA const char *const luaP_opnames[NUM_OPCODES+1]; /* opcode names */
+
+
+/* number of list items to accumulate before a SETLIST instruction */
+#define LFIELDS_PER_FLUSH 50
+
+
+#endif
diff --git a/app/lua/lparser.c b/app/lua/lparser.c
new file mode 100644
index 00000000..98c810a2
--- /dev/null
+++ b/app/lua/lparser.c
@@ -0,0 +1,1345 @@
+/*
+** $Id: lparser.c,v 2.42.1.3 2007/12/28 15:32:23 roberto Exp $
+** Lua Parser
+** See Copyright Notice in lua.h
+*/
+
+
+#include "c_string.h"
+
+#define lparser_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "lcode.h"
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "llex.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lparser.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+
+
+
+#define hasmultret(k) ((k) == VCALL || (k) == VVARARG)
+
+#define getlocvar(fs, i) ((fs)->f->locvars[(fs)->actvar[i]])
+
+#define luaY_checklimit(fs,v,l,m) if ((v)>(l)) errorlimit(fs,l,m)
+
+
+/*
+** nodes for block list (list of active blocks)
+*/
+typedef struct BlockCnt {
+ struct BlockCnt *previous; /* chain */
+ int breaklist; /* list of jumps out of this loop */
+ lu_byte nactvar; /* # active locals outside the breakable structure */
+ lu_byte upval; /* true if some variable in the block is an upvalue */
+ lu_byte isbreakable; /* true if `block' is a loop */
+} BlockCnt;
+
+
+
+/*
+** prototypes for recursive non-terminal functions
+*/
+static void chunk (LexState *ls);
+static void expr (LexState *ls, expdesc *v);
+
+
+static void ICACHE_FLASH_ATTR anchor_token (LexState *ls) {
+ if (ls->t.token == TK_NAME || ls->t.token == TK_STRING) {
+ TString *ts = ls->t.seminfo.ts;
+ luaX_newstring(ls, getstr(ts), ts->tsv.len);
+ }
+}
+
+
+static void ICACHE_FLASH_ATTR error_expected (LexState *ls, int token) {
+ luaX_syntaxerror(ls,
+ luaO_pushfstring(ls->L, LUA_QS " expected", luaX_token2str(ls, token)));
+}
+
+
+static void ICACHE_FLASH_ATTR errorlimit (FuncState *fs, int limit, const char *what) {
+ const char *msg = (fs->f->linedefined == 0) ?
+ luaO_pushfstring(fs->L, "main function has more than %d %s", limit, what) :
+ luaO_pushfstring(fs->L, "function at line %d has more than %d %s",
+ fs->f->linedefined, limit, what);
+ luaX_lexerror(fs->ls, msg, 0);
+}
+
+
+static int ICACHE_FLASH_ATTR testnext (LexState *ls, int c) {
+ if (ls->t.token == c) {
+ luaX_next(ls);
+ return 1;
+ }
+ else return 0;
+}
+
+
+static void ICACHE_FLASH_ATTR check (LexState *ls, int c) {
+ if (ls->t.token != c)
+ error_expected(ls, c);
+}
+
+static void ICACHE_FLASH_ATTR checknext (LexState *ls, int c) {
+ check(ls, c);
+ luaX_next(ls);
+}
+
+
+#define check_condition(ls,c,msg) { if (!(c)) luaX_syntaxerror(ls, msg); }
+
+
+
+static void ICACHE_FLASH_ATTR check_match (LexState *ls, int what, int who, int where) {
+ if (!testnext(ls, what)) {
+ if (where == ls->linenumber)
+ error_expected(ls, what);
+ else {
+ luaX_syntaxerror(ls, luaO_pushfstring(ls->L,
+ LUA_QS " expected (to close " LUA_QS " at line %d)",
+ luaX_token2str(ls, what), luaX_token2str(ls, who), where));
+ }
+ }
+}
+
+
+static TString *ICACHE_FLASH_ATTR str_checkname (LexState *ls) {
+ TString *ts;
+ check(ls, TK_NAME);
+ ts = ls->t.seminfo.ts;
+ luaX_next(ls);
+ return ts;
+}
+
+
+static void ICACHE_FLASH_ATTR init_exp (expdesc *e, expkind k, int i) {
+ e->f = e->t = NO_JUMP;
+ e->k = k;
+ e->u.s.info = i;
+}
+
+
+static void ICACHE_FLASH_ATTR codestring (LexState *ls, expdesc *e, TString *s) {
+ init_exp(e, VK, luaK_stringK(ls->fs, s));
+}
+
+
+static void ICACHE_FLASH_ATTR checkname(LexState *ls, expdesc *e) {
+ codestring(ls, e, str_checkname(ls));
+}
+
+
+static int ICACHE_FLASH_ATTR registerlocalvar (LexState *ls, TString *varname) {
+ FuncState *fs = ls->fs;
+ Proto *f = fs->f;
+ int oldsize = f->sizelocvars;
+ luaM_growvector(ls->L, f->locvars, fs->nlocvars, f->sizelocvars,
+ LocVar, SHRT_MAX, "too many local variables");
+ while (oldsize < f->sizelocvars) f->locvars[oldsize++].varname = NULL;
+ f->locvars[fs->nlocvars].varname = varname;
+ luaC_objbarrier(ls->L, f, varname);
+ return fs->nlocvars++;
+}
+
+
+#define new_localvarliteral(ls,v,n) \
+ new_localvar(ls, luaX_newstring(ls, "" v, (sizeof(v)/sizeof(char))-1), n)
+
+
+static void ICACHE_FLASH_ATTR new_localvar (LexState *ls, TString *name, int n) {
+ FuncState *fs = ls->fs;
+ luaY_checklimit(fs, fs->nactvar+n+1, LUAI_MAXVARS, "local variables");
+ fs->actvar[fs->nactvar+n] = cast(unsigned short, registerlocalvar(ls, name));
+}
+
+
+static void ICACHE_FLASH_ATTR adjustlocalvars (LexState *ls, int nvars) {
+ FuncState *fs = ls->fs;
+ fs->nactvar = cast_byte(fs->nactvar + nvars);
+ for (; nvars; nvars--) {
+ getlocvar(fs, fs->nactvar - nvars).startpc = fs->pc;
+ }
+}
+
+
+static void ICACHE_FLASH_ATTR removevars (LexState *ls, int tolevel) {
+ FuncState *fs = ls->fs;
+ while (fs->nactvar > tolevel)
+ getlocvar(fs, --fs->nactvar).endpc = fs->pc;
+}
+
+
+static int ICACHE_FLASH_ATTR indexupvalue (FuncState *fs, TString *name, expdesc *v) {
+ int i;
+ Proto *f = fs->f;
+ int oldsize = f->sizeupvalues;
+ for (i=0; inups; i++) {
+ if (fs->upvalues[i].k == v->k && fs->upvalues[i].info == v->u.s.info) {
+ lua_assert(f->upvalues[i] == name);
+ return i;
+ }
+ }
+ /* new one */
+ luaY_checklimit(fs, f->nups + 1, LUAI_MAXUPVALUES, "upvalues");
+ luaM_growvector(fs->L, f->upvalues, f->nups, f->sizeupvalues,
+ TString *, MAX_INT, "");
+ while (oldsize < f->sizeupvalues) f->upvalues[oldsize++] = NULL;
+ f->upvalues[f->nups] = name;
+ luaC_objbarrier(fs->L, f, name);
+ lua_assert(v->k == VLOCAL || v->k == VUPVAL);
+ fs->upvalues[f->nups].k = cast_byte(v->k);
+ fs->upvalues[f->nups].info = cast_byte(v->u.s.info);
+ return f->nups++;
+}
+
+
+static int ICACHE_FLASH_ATTR searchvar (FuncState *fs, TString *n) {
+ int i;
+ for (i=fs->nactvar-1; i >= 0; i--) {
+ if (n == getlocvar(fs, i).varname)
+ return i;
+ }
+ return -1; /* not found */
+}
+
+
+static void ICACHE_FLASH_ATTR markupval (FuncState *fs, int level) {
+ BlockCnt *bl = fs->bl;
+ while (bl && bl->nactvar > level) bl = bl->previous;
+ if (bl) bl->upval = 1;
+}
+
+
+static int ICACHE_FLASH_ATTR singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) {
+ if (fs == NULL) { /* no more levels? */
+ init_exp(var, VGLOBAL, NO_REG); /* default is global variable */
+ return VGLOBAL;
+ }
+ else {
+ int v = searchvar(fs, n); /* look up at current level */
+ if (v >= 0) {
+ init_exp(var, VLOCAL, v);
+ if (!base)
+ markupval(fs, v); /* local will be used as an upval */
+ return VLOCAL;
+ }
+ else { /* not found at current level; try upper one */
+ if (singlevaraux(fs->prev, n, var, 0) == VGLOBAL)
+ return VGLOBAL;
+ var->u.s.info = indexupvalue(fs, n, var); /* else was LOCAL or UPVAL */
+ var->k = VUPVAL; /* upvalue in this level */
+ return VUPVAL;
+ }
+ }
+}
+
+
+static void ICACHE_FLASH_ATTR singlevar (LexState *ls, expdesc *var) {
+ TString *varname = str_checkname(ls);
+ FuncState *fs = ls->fs;
+ if (singlevaraux(fs, varname, var, 1) == VGLOBAL)
+ var->u.s.info = luaK_stringK(fs, varname); /* info points to global name */
+}
+
+
+static void ICACHE_FLASH_ATTR adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) {
+ FuncState *fs = ls->fs;
+ int extra = nvars - nexps;
+ if (hasmultret(e->k)) {
+ extra++; /* includes call itself */
+ if (extra < 0) extra = 0;
+ luaK_setreturns(fs, e, extra); /* last exp. provides the difference */
+ if (extra > 1) luaK_reserveregs(fs, extra-1);
+ }
+ else {
+ if (e->k != VVOID) luaK_exp2nextreg(fs, e); /* close last expression */
+ if (extra > 0) {
+ int reg = fs->freereg;
+ luaK_reserveregs(fs, extra);
+ luaK_nil(fs, reg, extra);
+ }
+ }
+}
+
+
+static void ICACHE_FLASH_ATTR enterlevel (LexState *ls) {
+ if (++ls->L->nCcalls > LUAI_MAXCCALLS)
+ luaX_lexerror(ls, "chunk has too many syntax levels", 0);
+}
+
+
+#define leavelevel(ls) ((ls)->L->nCcalls--)
+
+
+static void ICACHE_FLASH_ATTR enterblock (FuncState *fs, BlockCnt *bl, lu_byte isbreakable) {
+ bl->breaklist = NO_JUMP;
+ bl->isbreakable = isbreakable;
+ bl->nactvar = fs->nactvar;
+ bl->upval = 0;
+ bl->previous = fs->bl;
+ fs->bl = bl;
+ lua_assert(fs->freereg == fs->nactvar);
+}
+
+
+static void ICACHE_FLASH_ATTR leaveblock (FuncState *fs) {
+ BlockCnt *bl = fs->bl;
+ fs->bl = bl->previous;
+ removevars(fs->ls, bl->nactvar);
+ if (bl->upval)
+ luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0);
+ /* a block either controls scope or breaks (never both) */
+ lua_assert(!bl->isbreakable || !bl->upval);
+ lua_assert(bl->nactvar == fs->nactvar);
+ fs->freereg = fs->nactvar; /* free registers */
+ luaK_patchtohere(fs, bl->breaklist);
+}
+
+
+static void ICACHE_FLASH_ATTR pushclosure (LexState *ls, FuncState *func, expdesc *v) {
+ FuncState *fs = ls->fs;
+ Proto *f = fs->f;
+ int oldsize = f->sizep;
+ int i;
+ luaM_growvector(ls->L, f->p, fs->np, f->sizep, Proto *,
+ MAXARG_Bx, "constant table overflow");
+ while (oldsize < f->sizep) f->p[oldsize++] = NULL;
+ f->p[fs->np++] = func->f;
+ luaC_objbarrier(ls->L, f, func->f);
+ init_exp(v, VRELOCABLE, luaK_codeABx(fs, OP_CLOSURE, 0, fs->np-1));
+ for (i=0; if->nups; i++) {
+ OpCode o = (func->upvalues[i].k == VLOCAL) ? OP_MOVE : OP_GETUPVAL;
+ luaK_codeABC(fs, o, 0, func->upvalues[i].info, 0);
+ }
+}
+
+
+static void ICACHE_FLASH_ATTR open_func (LexState *ls, FuncState *fs) {
+ lua_State *L = ls->L;
+ Proto *f = luaF_newproto(L);
+ fs->f = f;
+ fs->prev = ls->fs; /* linked list of funcstates */
+ fs->ls = ls;
+ fs->L = L;
+ ls->fs = fs;
+ fs->pc = 0;
+ fs->lasttarget = -1;
+ fs->jpc = NO_JUMP;
+ fs->freereg = 0;
+ fs->nk = 0;
+ fs->np = 0;
+ fs->nlocvars = 0;
+ fs->nactvar = 0;
+ fs->bl = NULL;
+ f->source = ls->source;
+ f->maxstacksize = 2; /* registers 0/1 are always valid */
+ fs->h = luaH_new(L, 0, 0);
+ /* anchor table of constants and prototype (to avoid being collected) */
+ sethvalue2s(L, L->top, fs->h);
+ incr_top(L);
+ setptvalue2s(L, L->top, f);
+ incr_top(L);
+}
+
+
+static void ICACHE_FLASH_ATTR close_func (LexState *ls) {
+ lua_State *L = ls->L;
+ FuncState *fs = ls->fs;
+ Proto *f = fs->f;
+ removevars(ls, 0);
+ luaK_ret(fs, 0, 0); /* final return */
+ luaM_reallocvector(L, f->code, f->sizecode, fs->pc, Instruction);
+ f->sizecode = fs->pc;
+ luaM_reallocvector(L, f->lineinfo, f->sizelineinfo, fs->pc, int);
+ f->sizelineinfo = fs->pc;
+ luaM_reallocvector(L, f->k, f->sizek, fs->nk, TValue);
+ f->sizek = fs->nk;
+ luaM_reallocvector(L, f->p, f->sizep, fs->np, Proto *);
+ f->sizep = fs->np;
+ luaM_reallocvector(L, f->locvars, f->sizelocvars, fs->nlocvars, LocVar);
+ f->sizelocvars = fs->nlocvars;
+ luaM_reallocvector(L, f->upvalues, f->sizeupvalues, f->nups, TString *);
+ f->sizeupvalues = f->nups;
+ lua_assert(luaG_checkcode(f));
+ lua_assert(fs->bl == NULL);
+ ls->fs = fs->prev;
+ /* last token read was anchored in defunct function; must reanchor it */
+ if (fs) anchor_token(ls);
+ L->top -= 2; /* remove table and prototype from the stack */
+}
+
+
+Proto *ICACHE_FLASH_ATTR luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, const char *name) {
+ struct LexState lexstate;
+ struct FuncState funcstate;
+ TString *tname = luaS_new(L, name);
+ setsvalue2s(L, L->top, tname); /* protect name */
+ incr_top(L);
+ lexstate.buff = buff;
+ luaX_setinput(L, &lexstate, z, tname);
+ open_func(&lexstate, &funcstate);
+ funcstate.f->is_vararg = VARARG_ISVARARG; /* main func. is always vararg */
+ luaX_next(&lexstate); /* read first token */
+ chunk(&lexstate);
+ check(&lexstate, TK_EOS);
+ close_func(&lexstate);
+ L->top--; /* remove 'name' from stack */
+ lua_assert(funcstate.prev == NULL);
+ lua_assert(funcstate.f->nups == 0);
+ lua_assert(lexstate.fs == NULL);
+ return funcstate.f;
+}
+
+
+
+/*============================================================*/
+/* GRAMMAR RULES */
+/*============================================================*/
+
+
+static void ICACHE_FLASH_ATTR field (LexState *ls, expdesc *v) {
+ /* field -> ['.' | ':'] NAME */
+ FuncState *fs = ls->fs;
+ expdesc key;
+ luaK_exp2anyreg(fs, v);
+ luaX_next(ls); /* skip the dot or colon */
+ checkname(ls, &key);
+ luaK_indexed(fs, v, &key);
+}
+
+
+static void ICACHE_FLASH_ATTR yindex (LexState *ls, expdesc *v) {
+ /* index -> '[' expr ']' */
+ luaX_next(ls); /* skip the '[' */
+ expr(ls, v);
+ luaK_exp2val(ls->fs, v);
+ checknext(ls, ']');
+}
+
+
+/*
+** {======================================================================
+** Rules for Constructors
+** =======================================================================
+*/
+
+
+struct ConsControl {
+ expdesc v; /* last list item read */
+ expdesc *t; /* table descriptor */
+ int nh; /* total number of `record' elements */
+ int na; /* total number of array elements */
+ int tostore; /* number of array elements pending to be stored */
+};
+
+
+static void ICACHE_FLASH_ATTR recfield (LexState *ls, struct ConsControl *cc) {
+ /* recfield -> (NAME | `['exp1`]') = exp1 */
+ FuncState *fs = ls->fs;
+ int reg = ls->fs->freereg;
+ expdesc key, val;
+ int rkkey;
+ if (ls->t.token == TK_NAME) {
+ luaY_checklimit(fs, cc->nh, MAX_INT, "items in a constructor");
+ checkname(ls, &key);
+ }
+ else /* ls->t.token == '[' */
+ yindex(ls, &key);
+ cc->nh++;
+ checknext(ls, '=');
+ rkkey = luaK_exp2RK(fs, &key);
+ expr(ls, &val);
+ luaK_codeABC(fs, OP_SETTABLE, cc->t->u.s.info, rkkey, luaK_exp2RK(fs, &val));
+ fs->freereg = reg; /* free registers */
+}
+
+
+static void ICACHE_FLASH_ATTR closelistfield (FuncState *fs, struct ConsControl *cc) {
+ if (cc->v.k == VVOID) return; /* there is no list item */
+ luaK_exp2nextreg(fs, &cc->v);
+ cc->v.k = VVOID;
+ if (cc->tostore == LFIELDS_PER_FLUSH) {
+ luaK_setlist(fs, cc->t->u.s.info, cc->na, cc->tostore); /* flush */
+ cc->tostore = 0; /* no more items pending */
+ }
+}
+
+
+static void ICACHE_FLASH_ATTR lastlistfield (FuncState *fs, struct ConsControl *cc) {
+ if (cc->tostore == 0) return;
+ if (hasmultret(cc->v.k)) {
+ luaK_setmultret(fs, &cc->v);
+ luaK_setlist(fs, cc->t->u.s.info, cc->na, LUA_MULTRET);
+ cc->na--; /* do not count last expression (unknown number of elements) */
+ }
+ else {
+ if (cc->v.k != VVOID)
+ luaK_exp2nextreg(fs, &cc->v);
+ luaK_setlist(fs, cc->t->u.s.info, cc->na, cc->tostore);
+ }
+}
+
+
+static void ICACHE_FLASH_ATTR listfield (LexState *ls, struct ConsControl *cc) {
+ expr(ls, &cc->v);
+ luaY_checklimit(ls->fs, cc->na, MAX_INT, "items in a constructor");
+ cc->na++;
+ cc->tostore++;
+}
+
+
+static void ICACHE_FLASH_ATTR constructor (LexState *ls, expdesc *t) {
+ /* constructor -> ?? */
+ FuncState *fs = ls->fs;
+ int line = ls->linenumber;
+ int pc = luaK_codeABC(fs, OP_NEWTABLE, 0, 0, 0);
+ struct ConsControl cc;
+ cc.na = cc.nh = cc.tostore = 0;
+ cc.t = t;
+ init_exp(t, VRELOCABLE, pc);
+ init_exp(&cc.v, VVOID, 0); /* no value (yet) */
+ luaK_exp2nextreg(ls->fs, t); /* fix it at stack top (for gc) */
+ checknext(ls, '{');
+ do {
+ lua_assert(cc.v.k == VVOID || cc.tostore > 0);
+ if (ls->t.token == '}') break;
+ closelistfield(fs, &cc);
+ switch(ls->t.token) {
+ case TK_NAME: { /* may be listfields or recfields */
+ luaX_lookahead(ls);
+ if (ls->lookahead.token != '=') /* expression? */
+ listfield(ls, &cc);
+ else
+ recfield(ls, &cc);
+ break;
+ }
+ case '[': { /* constructor_item -> recfield */
+ recfield(ls, &cc);
+ break;
+ }
+ default: { /* constructor_part -> listfield */
+ listfield(ls, &cc);
+ break;
+ }
+ }
+ } while (testnext(ls, ',') || testnext(ls, ';'));
+ check_match(ls, '}', '{', line);
+ lastlistfield(fs, &cc);
+ SETARG_B(fs->f->code[pc], luaO_int2fb(cc.na)); /* set initial array size */
+ SETARG_C(fs->f->code[pc], luaO_int2fb(cc.nh)); /* set initial table size */
+}
+
+/* }====================================================================== */
+
+
+
+static void ICACHE_FLASH_ATTR parlist (LexState *ls) {
+ /* parlist -> [ param { `,' param } ] */
+ FuncState *fs = ls->fs;
+ Proto *f = fs->f;
+ int nparams = 0;
+ f->is_vararg = 0;
+ if (ls->t.token != ')') { /* is `parlist' not empty? */
+ do {
+ switch (ls->t.token) {
+ case TK_NAME: { /* param -> NAME */
+ new_localvar(ls, str_checkname(ls), nparams++);
+ break;
+ }
+ case TK_DOTS: { /* param -> `...' */
+ luaX_next(ls);
+#if defined(LUA_COMPAT_VARARG)
+ /* use `arg' as default name */
+ new_localvarliteral(ls, "arg", nparams++);
+ f->is_vararg = VARARG_HASARG | VARARG_NEEDSARG;
+#endif
+ f->is_vararg |= VARARG_ISVARARG;
+ break;
+ }
+ default: luaX_syntaxerror(ls, " or " LUA_QL("...") " expected");
+ }
+ } while (!f->is_vararg && testnext(ls, ','));
+ }
+ adjustlocalvars(ls, nparams);
+ f->numparams = cast_byte(fs->nactvar - (f->is_vararg & VARARG_HASARG));
+ luaK_reserveregs(fs, fs->nactvar); /* reserve register for parameters */
+}
+
+
+static void ICACHE_FLASH_ATTR body (LexState *ls, expdesc *e, int needself, int line) {
+ /* body -> `(' parlist `)' chunk END */
+ FuncState new_fs;
+ open_func(ls, &new_fs);
+ new_fs.f->linedefined = line;
+ checknext(ls, '(');
+ if (needself) {
+ new_localvarliteral(ls, "self", 0);
+ adjustlocalvars(ls, 1);
+ }
+ parlist(ls);
+ checknext(ls, ')');
+ chunk(ls);
+ new_fs.f->lastlinedefined = ls->linenumber;
+ check_match(ls, TK_END, TK_FUNCTION, line);
+ close_func(ls);
+ pushclosure(ls, &new_fs, e);
+}
+
+
+static int ICACHE_FLASH_ATTR explist1 (LexState *ls, expdesc *v) {
+ /* explist1 -> expr { `,' expr } */
+ int n = 1; /* at least one expression */
+ expr(ls, v);
+ while (testnext(ls, ',')) {
+ luaK_exp2nextreg(ls->fs, v);
+ expr(ls, v);
+ n++;
+ }
+ return n;
+}
+
+
+static void ICACHE_FLASH_ATTR funcargs (LexState *ls, expdesc *f) {
+ FuncState *fs = ls->fs;
+ expdesc args;
+ int base, nparams;
+ int line = ls->linenumber;
+ switch (ls->t.token) {
+ case '(': { /* funcargs -> `(' [ explist1 ] `)' */
+ if (line != ls->lastline)
+ luaX_syntaxerror(ls,"ambiguous syntax (function call x new statement)");
+ luaX_next(ls);
+ if (ls->t.token == ')') /* arg list is empty? */
+ args.k = VVOID;
+ else {
+ explist1(ls, &args);
+ luaK_setmultret(fs, &args);
+ }
+ check_match(ls, ')', '(', line);
+ break;
+ }
+ case '{': { /* funcargs -> constructor */
+ constructor(ls, &args);
+ break;
+ }
+ case TK_STRING: { /* funcargs -> STRING */
+ codestring(ls, &args, ls->t.seminfo.ts);
+ luaX_next(ls); /* must use `seminfo' before `next' */
+ break;
+ }
+ default: {
+ luaX_syntaxerror(ls, "function arguments expected");
+ return;
+ }
+ }
+ lua_assert(f->k == VNONRELOC);
+ base = f->u.s.info; /* base register for call */
+ if (hasmultret(args.k))
+ nparams = LUA_MULTRET; /* open call */
+ else {
+ if (args.k != VVOID)
+ luaK_exp2nextreg(fs, &args); /* close last argument */
+ nparams = fs->freereg - (base+1);
+ }
+ init_exp(f, VCALL, luaK_codeABC(fs, OP_CALL, base, nparams+1, 2));
+ luaK_fixline(fs, line);
+ fs->freereg = base+1; /* call remove function and arguments and leaves
+ (unless changed) one result */
+}
+
+
+
+
+/*
+** {======================================================================
+** Expression parsing
+** =======================================================================
+*/
+
+
+static void ICACHE_FLASH_ATTR prefixexp (LexState *ls, expdesc *v) {
+ /* prefixexp -> NAME | '(' expr ')' */
+ switch (ls->t.token) {
+ case '(': {
+ int line = ls->linenumber;
+ luaX_next(ls);
+ expr(ls, v);
+ check_match(ls, ')', '(', line);
+ luaK_dischargevars(ls->fs, v);
+ return;
+ }
+ case TK_NAME: {
+ singlevar(ls, v);
+ return;
+ }
+ default: {
+ luaX_syntaxerror(ls, "unexpected symbol");
+ return;
+ }
+ }
+}
+
+
+static void ICACHE_FLASH_ATTR primaryexp (LexState *ls, expdesc *v) {
+ /* primaryexp ->
+ prefixexp { `.' NAME | `[' exp `]' | `:' NAME funcargs | funcargs } */
+ FuncState *fs = ls->fs;
+ prefixexp(ls, v);
+ for (;;) {
+ switch (ls->t.token) {
+ case '.': { /* field */
+ field(ls, v);
+ break;
+ }
+ case '[': { /* `[' exp1 `]' */
+ expdesc key;
+ luaK_exp2anyreg(fs, v);
+ yindex(ls, &key);
+ luaK_indexed(fs, v, &key);
+ break;
+ }
+ case ':': { /* `:' NAME funcargs */
+ expdesc key;
+ luaX_next(ls);
+ checkname(ls, &key);
+ luaK_self(fs, v, &key);
+ funcargs(ls, v);
+ break;
+ }
+ case '(': case TK_STRING: case '{': { /* funcargs */
+ luaK_exp2nextreg(fs, v);
+ funcargs(ls, v);
+ break;
+ }
+ default: return;
+ }
+ }
+}
+
+
+static void ICACHE_FLASH_ATTR simpleexp (LexState *ls, expdesc *v) {
+ /* simpleexp -> NUMBER | STRING | NIL | true | false | ... |
+ constructor | FUNCTION body | primaryexp */
+ switch (ls->t.token) {
+ case TK_NUMBER: {
+ init_exp(v, VKNUM, 0);
+ v->u.nval = ls->t.seminfo.r;
+ break;
+ }
+ case TK_STRING: {
+ codestring(ls, v, ls->t.seminfo.ts);
+ break;
+ }
+ case TK_NIL: {
+ init_exp(v, VNIL, 0);
+ break;
+ }
+ case TK_TRUE: {
+ init_exp(v, VTRUE, 0);
+ break;
+ }
+ case TK_FALSE: {
+ init_exp(v, VFALSE, 0);
+ break;
+ }
+ case TK_DOTS: { /* vararg */
+ FuncState *fs = ls->fs;
+ check_condition(ls, fs->f->is_vararg,
+ "cannot use " LUA_QL("...") " outside a vararg function");
+ fs->f->is_vararg &= ~VARARG_NEEDSARG; /* don't need 'arg' */
+ init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, 1, 0));
+ break;
+ }
+ case '{': { /* constructor */
+ constructor(ls, v);
+ return;
+ }
+ case TK_FUNCTION: {
+ luaX_next(ls);
+ body(ls, v, 0, ls->linenumber);
+ return;
+ }
+ default: {
+ primaryexp(ls, v);
+ return;
+ }
+ }
+ luaX_next(ls);
+}
+
+
+static UnOpr ICACHE_FLASH_ATTR getunopr (int op) {
+ switch (op) {
+ case TK_NOT: return OPR_NOT;
+ case '-': return OPR_MINUS;
+ case '#': return OPR_LEN;
+ default: return OPR_NOUNOPR;
+ }
+}
+
+
+static BinOpr ICACHE_FLASH_ATTR getbinopr (int op) {
+ switch (op) {
+ case '+': return OPR_ADD;
+ case '-': return OPR_SUB;
+ case '*': return OPR_MUL;
+ case '/': return OPR_DIV;
+ case '%': return OPR_MOD;
+ case '^': return OPR_POW;
+ case TK_CONCAT: return OPR_CONCAT;
+ case TK_NE: return OPR_NE;
+ case TK_EQ: return OPR_EQ;
+ case '<': return OPR_LT;
+ case TK_LE: return OPR_LE;
+ case '>': return OPR_GT;
+ case TK_GE: return OPR_GE;
+ case TK_AND: return OPR_AND;
+ case TK_OR: return OPR_OR;
+ default: return OPR_NOBINOPR;
+ }
+}
+
+
+static const struct {
+ lu_byte left; /* left priority for each binary operator */
+ lu_byte right; /* right priority */
+} priority[] = { /* ORDER OPR */
+ {6, 6}, {6, 6}, {7, 7}, {7, 7}, {7, 7}, /* `+' `-' `/' `%' */
+ {10, 9}, {5, 4}, /* power and concat (right associative) */
+ {3, 3}, {3, 3}, /* equality and inequality */
+ {3, 3}, {3, 3}, {3, 3}, {3, 3}, /* order */
+ {2, 2}, {1, 1} /* logical (and/or) */
+};
+
+#define UNARY_PRIORITY 8 /* priority for unary operators */
+
+
+/*
+** subexpr -> (simpleexp | unop subexpr) { binop subexpr }
+** where `binop' is any binary operator with a priority higher than `limit'
+*/
+static BinOpr ICACHE_FLASH_ATTR subexpr (LexState *ls, expdesc *v, unsigned int limit) {
+ BinOpr op;
+ UnOpr uop;
+ enterlevel(ls);
+ uop = getunopr(ls->t.token);
+ if (uop != OPR_NOUNOPR) {
+ luaX_next(ls);
+ subexpr(ls, v, UNARY_PRIORITY);
+ luaK_prefix(ls->fs, uop, v);
+ }
+ else simpleexp(ls, v);
+ /* expand while operators have priorities higher than `limit' */
+ op = getbinopr(ls->t.token);
+ while (op != OPR_NOBINOPR && priority[op].left > limit) {
+ expdesc v2;
+ BinOpr nextop;
+ luaX_next(ls);
+ luaK_infix(ls->fs, op, v);
+ /* read sub-expression with higher priority */
+ nextop = subexpr(ls, &v2, priority[op].right);
+ luaK_posfix(ls->fs, op, v, &v2);
+ op = nextop;
+ }
+ leavelevel(ls);
+ return op; /* return first untreated operator */
+}
+
+
+static void ICACHE_FLASH_ATTR expr (LexState *ls, expdesc *v) {
+ subexpr(ls, v, 0);
+}
+
+/* }==================================================================== */
+
+
+
+/*
+** {======================================================================
+** Rules for Statements
+** =======================================================================
+*/
+
+
+static int ICACHE_FLASH_ATTR block_follow (int token) {
+ switch (token) {
+ case TK_ELSE: case TK_ELSEIF: case TK_END:
+ case TK_UNTIL: case TK_EOS:
+ return 1;
+ default: return 0;
+ }
+}
+
+
+static void ICACHE_FLASH_ATTR block (LexState *ls) {
+ /* block -> chunk */
+ FuncState *fs = ls->fs;
+ BlockCnt *pbl = (BlockCnt*)luaM_malloc(ls->L,sizeof(BlockCnt));
+ enterblock(fs, pbl, 0);
+ chunk(ls);
+ lua_assert(pbl->breaklist == NO_JUMP);
+ leaveblock(fs);
+ luaM_free(ls->L,pbl);
+}
+
+
+/*
+** structure to chain all variables in the left-hand side of an
+** assignment
+*/
+struct LHS_assign {
+ struct LHS_assign *prev;
+ expdesc v; /* variable (global, local, upvalue, or indexed) */
+};
+
+
+/*
+** check whether, in an assignment to a local variable, the local variable
+** is needed in a previous assignment (to a table). If so, save original
+** local value in a safe place and use this safe copy in the previous
+** assignment.
+*/
+static void ICACHE_FLASH_ATTR check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) {
+ FuncState *fs = ls->fs;
+ int extra = fs->freereg; /* eventual position to save local variable */
+ int conflict = 0;
+ for (; lh; lh = lh->prev) {
+ if (lh->v.k == VINDEXED) {
+ if (lh->v.u.s.info == v->u.s.info) { /* conflict? */
+ conflict = 1;
+ lh->v.u.s.info = extra; /* previous assignment will use safe copy */
+ }
+ if (lh->v.u.s.aux == v->u.s.info) { /* conflict? */
+ conflict = 1;
+ lh->v.u.s.aux = extra; /* previous assignment will use safe copy */
+ }
+ }
+ }
+ if (conflict) {
+ luaK_codeABC(fs, OP_MOVE, fs->freereg, v->u.s.info, 0); /* make copy */
+ luaK_reserveregs(fs, 1);
+ }
+}
+
+
+static void ICACHE_FLASH_ATTR assignment (LexState *ls, struct LHS_assign *lh, int nvars) {
+ expdesc e;
+ check_condition(ls, VLOCAL <= lh->v.k && lh->v.k <= VINDEXED,
+ "syntax error");
+ if (testnext(ls, ',')) { /* assignment -> `,' primaryexp assignment */
+ struct LHS_assign nv;
+ nv.prev = lh;
+ primaryexp(ls, &nv.v);
+ if (nv.v.k == VLOCAL)
+ check_conflict(ls, lh, &nv.v);
+ luaY_checklimit(ls->fs, nvars, LUAI_MAXCCALLS - ls->L->nCcalls,
+ "variables in assignment");
+ assignment(ls, &nv, nvars+1);
+ }
+ else { /* assignment -> `=' explist1 */
+ int nexps;
+ checknext(ls, '=');
+ nexps = explist1(ls, &e);
+ if (nexps != nvars) {
+ adjust_assign(ls, nvars, nexps, &e);
+ if (nexps > nvars)
+ ls->fs->freereg -= nexps - nvars; /* remove extra values */
+ }
+ else {
+ luaK_setoneret(ls->fs, &e); /* close last expression */
+ luaK_storevar(ls->fs, &lh->v, &e);
+ return; /* avoid default */
+ }
+ }
+ init_exp(&e, VNONRELOC, ls->fs->freereg-1); /* default assignment */
+ luaK_storevar(ls->fs, &lh->v, &e);
+}
+
+
+static int ICACHE_FLASH_ATTR cond (LexState *ls) {
+ /* cond -> exp */
+ expdesc v;
+ expr(ls, &v); /* read condition */
+ if (v.k == VNIL) v.k = VFALSE; /* `falses' are all equal here */
+ luaK_goiftrue(ls->fs, &v);
+ return v.f;
+}
+
+
+static void ICACHE_FLASH_ATTR breakstat (LexState *ls) {
+ FuncState *fs = ls->fs;
+ BlockCnt *bl = fs->bl;
+ int upval = 0;
+ while (bl && !bl->isbreakable) {
+ upval |= bl->upval;
+ bl = bl->previous;
+ }
+ if (!bl)
+ luaX_syntaxerror(ls, "no loop to break");
+ if (upval)
+ luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0);
+ luaK_concat(fs, &bl->breaklist, luaK_jump(fs));
+}
+
+
+static void ICACHE_FLASH_ATTR whilestat (LexState *ls, int line) {
+ /* whilestat -> WHILE cond DO block END */
+ FuncState *fs = ls->fs;
+ int whileinit;
+ int condexit;
+ BlockCnt bl;
+ luaX_next(ls); /* skip WHILE */
+ whileinit = luaK_getlabel(fs);
+ condexit = cond(ls);
+ enterblock(fs, &bl, 1);
+ checknext(ls, TK_DO);
+ block(ls);
+ luaK_patchlist(fs, luaK_jump(fs), whileinit);
+ check_match(ls, TK_END, TK_WHILE, line);
+ leaveblock(fs);
+ luaK_patchtohere(fs, condexit); /* false conditions finish the loop */
+}
+
+
+static void ICACHE_FLASH_ATTR repeatstat (LexState *ls, int line) {
+ /* repeatstat -> REPEAT block UNTIL cond */
+ int condexit;
+ FuncState *fs = ls->fs;
+ int repeat_init = luaK_getlabel(fs);
+ BlockCnt bl1, bl2;
+ enterblock(fs, &bl1, 1); /* loop block */
+ enterblock(fs, &bl2, 0); /* scope block */
+ luaX_next(ls); /* skip REPEAT */
+ chunk(ls);
+ check_match(ls, TK_UNTIL, TK_REPEAT, line);
+ condexit = cond(ls); /* read condition (inside scope block) */
+ if (!bl2.upval) { /* no upvalues? */
+ leaveblock(fs); /* finish scope */
+ luaK_patchlist(ls->fs, condexit, repeat_init); /* close the loop */
+ }
+ else { /* complete semantics when there are upvalues */
+ breakstat(ls); /* if condition then break */
+ luaK_patchtohere(ls->fs, condexit); /* else... */
+ leaveblock(fs); /* finish scope... */
+ luaK_patchlist(ls->fs, luaK_jump(fs), repeat_init); /* and repeat */
+ }
+ leaveblock(fs); /* finish loop */
+}
+
+
+static int ICACHE_FLASH_ATTR exp1 (LexState *ls) {
+ expdesc e;
+ int k;
+ expr(ls, &e);
+ k = e.k;
+ luaK_exp2nextreg(ls->fs, &e);
+ return k;
+}
+
+
+static void ICACHE_FLASH_ATTR forbody (LexState *ls, int base, int line, int nvars, int isnum) {
+ /* forbody -> DO block */
+ BlockCnt *pbl = (BlockCnt*)luaM_malloc(ls->L,sizeof(BlockCnt));
+ FuncState *fs = ls->fs;
+ int prep, endfor;
+ adjustlocalvars(ls, 3); /* control variables */
+ checknext(ls, TK_DO);
+ prep = isnum ? luaK_codeAsBx(fs, OP_FORPREP, base, NO_JUMP) : luaK_jump(fs);
+ enterblock(fs, pbl, 0); /* scope for declared variables */
+ adjustlocalvars(ls, nvars);
+ luaK_reserveregs(fs, nvars);
+ block(ls);
+ leaveblock(fs); /* end of scope for declared variables */
+ luaK_patchtohere(fs, prep);
+ endfor = (isnum) ? luaK_codeAsBx(fs, OP_FORLOOP, base, NO_JUMP) :
+ luaK_codeABC(fs, OP_TFORLOOP, base, 0, nvars);
+ luaK_fixline(fs, line); /* pretend that `OP_FOR' starts the loop */
+ luaK_patchlist(fs, (isnum ? endfor : luaK_jump(fs)), prep + 1);
+ luaM_free(ls->L,pbl);
+}
+
+
+static void ICACHE_FLASH_ATTR fornum (LexState *ls, TString *varname, int line) {
+ /* fornum -> NAME = exp1,exp1[,exp1] forbody */
+ FuncState *fs = ls->fs;
+ int base = fs->freereg;
+ new_localvarliteral(ls, "(for index)", 0);
+ new_localvarliteral(ls, "(for limit)", 1);
+ new_localvarliteral(ls, "(for step)", 2);
+ new_localvar(ls, varname, 3);
+ checknext(ls, '=');
+ exp1(ls); /* initial value */
+ checknext(ls, ',');
+ exp1(ls); /* limit */
+ if (testnext(ls, ','))
+ exp1(ls); /* optional step */
+ else { /* default step = 1 */
+ luaK_codeABx(fs, OP_LOADK, fs->freereg, luaK_numberK(fs, 1));
+ luaK_reserveregs(fs, 1);
+ }
+ forbody(ls, base, line, 1, 1);
+}
+
+
+static void ICACHE_FLASH_ATTR forlist (LexState *ls, TString *indexname) {
+ /* forlist -> NAME {,NAME} IN explist1 forbody */
+ FuncState *fs = ls->fs;
+ expdesc e;
+ int nvars = 0;
+ int line;
+ int base = fs->freereg;
+ /* create control variables */
+ new_localvarliteral(ls, "(for generator)", nvars++);
+ new_localvarliteral(ls, "(for state)", nvars++);
+ new_localvarliteral(ls, "(for control)", nvars++);
+ /* create declared variables */
+ new_localvar(ls, indexname, nvars++);
+ while (testnext(ls, ','))
+ new_localvar(ls, str_checkname(ls), nvars++);
+ checknext(ls, TK_IN);
+ line = ls->linenumber;
+ adjust_assign(ls, 3, explist1(ls, &e), &e);
+ luaK_checkstack(fs, 3); /* extra space to call generator */
+ forbody(ls, base, line, nvars - 3, 0);
+}
+
+
+static void ICACHE_FLASH_ATTR forstat (LexState *ls, int line) {
+ /* forstat -> FOR (fornum | forlist) END */
+ FuncState *fs = ls->fs;
+ TString *varname;
+ BlockCnt bl;
+ enterblock(fs, &bl, 1); /* scope for loop and control variables */
+ luaX_next(ls); /* skip `for' */
+ varname = str_checkname(ls); /* first variable name */
+ switch (ls->t.token) {
+ case '=': fornum(ls, varname, line); break;
+ case ',': case TK_IN: forlist(ls, varname); break;
+ default: luaX_syntaxerror(ls, LUA_QL("=") " or " LUA_QL("in") " expected");
+ }
+ check_match(ls, TK_END, TK_FOR, line);
+ leaveblock(fs); /* loop scope (`break' jumps to this point) */
+}
+
+
+static int ICACHE_FLASH_ATTR test_then_block (LexState *ls) {
+ /* test_then_block -> [IF | ELSEIF] cond THEN block */
+ int condexit;
+ luaX_next(ls); /* skip IF or ELSEIF */
+ condexit = cond(ls);
+ checknext(ls, TK_THEN);
+ block(ls); /* `then' part */
+ return condexit;
+}
+
+
+static void ICACHE_FLASH_ATTR ifstat (LexState *ls, int line) {
+ /* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] END */
+ FuncState *fs = ls->fs;
+ int flist;
+ int escapelist = NO_JUMP;
+ flist = test_then_block(ls); /* IF cond THEN block */
+ while (ls->t.token == TK_ELSEIF) {
+ luaK_concat(fs, &escapelist, luaK_jump(fs));
+ luaK_patchtohere(fs, flist);
+ flist = test_then_block(ls); /* ELSEIF cond THEN block */
+ }
+ if (ls->t.token == TK_ELSE) {
+ luaK_concat(fs, &escapelist, luaK_jump(fs));
+ luaK_patchtohere(fs, flist);
+ luaX_next(ls); /* skip ELSE (after patch, for correct line info) */
+ block(ls); /* `else' part */
+ }
+ else
+ luaK_concat(fs, &escapelist, flist);
+ luaK_patchtohere(fs, escapelist);
+ check_match(ls, TK_END, TK_IF, line);
+}
+
+
+static void ICACHE_FLASH_ATTR localfunc (LexState *ls) {
+ expdesc v, b;
+ FuncState *fs = ls->fs;
+ new_localvar(ls, str_checkname(ls), 0);
+ init_exp(&v, VLOCAL, fs->freereg);
+ luaK_reserveregs(fs, 1);
+ adjustlocalvars(ls, 1);
+ body(ls, &b, 0, ls->linenumber);
+ luaK_storevar(fs, &v, &b);
+ /* debug information will only see the variable after this point! */
+ getlocvar(fs, fs->nactvar - 1).startpc = fs->pc;
+}
+
+
+static void ICACHE_FLASH_ATTR localstat (LexState *ls) {
+ /* stat -> LOCAL NAME {`,' NAME} [`=' explist1] */
+ int nvars = 0;
+ int nexps;
+ expdesc e;
+ do {
+ new_localvar(ls, str_checkname(ls), nvars++);
+ } while (testnext(ls, ','));
+ if (testnext(ls, '='))
+ nexps = explist1(ls, &e);
+ else {
+ e.k = VVOID;
+ nexps = 0;
+ }
+ adjust_assign(ls, nvars, nexps, &e);
+ adjustlocalvars(ls, nvars);
+}
+
+
+static int ICACHE_FLASH_ATTR funcname (LexState *ls, expdesc *v) {
+ /* funcname -> NAME {field} [`:' NAME] */
+ int needself = 0;
+ singlevar(ls, v);
+ while (ls->t.token == '.')
+ field(ls, v);
+ if (ls->t.token == ':') {
+ needself = 1;
+ field(ls, v);
+ }
+ return needself;
+}
+
+
+static void ICACHE_FLASH_ATTR funcstat (LexState *ls, int line) {
+ /* funcstat -> FUNCTION funcname body */
+ int needself;
+ expdesc v, b;
+ luaX_next(ls); /* skip FUNCTION */
+ needself = funcname(ls, &v);
+ body(ls, &b, needself, line);
+ luaK_storevar(ls->fs, &v, &b);
+ luaK_fixline(ls->fs, line); /* definition `happens' in the first line */
+}
+
+
+static void ICACHE_FLASH_ATTR exprstat (LexState *ls) {
+ /* stat -> func | assignment */
+ FuncState *fs = ls->fs;
+ struct LHS_assign v;
+ primaryexp(ls, &v.v);
+ if (v.v.k == VCALL) /* stat -> func */
+ SETARG_C(getcode(fs, &v.v), 1); /* call statement uses no results */
+ else { /* stat -> assignment */
+ v.prev = NULL;
+ assignment(ls, &v, 1);
+ }
+}
+
+
+static void ICACHE_FLASH_ATTR retstat (LexState *ls) {
+ /* stat -> RETURN explist */
+ FuncState *fs = ls->fs;
+ expdesc e;
+ int first, nret; /* registers with returned values */
+ luaX_next(ls); /* skip RETURN */
+ if (block_follow(ls->t.token) || ls->t.token == ';')
+ first = nret = 0; /* return no values */
+ else {
+ nret = explist1(ls, &e); /* optional return values */
+ if (hasmultret(e.k)) {
+ luaK_setmultret(fs, &e);
+ if (e.k == VCALL && nret == 1) { /* tail call? */
+ SET_OPCODE(getcode(fs,&e), OP_TAILCALL);
+ lua_assert(GETARG_A(getcode(fs,&e)) == fs->nactvar);
+ }
+ first = fs->nactvar;
+ nret = LUA_MULTRET; /* return all values */
+ }
+ else {
+ if (nret == 1) /* only one single value? */
+ first = luaK_exp2anyreg(fs, &e);
+ else {
+ luaK_exp2nextreg(fs, &e); /* values must go to the `stack' */
+ first = fs->nactvar; /* return all `active' values */
+ lua_assert(nret == fs->freereg - first);
+ }
+ }
+ }
+ luaK_ret(fs, first, nret);
+}
+
+
+static int ICACHE_FLASH_ATTR statement (LexState *ls) {
+ int line = ls->linenumber; /* may be needed for error messages */
+ switch (ls->t.token) {
+ case TK_IF: { /* stat -> ifstat */
+ ifstat(ls, line);
+ return 0;
+ }
+ case TK_WHILE: { /* stat -> whilestat */
+ whilestat(ls, line);
+ return 0;
+ }
+ case TK_DO: { /* stat -> DO block END */
+ luaX_next(ls); /* skip DO */
+ block(ls);
+ check_match(ls, TK_END, TK_DO, line);
+ return 0;
+ }
+ case TK_FOR: { /* stat -> forstat */
+ forstat(ls, line);
+ return 0;
+ }
+ case TK_REPEAT: { /* stat -> repeatstat */
+ repeatstat(ls, line);
+ return 0;
+ }
+ case TK_FUNCTION: {
+ funcstat(ls, line); /* stat -> funcstat */
+ return 0;
+ }
+ case TK_LOCAL: { /* stat -> localstat */
+ luaX_next(ls); /* skip LOCAL */
+ if (testnext(ls, TK_FUNCTION)) /* local function? */
+ localfunc(ls);
+ else
+ localstat(ls);
+ return 0;
+ }
+ case TK_RETURN: { /* stat -> retstat */
+ retstat(ls);
+ return 1; /* must be last statement */
+ }
+ case TK_BREAK: { /* stat -> breakstat */
+ luaX_next(ls); /* skip BREAK */
+ breakstat(ls);
+ return 1; /* must be last statement */
+ }
+ default: {
+ exprstat(ls);
+ return 0; /* to avoid warnings */
+ }
+ }
+}
+
+
+static void ICACHE_FLASH_ATTR chunk (LexState *ls) {
+ /* chunk -> { stat [`;'] } */
+ int islast = 0;
+ enterlevel(ls);
+ while (!islast && !block_follow(ls->t.token)) {
+ islast = statement(ls);
+ testnext(ls, ';');
+ lua_assert(ls->fs->f->maxstacksize >= ls->fs->freereg &&
+ ls->fs->freereg >= ls->fs->nactvar);
+ ls->fs->freereg = ls->fs->nactvar; /* free registers */
+ }
+ leavelevel(ls);
+}
+
+/* }====================================================================== */
diff --git a/app/lua/lparser.h b/app/lua/lparser.h
new file mode 100644
index 00000000..18836afd
--- /dev/null
+++ b/app/lua/lparser.h
@@ -0,0 +1,82 @@
+/*
+** $Id: lparser.h,v 1.57.1.1 2007/12/27 13:02:25 roberto Exp $
+** Lua Parser
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lparser_h
+#define lparser_h
+
+#include "llimits.h"
+#include "lobject.h"
+#include "lzio.h"
+
+
+/*
+** Expression descriptor
+*/
+
+typedef enum {
+ VVOID, /* no value */
+ VNIL,
+ VTRUE,
+ VFALSE,
+ VK, /* info = index of constant in `k' */
+ VKNUM, /* nval = numerical value */
+ VLOCAL, /* info = local register */
+ VUPVAL, /* info = index of upvalue in `upvalues' */
+ VGLOBAL, /* info = index of table; aux = index of global name in `k' */
+ VINDEXED, /* info = table register; aux = index register (or `k') */
+ VJMP, /* info = instruction pc */
+ VRELOCABLE, /* info = instruction pc */
+ VNONRELOC, /* info = result register */
+ VCALL, /* info = instruction pc */
+ VVARARG /* info = instruction pc */
+} expkind;
+
+typedef struct expdesc {
+ expkind k;
+ union {
+ struct { int info, aux; } s;
+ lua_Number nval;
+ } u;
+ int t; /* patch list of `exit when true' */
+ int f; /* patch list of `exit when false' */
+} expdesc;
+
+
+typedef struct upvaldesc {
+ lu_byte k;
+ lu_byte info;
+} upvaldesc;
+
+
+struct BlockCnt; /* defined in lparser.c */
+
+
+/* state needed to generate code for a given function */
+typedef struct FuncState {
+ Proto *f; /* current function header */
+ Table *h; /* table to find (and reuse) elements in `k' */
+ struct FuncState *prev; /* enclosing function */
+ struct LexState *ls; /* lexical state */
+ struct lua_State *L; /* copy of the Lua state */
+ struct BlockCnt *bl; /* chain of current blocks */
+ int pc; /* next position to code (equivalent to `ncode') */
+ int lasttarget; /* `pc' of last `jump target' */
+ int jpc; /* list of pending jumps to `pc' */
+ int freereg; /* first free register */
+ int nk; /* number of elements in `k' */
+ int np; /* number of elements in `p' */
+ short nlocvars; /* number of elements in `locvars' */
+ lu_byte nactvar; /* number of active local variables */
+ upvaldesc upvalues[LUAI_MAXUPVALUES]; /* upvalues */
+ unsigned short actvar[LUAI_MAXVARS]; /* declared-variable stack */
+} FuncState;
+
+
+LUAI_FUNC Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
+ const char *name);
+
+
+#endif
diff --git a/app/lua/lrodefs.h b/app/lua/lrodefs.h
new file mode 100644
index 00000000..02b635ef
--- /dev/null
+++ b/app/lua/lrodefs.h
@@ -0,0 +1,41 @@
+/* Read-only tables helper */
+
+#ifndef lrodefs_h
+#define lrodefs_h
+
+#include "lrotable.h"
+
+#undef LUA_REG_TYPE
+#undef LSTRKEY
+#undef LNILKEY
+#undef LNUMKEY
+#undef LFUNCVAL
+#undef LNUMVAL
+#undef LROVAL
+#undef LNILVAL
+#undef LREGISTER
+
+#if (MIN_OPT_LEVEL > 0) && (LUA_OPTIMIZE_MEMORY >= MIN_OPT_LEVEL)
+#define LUA_REG_TYPE luaR_entry
+#define LSTRKEY LRO_STRKEY
+#define LNUMKEY LRO_NUMKEY
+#define LNILKEY LRO_NILKEY
+#define LFUNCVAL LRO_FUNCVAL
+#define LNUMVAL LRO_NUMVAL
+#define LROVAL LRO_ROVAL
+#define LNILVAL LRO_NILVAL
+#define LREGISTER(L, name, table)\
+ return 0
+#else
+#define LUA_REG_TYPE luaL_reg
+#define LSTRKEY(x) x
+#define LNILKEY NULL
+#define LFUNCVAL(x) x
+#define LNILVAL NULL
+#define LREGISTER(L, name, table)\
+ luaL_register(L, name, table);\
+ return 1
+#endif
+
+#endif /* lrodefs_h */
+
diff --git a/app/lua/lrotable.c b/app/lua/lrotable.c
new file mode 100644
index 00000000..60cf6788
--- /dev/null
+++ b/app/lua/lrotable.c
@@ -0,0 +1,135 @@
+/* Read-only tables for Lua */
+
+#include "c_string.h"
+#include "lrotable.h"
+#include "lua.h"
+#include "lauxlib.h"
+#include "lstring.h"
+#include "lobject.h"
+#include "lapi.h"
+
+/* Local defines */
+#define LUAR_FINDFUNCTION 0
+#define LUAR_FINDVALUE 1
+
+/* Externally defined read-only table array */
+extern const luaR_table lua_rotable[];
+
+/* Find a global "read only table" in the constant lua_rotable array */
+void* ICACHE_FLASH_ATTR luaR_findglobal(const char *name, unsigned len) {
+ unsigned i;
+
+ if (c_strlen(name) > LUA_MAX_ROTABLE_NAME)
+ return NULL;
+ for (i=0; lua_rotable[i].name; i ++)
+ if (*lua_rotable[i].name != '\0' && c_strlen(lua_rotable[i].name) == len && !c_strncmp(lua_rotable[i].name, name, len)) {
+ return (void*)(lua_rotable[i].pentries);
+ }
+ return NULL;
+}
+
+/* Find an entry in a rotable and return it */
+static const TValue* ICACHE_FLASH_ATTR luaR_auxfind(const luaR_entry *pentry, const char *strkey, luaR_numkey numkey, unsigned *ppos) {
+ const TValue *res = NULL;
+ unsigned i = 0;
+
+ if (pentry == NULL)
+ return NULL;
+ while(pentry->key.type != LUA_TNIL) {
+ if ((strkey && (pentry->key.type == LUA_TSTRING) && (!c_strcmp(pentry->key.id.strkey, strkey))) ||
+ (!strkey && (pentry->key.type == LUA_TNUMBER) && ((luaR_numkey)pentry->key.id.numkey == numkey))) {
+ res = &pentry->value;
+ break;
+ }
+ i ++; pentry ++;
+ }
+ if (res && ppos)
+ *ppos = i;
+ return res;
+}
+
+int ICACHE_FLASH_ATTR luaR_findfunction(lua_State *L, const luaR_entry *ptable) {
+ const TValue *res = NULL;
+ const char *key = luaL_checkstring(L, 2);
+
+ res = luaR_auxfind(ptable, key, 0, NULL);
+ if (res && ttislightfunction(res)) {
+ luaA_pushobject(L, res);
+ return 1;
+ }
+ else
+ return 0;
+}
+
+/* Find an entry in a rotable and return its type
+ If "strkey" is not NULL, the function will look for a string key,
+ otherwise it will look for a number key */
+const TValue* ICACHE_FLASH_ATTR luaR_findentry(void *data, const char *strkey, luaR_numkey numkey, unsigned *ppos) {
+ return luaR_auxfind((const luaR_entry*)data, strkey, numkey, ppos);
+}
+
+/* Find the metatable of a given table */
+void* ICACHE_FLASH_ATTR luaR_getmeta(void *data) {
+#ifdef LUA_META_ROTABLES
+ const TValue *res = luaR_auxfind((const luaR_entry*)data, "__metatable", 0, NULL);
+ return res && ttisrotable(res) ? rvalue(res) : NULL;
+#else
+ return NULL;
+#endif
+}
+
+static void ICACHE_FLASH_ATTR luaR_next_helper(lua_State *L, const luaR_entry *pentries, int pos, TValue *key, TValue *val) {
+ setnilvalue(key);
+ setnilvalue(val);
+ if (pentries[pos].key.type != LUA_TNIL) {
+ /* Found an entry */
+ if (pentries[pos].key.type == LUA_TSTRING)
+ setsvalue(L, key, luaS_newro(L, pentries[pos].key.id.strkey))
+ else
+ setnvalue(key, (lua_Number)pentries[pos].key.id.numkey)
+ setobj2s(L, val, &pentries[pos].value);
+ }
+}
+/* next (used for iteration) */
+void ICACHE_FLASH_ATTR luaR_next(lua_State *L, void *data, TValue *key, TValue *val) {
+ const luaR_entry* pentries = (const luaR_entry*)data;
+ char strkey[LUA_MAX_ROTABLE_NAME + 1], *pstrkey = NULL;
+ luaR_numkey numkey = 0;
+ unsigned keypos;
+
+ /* Special case: if key is nil, return the first element of the rotable */
+ if (ttisnil(key))
+ luaR_next_helper(L, pentries, 0, key, val);
+ else if (ttisstring(key) || ttisnumber(key)) {
+ /* Find the previoud key again */
+ if (ttisstring(key)) {
+ luaR_getcstr(strkey, rawtsvalue(key), LUA_MAX_ROTABLE_NAME);
+ pstrkey = strkey;
+ } else
+ numkey = (luaR_numkey)nvalue(key);
+ luaR_findentry(data, pstrkey, numkey, &keypos);
+ /* Advance to next key */
+ keypos ++;
+ luaR_next_helper(L, pentries, keypos, key, val);
+ }
+}
+
+/* Convert a Lua string to a C string */
+void ICACHE_FLASH_ATTR luaR_getcstr(char *dest, const TString *src, size_t maxsize) {
+ if (src->tsv.len+1 > maxsize)
+ dest[0] = '\0';
+ else {
+ c_memcpy(dest, getstr(src), src->tsv.len);
+ dest[src->tsv.len] = '\0';
+ }
+}
+
+/* Return 1 if the given pointer is a rotable */
+#ifdef LUA_META_ROTABLES
+
+#include "compiler.h"
+
+int ICACHE_FLASH_ATTR luaR_isrotable(void *p) {
+ return RODATA_START_ADDRESS <= (char*)p && (char*)p <= RODATA_END_ADDRESS;
+}
+#endif
diff --git a/app/lua/lrotable.h b/app/lua/lrotable.h
new file mode 100644
index 00000000..27de84a0
--- /dev/null
+++ b/app/lua/lrotable.h
@@ -0,0 +1,77 @@
+/* Read-only tables for Lua */
+
+#ifndef lrotable_h
+#define lrotable_h
+
+#include "lua.h"
+#include "llimits.h"
+#include "lobject.h"
+#include "luaconf.h"
+
+/* Macros one can use to define rotable entries */
+#ifndef LUA_PACK_VALUE
+#define LRO_FUNCVAL(v) {{.p = v}, LUA_TLIGHTFUNCTION}
+#define LRO_NUMVAL(v) {{.n = v}, LUA_TNUMBER}
+#define LRO_ROVAL(v) {{.p = (void*)v}, LUA_TROTABLE}
+#define LRO_NILVAL {{.p = NULL}, LUA_TNIL}
+#else // #ifndef LUA_PACK_VALUE
+#define LRO_NUMVAL(v) {.value.n = v}
+#ifdef ELUA_ENDIAN_LITTLE
+#define LRO_FUNCVAL(v) {{(int)v, add_sig(LUA_TLIGHTFUNCTION)}}
+#define LRO_ROVAL(v) {{(int)v, add_sig(LUA_TROTABLE)}}
+#define LRO_NILVAL {{0, add_sig(LUA_TNIL)}}
+#else // #ifdef ELUA_ENDIAN_LITTLE
+#define LRO_FUNCVAL(v) {{add_sig(LUA_TLIGHTFUNCTION), (int)v}}
+#define LRO_ROVAL(v) {{add_sig(LUA_TROTABLE), (int)v}}
+#define LRO_NILVAL {{add_sig(LUA_TNIL), 0}}
+#endif // #ifdef ELUA_ENDIAN_LITTLE
+#endif // #ifndef LUA_PACK_VALUE
+
+#define LRO_STRKEY(k) {LUA_TSTRING, {.strkey = k}}
+#define LRO_NUMKEY(k) {LUA_TNUMBER, {.numkey = k}}
+#define LRO_NILKEY {LUA_TNIL, {.strkey=NULL}}
+
+/* Maximum length of a rotable name and of a string key*/
+#define LUA_MAX_ROTABLE_NAME 32
+
+/* Type of a numeric key in a rotable */
+typedef int luaR_numkey;
+
+/* The next structure defines the type of a key */
+typedef struct
+{
+ int type;
+ union
+ {
+ const char* strkey;
+ luaR_numkey numkey;
+ } id;
+} luaR_key;
+
+/* An entry in the read only table */
+typedef struct
+{
+ const luaR_key key;
+ const TValue value;
+} luaR_entry;
+
+/* A rotable */
+typedef struct
+{
+ const char *name;
+ const luaR_entry *pentries;
+} luaR_table;
+
+void* luaR_findglobal(const char *key, unsigned len);
+int luaR_findfunction(lua_State *L, const luaR_entry *ptable);
+const TValue* luaR_findentry(void *data, const char *strkey, luaR_numkey numkey, unsigned *ppos);
+void luaR_getcstr(char *dest, const TString *src, size_t maxsize);
+void luaR_next(lua_State *L, void *data, TValue *key, TValue *val);
+void* luaR_getmeta(void *data);
+#ifdef LUA_META_ROTABLES
+int luaR_isrotable(void *p);
+#else
+#define luaR_isrotable(p) (0)
+#endif
+
+#endif
diff --git a/app/lua/lstate.c b/app/lua/lstate.c
new file mode 100644
index 00000000..75454af1
--- /dev/null
+++ b/app/lua/lstate.c
@@ -0,0 +1,246 @@
+/*
+** $Id: lstate.c,v 2.36.1.2 2008/01/03 15:20:39 roberto Exp $
+** Global State
+** See Copyright Notice in lua.h
+*/
+
+
+#include "c_stddef.h"
+
+#define lstate_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lgc.h"
+#include "llex.h"
+#include "lmem.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+
+#define state_size(x) (sizeof(x) + LUAI_EXTRASPACE)
+#define fromstate(l) (cast(lu_byte *, (l)) - LUAI_EXTRASPACE)
+#define tostate(l) (cast(lua_State *, cast(lu_byte *, l) + LUAI_EXTRASPACE))
+
+
+/*
+** Main thread combines a thread state and the global state
+*/
+typedef struct LG {
+ lua_State l;
+ global_State g;
+} LG;
+
+
+
+static void ICACHE_FLASH_ATTR stack_init (lua_State *L1, lua_State *L) {
+ /* initialize CallInfo array */
+ L1->base_ci = luaM_newvector(L, BASIC_CI_SIZE, CallInfo);
+ L1->ci = L1->base_ci;
+ L1->size_ci = BASIC_CI_SIZE;
+ L1->end_ci = L1->base_ci + L1->size_ci - 1;
+ /* initialize stack array */
+ L1->stack = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, TValue);
+ L1->stacksize = BASIC_STACK_SIZE + EXTRA_STACK;
+ L1->top = L1->stack;
+ L1->stack_last = L1->stack+(L1->stacksize - EXTRA_STACK)-1;
+ /* initialize first ci */
+ L1->ci->func = L1->top;
+ setnilvalue(L1->top++); /* `function' entry for this `ci' */
+ L1->base = L1->ci->base = L1->top;
+ L1->ci->top = L1->top + LUA_MINSTACK;
+}
+
+
+static void ICACHE_FLASH_ATTR freestack (lua_State *L, lua_State *L1) {
+ luaM_freearray(L, L1->base_ci, L1->size_ci, CallInfo);
+ luaM_freearray(L, L1->stack, L1->stacksize, TValue);
+}
+
+
+/*
+** open parts that may cause memory-allocation errors
+*/
+static void ICACHE_FLASH_ATTR f_luaopen (lua_State *L, void *ud) {
+ global_State *g = G(L);
+ UNUSED(ud);
+ stack_init(L, L); /* init stack */
+ sethvalue(L, gt(L), luaH_new(L, 0, 2)); /* table of globals */
+ sethvalue(L, registry(L), luaH_new(L, 0, 2)); /* registry */
+ luaS_resize(L, MINSTRTABSIZE); /* initial size of string table */
+ luaT_init(L);
+ luaX_init(L);
+ luaS_fix(luaS_newliteral(L, MEMERRMSG));
+ g->GCthreshold = 4*g->totalbytes;
+}
+
+
+static void ICACHE_FLASH_ATTR preinit_state (lua_State *L, global_State *g) {
+ G(L) = g;
+ L->stack = NULL;
+ L->stacksize = 0;
+ L->errorJmp = NULL;
+ L->hook = NULL;
+ L->hookmask = 0;
+ L->basehookcount = 0;
+ L->allowhook = 1;
+ resethookcount(L);
+ L->openupval = NULL;
+ L->size_ci = 0;
+ L->nCcalls = L->baseCcalls = 0;
+ L->status = 0;
+ L->base_ci = L->ci = NULL;
+ L->savedpc = NULL;
+ L->errfunc = 0;
+ setnilvalue(gt(L));
+}
+
+
+static void ICACHE_FLASH_ATTR close_state (lua_State *L) {
+ global_State *g = G(L);
+ luaF_close(L, L->stack); /* close all upvalues for this thread */
+ luaC_freeall(L); /* collect all objects */
+ lua_assert(g->rootgc == obj2gco(L));
+ lua_assert(g->strt.nuse == 0);
+ luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size, TString *);
+ luaZ_freebuffer(L, &g->buff);
+ freestack(L, L);
+ lua_assert(g->totalbytes == sizeof(LG));
+ (*g->frealloc)(g->ud, fromstate(L), state_size(LG), 0);
+}
+
+
+lua_State *ICACHE_FLASH_ATTR luaE_newthread (lua_State *L) {
+ lua_State *L1 = tostate(luaM_malloc(L, state_size(lua_State)));
+ luaC_link(L, obj2gco(L1), LUA_TTHREAD);
+ setthvalue(L, L->top, L1); /* put thread on stack */
+ incr_top(L);
+ preinit_state(L1, G(L));
+ stack_init(L1, L); /* init stack */
+ setobj2n(L, gt(L1), gt(L)); /* share table of globals */
+ L1->hookmask = L->hookmask;
+ L1->basehookcount = L->basehookcount;
+ L1->hook = L->hook;
+ resethookcount(L1);
+ lua_assert(!isdead(G(L), obj2gco(L1)));
+ L->top--; /* remove thread from stack */
+ return L1;
+}
+
+
+void ICACHE_FLASH_ATTR luaE_freethread (lua_State *L, lua_State *L1) {
+ luaF_close(L1, L1->stack); /* close all upvalues for this thread */
+ lua_assert(L1->openupval == NULL);
+ luai_userstatefree(L1);
+ freestack(L, L1);
+ luaM_freemem(L, fromstate(L1), state_size(lua_State));
+}
+
+
+LUA_API lua_State *ICACHE_FLASH_ATTR lua_newstate (lua_Alloc f, void *ud) {
+ int i;
+ lua_State *L;
+ global_State *g;
+ void *l = (*f)(ud, NULL, 0, state_size(LG));
+ if (l == NULL) return NULL;
+ L = tostate(l);
+ g = &((LG *)L)->g;
+ L->next = NULL;
+ L->tt = LUA_TTHREAD;
+ g->currentwhite = bit2mask(WHITE0BIT, FIXEDBIT);
+ L->marked = luaC_white(g);
+ set2bits(L->marked, FIXEDBIT, SFIXEDBIT);
+ preinit_state(L, g);
+ g->frealloc = f;
+ g->ud = ud;
+ g->mainthread = L;
+ g->uvhead.u.l.prev = &g->uvhead;
+ g->uvhead.u.l.next = &g->uvhead;
+ g->GCthreshold = 0; /* mark it as unfinished state */
+ g->estimate = 0;
+ g->strt.size = 0;
+ g->strt.nuse = 0;
+ g->strt.hash = NULL;
+ setnilvalue(registry(L));
+ luaZ_initbuffer(L, &g->buff);
+ g->panic = NULL;
+ g->gcstate = GCSpause;
+ g->gcflags = GCFlagsNone;
+ g->rootgc = obj2gco(L);
+ g->sweepstrgc = 0;
+ g->sweepgc = &g->rootgc;
+ g->gray = NULL;
+ g->grayagain = NULL;
+ g->weak = NULL;
+ g->tmudata = NULL;
+ g->totalbytes = sizeof(LG);
+ g->memlimit = 0;
+ g->gcpause = LUAI_GCPAUSE;
+ g->gcstepmul = LUAI_GCMUL;
+ g->gcdept = 0;
+#ifdef EGC_INITIAL_MODE
+ g->egcmode = EGC_INITIAL_MODE;
+#else
+ g->egcmode = 0;
+#endif
+#ifdef EGC_INITIAL_MEMLIMIT
+ g->memlimit = EGC_INITIAL_MEMLIMIT;
+#else
+ g->memlimit = 0;
+#endif
+ for (i=0; imt[i] = NULL;
+ if (luaD_rawrunprotected(L, f_luaopen, NULL) != 0) {
+ /* memory allocation error: free partial state */
+ close_state(L);
+ L = NULL;
+ }
+ else
+ luai_userstateopen(L);
+ return L;
+}
+
+
+static void ICACHE_FLASH_ATTR callallgcTM (lua_State *L, void *ud) {
+ UNUSED(ud);
+ luaC_callGCTM(L); /* call GC metamethods for all udata */
+}
+
+// BogdanM: modified for eLua interrupt support
+extern lua_State *luaL_newstate (void);
+static lua_State *lua_crtstate;
+
+lua_State *ICACHE_FLASH_ATTR lua_open(void) {
+ lua_crtstate = luaL_newstate();
+ return lua_crtstate;
+}
+
+lua_State *ICACHE_FLASH_ATTR lua_getstate(void) {
+ return lua_crtstate;
+}
+LUA_API void ICACHE_FLASH_ATTR lua_close (lua_State *L) {
+#ifndef LUA_CROSS_COMPILER
+ lua_sethook( L, NULL, 0, 0 );
+ lua_crtstate = NULL;
+ lua_pushnil( L );
+// lua_rawseti( L, LUA_REGISTRYINDEX, LUA_INT_HANDLER_KEY );
+#endif
+ L = G(L)->mainthread; /* only the main thread can be closed */
+ lua_lock(L);
+ luaF_close(L, L->stack); /* close all upvalues for this thread */
+ luaC_separateudata(L, 1); /* separate udata that have GC metamethods */
+ L->errfunc = 0; /* no error function during GC metamethods */
+ do { /* repeat until no more errors */
+ L->ci = L->base_ci;
+ L->base = L->top = L->ci->base;
+ L->nCcalls = L->baseCcalls = 0;
+ } while (luaD_rawrunprotected(L, callallgcTM, NULL) != 0);
+ lua_assert(G(L)->tmudata == NULL);
+ luai_userstateclose(L);
+ close_state(L);
+}
+
diff --git a/app/lua/lstate.h b/app/lua/lstate.h
new file mode 100644
index 00000000..1b3c363a
--- /dev/null
+++ b/app/lua/lstate.h
@@ -0,0 +1,171 @@
+/*
+** $Id: lstate.h,v 2.24.1.2 2008/01/03 15:20:39 roberto Exp $
+** Global State
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lstate_h
+#define lstate_h
+
+#include "lua.h"
+
+#include "lobject.h"
+#include "ltm.h"
+#include "lzio.h"
+
+
+
+struct lua_longjmp; /* defined in ldo.c */
+
+
+/* table of globals */
+#define gt(L) (&L->l_gt)
+
+/* registry */
+#define registry(L) (&G(L)->l_registry)
+
+
+/* extra stack space to handle TM calls and some other extras */
+#define EXTRA_STACK 5
+
+
+#define BASIC_CI_SIZE 8
+
+#define BASIC_STACK_SIZE (2*LUA_MINSTACK)
+
+
+
+typedef struct stringtable {
+ GCObject **hash;
+ lu_int32 nuse; /* number of elements */
+ int size;
+} stringtable;
+
+
+/*
+** informations about a call
+*/
+typedef struct CallInfo {
+ StkId base; /* base for this function */
+ StkId func; /* function index in the stack */
+ StkId top; /* top for this function */
+ const Instruction *savedpc;
+ int nresults; /* expected number of results from this function */
+ int tailcalls; /* number of tail calls lost under this entry */
+} CallInfo;
+
+
+
+#define curr_func(L) (ttisfunction(L->ci->func) ? clvalue(L->ci->func) : NULL)
+#define ci_func(ci) (ttisfunction((ci)->func) ? clvalue((ci)->func) : NULL)
+#define f_isLua(ci) (!ttislightfunction((ci)->func) && !ci_func(ci)->c.isC)
+#define isLua(ci) (ttisfunction((ci)->func) && f_isLua(ci))
+
+
+/*
+** `global state', shared by all threads of this state
+*/
+typedef struct global_State {
+ stringtable strt; /* hash table for strings */
+ lua_Alloc frealloc; /* function to reallocate memory */
+ void *ud; /* auxiliary data to `frealloc' */
+ lu_byte currentwhite;
+ lu_byte gcstate; /* state of garbage collector */
+ lu_byte gcflags; /* flags for the garbage collector */
+ int sweepstrgc; /* position of sweep in `strt' */
+ GCObject *rootgc; /* list of all collectable objects */
+ GCObject **sweepgc; /* position of sweep in `rootgc' */
+ GCObject *gray; /* list of gray objects */
+ GCObject *grayagain; /* list of objects to be traversed atomically */
+ GCObject *weak; /* list of weak tables (to be cleared) */
+ GCObject *tmudata; /* last element of list of userdata to be GC */
+ Mbuffer buff; /* temporary buffer for string concatentation */
+ lu_mem GCthreshold;
+ lu_mem totalbytes; /* number of bytes currently allocated */
+ lu_mem memlimit; /* maximum number of bytes that can be allocated, 0 = no limit. */
+ lu_mem estimate; /* an estimate of number of bytes actually in use */
+ lu_mem gcdept; /* how much GC is `behind schedule' */
+ int gcpause; /* size of pause between successive GCs */
+ int gcstepmul; /* GC `granularity' */
+ int egcmode; /* emergency garbage collection operation mode */
+ lua_CFunction panic; /* to be called in unprotected errors */
+ TValue l_registry;
+ struct lua_State *mainthread;
+ UpVal uvhead; /* head of double-linked list of all open upvalues */
+ struct Table *mt[NUM_TAGS]; /* metatables for basic types */
+ TString *tmname[TM_N]; /* array with tag-method names */
+} global_State;
+
+
+/*
+** `per thread' state
+*/
+struct lua_State {
+ CommonHeader;
+ lu_byte status;
+ StkId top; /* first free slot in the stack */
+ StkId base; /* base of current function */
+ global_State *l_G;
+ CallInfo *ci; /* call info for current function */
+ const Instruction *savedpc; /* `savedpc' of current function */
+ StkId stack_last; /* last free slot in the stack */
+ StkId stack; /* stack base */
+ CallInfo *end_ci; /* points after end of ci array*/
+ CallInfo *base_ci; /* array of CallInfo's */
+ int stacksize;
+ int size_ci; /* size of array `base_ci' */
+ unsigned short nCcalls; /* number of nested C calls */
+ unsigned short baseCcalls; /* nested C calls when resuming coroutine */
+ lu_byte hookmask;
+ lu_byte allowhook;
+ int basehookcount;
+ int hookcount;
+ lua_Hook hook;
+ TValue l_gt; /* table of globals */
+ TValue env; /* temporary place for environments */
+ GCObject *openupval; /* list of open upvalues in this stack */
+ GCObject *gclist;
+ struct lua_longjmp *errorJmp; /* current error recover point */
+ ptrdiff_t errfunc; /* current error handling function (stack index) */
+};
+
+
+#define G(L) (L->l_G)
+
+
+/*
+** Union of all collectable objects
+*/
+union GCObject {
+ GCheader gch;
+ union TString ts;
+ union Udata u;
+ union Closure cl;
+ struct Table h;
+ struct Proto p;
+ struct UpVal uv;
+ struct lua_State th; /* thread */
+};
+
+
+/* macros to convert a GCObject into a specific value */
+#define rawgco2ts(o) check_exp((o)->gch.tt == LUA_TSTRING, &((o)->ts))
+#define gco2ts(o) (&rawgco2ts(o)->tsv)
+#define rawgco2u(o) check_exp((o)->gch.tt == LUA_TUSERDATA, &((o)->u))
+#define gco2u(o) (&rawgco2u(o)->uv)
+#define gco2cl(o) check_exp((o)->gch.tt == LUA_TFUNCTION, &((o)->cl))
+#define gco2h(o) check_exp((o)->gch.tt == LUA_TTABLE, &((o)->h))
+#define gco2p(o) check_exp((o)->gch.tt == LUA_TPROTO, &((o)->p))
+#define gco2uv(o) check_exp((o)->gch.tt == LUA_TUPVAL, &((o)->uv))
+#define ngcotouv(o) \
+ check_exp((o) == NULL || (o)->gch.tt == LUA_TUPVAL, &((o)->uv))
+#define gco2th(o) check_exp((o)->gch.tt == LUA_TTHREAD, &((o)->th))
+
+/* macro to convert any Lua object into a GCObject */
+#define obj2gco(v) (cast(GCObject *, (v)))
+
+LUAI_FUNC lua_State *luaE_newthread (lua_State *L);
+LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1);
+
+#endif
+
diff --git a/app/lua/lstring.c b/app/lua/lstring.c
new file mode 100644
index 00000000..61dc74cd
--- /dev/null
+++ b/app/lua/lstring.c
@@ -0,0 +1,147 @@
+/*
+** $Id: lstring.c,v 2.8.1.1 2007/12/27 13:02:25 roberto Exp $
+** String table (keeps all strings handled by Lua)
+** See Copyright Notice in lua.h
+*/
+
+
+#include "c_string.h"
+
+#define lstring_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+#include "lstring.h"
+
+#define LUAS_READONLY_STRING 1
+#define LUAS_REGULAR_STRING 0
+
+void ICACHE_FLASH_ATTR luaS_resize (lua_State *L, int newsize) {
+ stringtable *tb;
+ int i;
+ tb = &G(L)->strt;
+ if (luaC_sweepstrgc(L) || newsize == tb->size || is_resizing_strings_gc(L))
+ return; /* cannot resize during GC traverse or doesn't need to be resized */
+ set_resizing_strings_gc(L);
+ if (newsize > tb->size) {
+ luaM_reallocvector(L, tb->hash, tb->size, newsize, GCObject *);
+ for (i=tb->size; ihash[i] = NULL;
+ }
+ /* rehash */
+ for (i=0; isize; i++) {
+ GCObject *p = tb->hash[i];
+ tb->hash[i] = NULL;
+ while (p) { /* for each node in the list */
+ GCObject *next = p->gch.next; /* save next */
+ unsigned int h = gco2ts(p)->hash;
+ int h1 = lmod(h, newsize); /* new position */
+ lua_assert(cast_int(h%newsize) == lmod(h, newsize));
+ p->gch.next = tb->hash[h1]; /* chain it */
+ tb->hash[h1] = p;
+ p = next;
+ }
+ }
+ if (newsize < tb->size)
+ luaM_reallocvector(L, tb->hash, tb->size, newsize, GCObject *);
+ tb->size = newsize;
+ unset_resizing_strings_gc(L);
+}
+
+static TString *ICACHE_FLASH_ATTR newlstr (lua_State *L, const char *str, size_t l,
+ unsigned int h, int readonly) {
+ TString *ts;
+ stringtable *tb;
+ if (l+1 > (MAX_SIZET - sizeof(TString))/sizeof(char))
+ luaM_toobig(L);
+ tb = &G(L)->strt;
+ if ((tb->nuse + 1) > cast(lu_int32, tb->size) && tb->size <= MAX_INT/2)
+ luaS_resize(L, tb->size*2); /* too crowded */
+ ts = cast(TString *, luaM_malloc(L, readonly ? sizeof(char**)+sizeof(TString) : (l+1)*sizeof(char)+sizeof(TString)));
+ ts->tsv.len = l;
+ ts->tsv.hash = h;
+ ts->tsv.marked = luaC_white(G(L));
+ ts->tsv.tt = LUA_TSTRING;
+ if (!readonly) {
+ c_memcpy(ts+1, str, l*sizeof(char));
+ ((char *)(ts+1))[l] = '\0'; /* ending 0 */
+ } else {
+ *(char **)(ts+1) = (char *)str;
+ luaS_readonly(ts);
+ }
+ h = lmod(h, tb->size);
+ ts->tsv.next = tb->hash[h]; /* chain new entry */
+ tb->hash[h] = obj2gco(ts);
+ tb->nuse++;
+ return ts;
+}
+
+
+static TString *ICACHE_FLASH_ATTR luaS_newlstr_helper (lua_State *L, const char *str, size_t l, int readonly) {
+ GCObject *o;
+ unsigned int h = cast(unsigned int, l); /* seed */
+ size_t step = (l>>5)+1; /* if string is too long, don't hash all its chars */
+ size_t l1;
+ for (l1=l; l1>=step; l1-=step) /* compute hash */
+ h = h ^ ((h<<5)+(h>>2)+cast(unsigned char, str[l1-1]));
+ for (o = G(L)->strt.hash[lmod(h, G(L)->strt.size)];
+ o != NULL;
+ o = o->gch.next) {
+ TString *ts = rawgco2ts(o);
+ if (ts->tsv.len == l && (c_memcmp(str, getstr(ts), l) == 0)) {
+ /* string may be dead */
+ if (isdead(G(L), o)) changewhite(o);
+ return ts;
+ }
+ }
+ return newlstr(L, str, l, h, readonly); /* not found */
+}
+
+static int ICACHE_FLASH_ATTR lua_is_ptr_in_ro_area(const char *p) {
+#ifdef LUA_CROSS_COMPILER
+ return 0;
+#else
+
+#include "compiler.h"
+
+ return p >= RODATA_START_ADDRESS && p <= RODATA_END_ADDRESS;
+#endif
+}
+
+TString *ICACHE_FLASH_ATTR luaS_newlstr (lua_State *L, const char *str, size_t l) {
+ // If the pointer is in a read-only memory and the string is at least 4 chars in length,
+ // create it as a read-only string instead
+ if(lua_is_ptr_in_ro_area(str) && l+1 > sizeof(char**) && l == c_strlen(str))
+ return luaS_newlstr_helper(L, str, l, LUAS_READONLY_STRING);
+ else
+ return luaS_newlstr_helper(L, str, l, LUAS_REGULAR_STRING);
+}
+
+
+LUAI_FUNC TString *ICACHE_FLASH_ATTR luaS_newrolstr (lua_State *L, const char *str, size_t l) {
+ if(l+1 > sizeof(char**) && l == c_strlen(str))
+ return luaS_newlstr_helper(L, str, l, LUAS_READONLY_STRING);
+ else // no point in creating a RO string, as it would actually be larger
+ return luaS_newlstr_helper(L, str, l, LUAS_REGULAR_STRING);
+}
+
+
+Udata *ICACHE_FLASH_ATTR luaS_newudata (lua_State *L, size_t s, Table *e) {
+ Udata *u;
+ if (s > MAX_SIZET - sizeof(Udata))
+ luaM_toobig(L);
+ u = cast(Udata *, luaM_malloc(L, s + sizeof(Udata)));
+ u->uv.marked = luaC_white(G(L)); /* is not finalized */
+ u->uv.tt = LUA_TUSERDATA;
+ u->uv.len = s;
+ u->uv.metatable = NULL;
+ u->uv.env = e;
+ /* chain it on udata list (after main thread) */
+ u->uv.next = G(L)->mainthread->next;
+ G(L)->mainthread->next = obj2gco(u);
+ return u;
+}
+
diff --git a/app/lua/lstring.h b/app/lua/lstring.h
new file mode 100644
index 00000000..b4cc3dda
--- /dev/null
+++ b/app/lua/lstring.h
@@ -0,0 +1,34 @@
+/*
+** $Id: lstring.h,v 1.43.1.1 2007/12/27 13:02:25 roberto Exp $
+** String table (keep all strings handled by Lua)
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lstring_h
+#define lstring_h
+
+
+#include "lgc.h"
+#include "lobject.h"
+#include "lstate.h"
+
+
+#define sizestring(s) (sizeof(union TString)+(luaS_isreadonly(s) ? sizeof(char **) : ((s)->len+1)*sizeof(char)))
+
+#define sizeudata(u) (sizeof(union Udata)+(u)->len)
+
+#define luaS_new(L, s) (luaS_newlstr(L, s, c_strlen(s)))
+#define luaS_newro(L, s) (luaS_newrolstr(L, s, c_strlen(s)))
+#define luaS_newliteral(L, s) (luaS_newlstr(L, "" s, \
+ (sizeof(s)/sizeof(char))-1))
+
+#define luaS_fix(s) l_setbit((s)->tsv.marked, FIXEDBIT)
+#define luaS_readonly(s) l_setbit((s)->tsv.marked, READONLYBIT)
+#define luaS_isreadonly(s) testbit((s)->marked, READONLYBIT)
+
+LUAI_FUNC void luaS_resize (lua_State *L, int newsize);
+LUAI_FUNC Udata *luaS_newudata (lua_State *L, size_t s, Table *e);
+LUAI_FUNC TString *luaS_newlstr (lua_State *L, const char *str, size_t l);
+LUAI_FUNC TString *luaS_newrolstr (lua_State *L, const char *str, size_t l);
+
+#endif
diff --git a/app/lua/lstrlib.c b/app/lua/lstrlib.c
new file mode 100644
index 00000000..1a6aeb4d
--- /dev/null
+++ b/app/lua/lstrlib.c
@@ -0,0 +1,893 @@
+/*
+** $Id: lstrlib.c,v 1.132.1.5 2010/05/14 15:34:19 roberto Exp $
+** Standard library for string operations and pattern-matching
+** See Copyright Notice in lua.h
+*/
+
+
+#include "c_ctype.h"
+#include "c_stddef.h"
+#include "c_stdio.h"
+#include "c_stdlib.h"
+#include "c_string.h"
+
+#define lstrlib_c
+#define LUA_LIB
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+#include "lrotable.h"
+
+/* macro to `unsign' a character */
+#define uchar(c) ((unsigned char)(c))
+
+
+
+static int ICACHE_FLASH_ATTR str_len (lua_State *L) {
+ size_t l;
+ luaL_checklstring(L, 1, &l);
+ lua_pushinteger(L, l);
+ return 1;
+}
+
+
+static ptrdiff_t ICACHE_FLASH_ATTR posrelat (ptrdiff_t pos, size_t len) {
+ /* relative string position: negative means back from end */
+ if (pos < 0) pos += (ptrdiff_t)len + 1;
+ return (pos >= 0) ? pos : 0;
+}
+
+
+static int ICACHE_FLASH_ATTR str_sub (lua_State *L) {
+ size_t l;
+ const char *s = luaL_checklstring(L, 1, &l);
+ ptrdiff_t start = posrelat(luaL_checkinteger(L, 2), l);
+ ptrdiff_t end = posrelat(luaL_optinteger(L, 3, -1), l);
+ if (start < 1) start = 1;
+ if (end > (ptrdiff_t)l) end = (ptrdiff_t)l;
+ if (start <= end)
+ lua_pushlstring(L, s+start-1, end-start+1);
+ else lua_pushliteral(L, "");
+ return 1;
+}
+
+
+static int ICACHE_FLASH_ATTR str_reverse (lua_State *L) {
+ size_t l;
+ luaL_Buffer b;
+ const char *s = luaL_checklstring(L, 1, &l);
+ luaL_buffinit(L, &b);
+ while (l--) luaL_addchar(&b, s[l]);
+ luaL_pushresult(&b);
+ return 1;
+}
+
+
+static int ICACHE_FLASH_ATTR str_lower (lua_State *L) {
+ size_t l;
+ size_t i;
+ luaL_Buffer b;
+ const char *s = luaL_checklstring(L, 1, &l);
+ luaL_buffinit(L, &b);
+ for (i=0; i 0)
+ luaL_addlstring(&b, s, l);
+ luaL_pushresult(&b);
+ return 1;
+}
+
+
+static int ICACHE_FLASH_ATTR str_byte (lua_State *L) {
+ size_t l;
+ const char *s = luaL_checklstring(L, 1, &l);
+ ptrdiff_t posi = posrelat(luaL_optinteger(L, 2, 1), l);
+ ptrdiff_t pose = posrelat(luaL_optinteger(L, 3, posi), l);
+ int n, i;
+ if (posi <= 0) posi = 1;
+ if ((size_t)pose > l) pose = l;
+ if (posi > pose) return 0; /* empty interval; return no values */
+ n = (int)(pose - posi + 1);
+ if (posi + n <= pose) /* overflow? */
+ luaL_error(L, "string slice too long");
+ luaL_checkstack(L, n, "string slice too long");
+ for (i=0; i= ms->level || ms->capture[l].len == CAP_UNFINISHED)
+ return luaL_error(ms->L, "invalid capture index");
+ return l;
+}
+
+
+static int ICACHE_FLASH_ATTR capture_to_close (MatchState *ms) {
+ int level = ms->level;
+ for (level--; level>=0; level--)
+ if (ms->capture[level].len == CAP_UNFINISHED) return level;
+ return luaL_error(ms->L, "invalid pattern capture");
+}
+
+
+static const char *ICACHE_FLASH_ATTR classend (MatchState *ms, const char *p) {
+ switch (*p++) {
+ case L_ESC: {
+ if (*p == '\0')
+ luaL_error(ms->L, "malformed pattern (ends with " LUA_QL("%%") ")");
+ return p+1;
+ }
+ case '[': {
+ if (*p == '^') p++;
+ do { /* look for a `]' */
+ if (*p == '\0')
+ luaL_error(ms->L, "malformed pattern (missing " LUA_QL("]") ")");
+ if (*(p++) == L_ESC && *p != '\0')
+ p++; /* skip escapes (e.g. `%]') */
+ } while (*p != ']');
+ return p+1;
+ }
+ default: {
+ return p;
+ }
+ }
+}
+
+
+static int ICACHE_FLASH_ATTR match_class (int c, int cl) {
+ int res;
+ switch (tolower(cl)) {
+ case 'a' : res = isalpha(c); break;
+ case 'c' : res = iscntrl(c); break;
+ case 'd' : res = isdigit(c); break;
+ case 'l' : res = islower(c); break;
+ case 'p' : res = ispunct(c); break;
+ case 's' : res = isspace(c); break;
+ case 'u' : res = isupper(c); break;
+ case 'w' : res = isalnum(c); break;
+ case 'x' : res = isxdigit(c); break;
+ case 'z' : res = (c == 0); break;
+ default: return (cl == c);
+ }
+ return (islower(cl) ? res : !res);
+}
+
+
+static int ICACHE_FLASH_ATTR matchbracketclass (int c, const char *p, const char *ec) {
+ int sig = 1;
+ if (*(p+1) == '^') {
+ sig = 0;
+ p++; /* skip the `^' */
+ }
+ while (++p < ec) {
+ if (*p == L_ESC) {
+ p++;
+ if (match_class(c, uchar(*p)))
+ return sig;
+ }
+ else if ((*(p+1) == '-') && (p+2 < ec)) {
+ p+=2;
+ if (uchar(*(p-2)) <= c && c <= uchar(*p))
+ return sig;
+ }
+ else if (uchar(*p) == c) return sig;
+ }
+ return !sig;
+}
+
+
+static int ICACHE_FLASH_ATTR singlematch (int c, const char *p, const char *ep) {
+ switch (*p) {
+ case '.': return 1; /* matches any char */
+ case L_ESC: return match_class(c, uchar(*(p+1)));
+ case '[': return matchbracketclass(c, p, ep-1);
+ default: return (uchar(*p) == c);
+ }
+}
+
+
+static const char *match (MatchState *ms, const char *s, const char *p);
+
+
+static const char *ICACHE_FLASH_ATTR matchbalance (MatchState *ms, const char *s,
+ const char *p) {
+ if (*p == 0 || *(p+1) == 0)
+ luaL_error(ms->L, "unbalanced pattern");
+ if (*s != *p) return NULL;
+ else {
+ int b = *p;
+ int e = *(p+1);
+ int cont = 1;
+ while (++s < ms->src_end) {
+ if (*s == e) {
+ if (--cont == 0) return s+1;
+ }
+ else if (*s == b) cont++;
+ }
+ }
+ return NULL; /* string ends out of balance */
+}
+
+
+static const char *ICACHE_FLASH_ATTR max_expand (MatchState *ms, const char *s,
+ const char *p, const char *ep) {
+ ptrdiff_t i = 0; /* counts maximum expand for item */
+ while ((s+i)src_end && singlematch(uchar(*(s+i)), p, ep))
+ i++;
+ /* keeps trying to match with the maximum repetitions */
+ while (i>=0) {
+ const char *res = match(ms, (s+i), ep+1);
+ if (res) return res;
+ i--; /* else didn't match; reduce 1 repetition to try again */
+ }
+ return NULL;
+}
+
+
+static const char *ICACHE_FLASH_ATTR min_expand (MatchState *ms, const char *s,
+ const char *p, const char *ep) {
+ for (;;) {
+ const char *res = match(ms, s, ep+1);
+ if (res != NULL)
+ return res;
+ else if (ssrc_end && singlematch(uchar(*s), p, ep))
+ s++; /* try with one more repetition */
+ else return NULL;
+ }
+}
+
+
+static const char *ICACHE_FLASH_ATTR start_capture (MatchState *ms, const char *s,
+ const char *p, int what) {
+ const char *res;
+ int level = ms->level;
+ if (level >= LUA_MAXCAPTURES) luaL_error(ms->L, "too many captures");
+ ms->capture[level].init = s;
+ ms->capture[level].len = what;
+ ms->level = level+1;
+ if ((res=match(ms, s, p)) == NULL) /* match failed? */
+ ms->level--; /* undo capture */
+ return res;
+}
+
+
+static const char *ICACHE_FLASH_ATTR end_capture (MatchState *ms, const char *s,
+ const char *p) {
+ int l = capture_to_close(ms);
+ const char *res;
+ ms->capture[l].len = s - ms->capture[l].init; /* close capture */
+ if ((res = match(ms, s, p)) == NULL) /* match failed? */
+ ms->capture[l].len = CAP_UNFINISHED; /* undo capture */
+ return res;
+}
+
+
+static const char *ICACHE_FLASH_ATTR match_capture (MatchState *ms, const char *s, int l) {
+ size_t len;
+ l = check_capture(ms, l);
+ len = ms->capture[l].len;
+ if ((size_t)(ms->src_end-s) >= len &&
+ c_memcmp(ms->capture[l].init, s, len) == 0)
+ return s+len;
+ else return NULL;
+}
+
+
+static const char *ICACHE_FLASH_ATTR match (MatchState *ms, const char *s, const char *p) {
+ init: /* using goto's to optimize tail recursion */
+ switch (*p) {
+ case '(': { /* start capture */
+ if (*(p+1) == ')') /* position capture? */
+ return start_capture(ms, s, p+2, CAP_POSITION);
+ else
+ return start_capture(ms, s, p+1, CAP_UNFINISHED);
+ }
+ case ')': { /* end capture */
+ return end_capture(ms, s, p+1);
+ }
+ case L_ESC: {
+ switch (*(p+1)) {
+ case 'b': { /* balanced string? */
+ s = matchbalance(ms, s, p+2);
+ if (s == NULL) return NULL;
+ p+=4; goto init; /* else return match(ms, s, p+4); */
+ }
+ case 'f': { /* frontier? */
+ const char *ep; char previous;
+ p += 2;
+ if (*p != '[')
+ luaL_error(ms->L, "missing " LUA_QL("[") " after "
+ LUA_QL("%%f") " in pattern");
+ ep = classend(ms, p); /* points to what is next */
+ previous = (s == ms->src_init) ? '\0' : *(s-1);
+ if (matchbracketclass(uchar(previous), p, ep-1) ||
+ !matchbracketclass(uchar(*s), p, ep-1)) return NULL;
+ p=ep; goto init; /* else return match(ms, s, ep); */
+ }
+ default: {
+ if (isdigit(uchar(*(p+1)))) { /* capture results (%0-%9)? */
+ s = match_capture(ms, s, uchar(*(p+1)));
+ if (s == NULL) return NULL;
+ p+=2; goto init; /* else return match(ms, s, p+2) */
+ }
+ goto dflt; /* case default */
+ }
+ }
+ }
+ case '\0': { /* end of pattern */
+ return s; /* match succeeded */
+ }
+ case '$': {
+ if (*(p+1) == '\0') /* is the `$' the last char in pattern? */
+ return (s == ms->src_end) ? s : NULL; /* check end of string */
+ else goto dflt;
+ }
+ default: dflt: { /* it is a pattern item */
+ const char *ep = classend(ms, p); /* points to what is next */
+ int m = ssrc_end && singlematch(uchar(*s), p, ep);
+ switch (*ep) {
+ case '?': { /* optional */
+ const char *res;
+ if (m && ((res=match(ms, s+1, ep+1)) != NULL))
+ return res;
+ p=ep+1; goto init; /* else return match(ms, s, ep+1); */
+ }
+ case '*': { /* 0 or more repetitions */
+ return max_expand(ms, s, p, ep);
+ }
+ case '+': { /* 1 or more repetitions */
+ return (m ? max_expand(ms, s+1, p, ep) : NULL);
+ }
+ case '-': { /* 0 or more repetitions (minimum) */
+ return min_expand(ms, s, p, ep);
+ }
+ default: {
+ if (!m) return NULL;
+ s++; p=ep; goto init; /* else return match(ms, s+1, ep); */
+ }
+ }
+ }
+ }
+}
+
+
+
+static const char *ICACHE_FLASH_ATTR lmemfind (const char *s1, size_t l1,
+ const char *s2, size_t l2) {
+ if (l2 == 0) return s1; /* empty strings are everywhere */
+ else if (l2 > l1) return NULL; /* avoids a negative `l1' */
+ else {
+ const char *init; /* to search for a `*s2' inside `s1' */
+ l2--; /* 1st char will be checked by `memchr' */
+ l1 = l1-l2; /* `s2' cannot be found after that */
+ while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) {
+ init++; /* 1st char is already checked */
+ if (c_memcmp(init, s2+1, l2) == 0)
+ return init-1;
+ else { /* correct `l1' and `s1' to try again */
+ l1 -= init-s1;
+ s1 = init;
+ }
+ }
+ return NULL; /* not found */
+ }
+}
+
+
+static void ICACHE_FLASH_ATTR push_onecapture (MatchState *ms, int i, const char *s,
+ const char *e) {
+ if (i >= ms->level) {
+ if (i == 0) /* ms->level == 0, too */
+ lua_pushlstring(ms->L, s, e - s); /* add whole match */
+ else
+ luaL_error(ms->L, "invalid capture index");
+ }
+ else {
+ ptrdiff_t l = ms->capture[i].len;
+ if (l == CAP_UNFINISHED) luaL_error(ms->L, "unfinished capture");
+ if (l == CAP_POSITION)
+ lua_pushinteger(ms->L, ms->capture[i].init - ms->src_init + 1);
+ else
+ lua_pushlstring(ms->L, ms->capture[i].init, l);
+ }
+}
+
+
+static int ICACHE_FLASH_ATTR push_captures (MatchState *ms, const char *s, const char *e) {
+ int i;
+ int nlevels = (ms->level == 0 && s) ? 1 : ms->level;
+ luaL_checkstack(ms->L, nlevels, "too many captures");
+ for (i = 0; i < nlevels; i++)
+ push_onecapture(ms, i, s, e);
+ return nlevels; /* number of strings pushed */
+}
+
+
+static int ICACHE_FLASH_ATTR str_find_aux (lua_State *L, int find) {
+ size_t l1, l2;
+ const char *s = luaL_checklstring(L, 1, &l1);
+ const char *p = luaL_checklstring(L, 2, &l2);
+ ptrdiff_t init = posrelat(luaL_optinteger(L, 3, 1), l1) - 1;
+ if (init < 0) init = 0;
+ else if ((size_t)(init) > l1) init = (ptrdiff_t)l1;
+ if (find && (lua_toboolean(L, 4) || /* explicit request? */
+ c_strpbrk(p, SPECIALS) == NULL)) { /* or no special characters? */
+ /* do a plain search */
+ const char *s2 = lmemfind(s+init, l1-init, p, l2);
+ if (s2) {
+ lua_pushinteger(L, s2-s+1);
+ lua_pushinteger(L, s2-s+l2);
+ return 2;
+ }
+ }
+ else {
+ MatchState ms;
+ int anchor = (*p == '^') ? (p++, 1) : 0;
+ const char *s1=s+init;
+ ms.L = L;
+ ms.src_init = s;
+ ms.src_end = s+l1;
+ do {
+ const char *res;
+ ms.level = 0;
+ if ((res=match(&ms, s1, p)) != NULL) {
+ if (find) {
+ lua_pushinteger(L, s1-s+1); /* start */
+ lua_pushinteger(L, res-s); /* end */
+ return push_captures(&ms, NULL, 0) + 2;
+ }
+ else
+ return push_captures(&ms, s1, res);
+ }
+ } while (s1++ < ms.src_end && !anchor);
+ }
+ lua_pushnil(L); /* not found */
+ return 1;
+}
+
+
+static int ICACHE_FLASH_ATTR str_find (lua_State *L) {
+ return str_find_aux(L, 1);
+}
+
+
+static int ICACHE_FLASH_ATTR str_match (lua_State *L) {
+ return str_find_aux(L, 0);
+}
+
+
+static int ICACHE_FLASH_ATTR gmatch_aux (lua_State *L) {
+ MatchState ms;
+ size_t ls;
+ const char *s = lua_tolstring(L, lua_upvalueindex(1), &ls);
+ const char *p = lua_tostring(L, lua_upvalueindex(2));
+ const char *src;
+ ms.L = L;
+ ms.src_init = s;
+ ms.src_end = s+ls;
+ for (src = s + (size_t)lua_tointeger(L, lua_upvalueindex(3));
+ src <= ms.src_end;
+ src++) {
+ const char *e;
+ ms.level = 0;
+ if ((e = match(&ms, src, p)) != NULL) {
+ lua_Integer newstart = e-s;
+ if (e == src) newstart++; /* empty match? go at least one position */
+ lua_pushinteger(L, newstart);
+ lua_replace(L, lua_upvalueindex(3));
+ return push_captures(&ms, src, e);
+ }
+ }
+ return 0; /* not found */
+}
+
+
+static int ICACHE_FLASH_ATTR gmatch (lua_State *L) {
+ luaL_checkstring(L, 1);
+ luaL_checkstring(L, 2);
+ lua_settop(L, 2);
+ lua_pushinteger(L, 0);
+ lua_pushcclosure(L, gmatch_aux, 3);
+ return 1;
+}
+
+#if LUA_OPTIMIZE_MEMORY == 0 || !defined(LUA_COMPAT_GFIND)
+static int ICACHE_FLASH_ATTR gfind_nodef (lua_State *L) {
+ return luaL_error(L, LUA_QL("string.gfind") " was renamed to "
+ LUA_QL("string.gmatch"));
+}
+#endif
+
+static void ICACHE_FLASH_ATTR add_s (MatchState *ms, luaL_Buffer *b, const char *s,
+ const char *e) {
+ size_t l, i;
+ const char *news = lua_tolstring(ms->L, 3, &l);
+ for (i = 0; i < l; i++) {
+ if (news[i] != L_ESC)
+ luaL_addchar(b, news[i]);
+ else {
+ i++; /* skip ESC */
+ if (!isdigit(uchar(news[i])))
+ luaL_addchar(b, news[i]);
+ else if (news[i] == '0')
+ luaL_addlstring(b, s, e - s);
+ else {
+ push_onecapture(ms, news[i] - '1', s, e);
+ luaL_addvalue(b); /* add capture to accumulated result */
+ }
+ }
+ }
+}
+
+
+static void ICACHE_FLASH_ATTR add_value (MatchState *ms, luaL_Buffer *b, const char *s,
+ const char *e) {
+ lua_State *L = ms->L;
+ switch (lua_type(L, 3)) {
+ case LUA_TNUMBER:
+ case LUA_TSTRING: {
+ add_s(ms, b, s, e);
+ return;
+ }
+ case LUA_TFUNCTION:
+ case LUA_TLIGHTFUNCTION: {
+ int n;
+ lua_pushvalue(L, 3);
+ n = push_captures(ms, s, e);
+ lua_call(L, n, 1);
+ break;
+ }
+ case LUA_TTABLE: {
+ push_onecapture(ms, 0, s, e);
+ lua_gettable(L, 3);
+ break;
+ }
+ }
+ if (!lua_toboolean(L, -1)) { /* nil or false? */
+ lua_pop(L, 1);
+ lua_pushlstring(L, s, e - s); /* keep original text */
+ }
+ else if (!lua_isstring(L, -1))
+ luaL_error(L, "invalid replacement value (a %s)", luaL_typename(L, -1));
+ luaL_addvalue(b); /* add result to accumulator */
+}
+
+
+static int ICACHE_FLASH_ATTR str_gsub (lua_State *L) {
+ size_t srcl;
+ const char *src = luaL_checklstring(L, 1, &srcl);
+ const char *p = luaL_checkstring(L, 2);
+ int tr = lua_type(L, 3);
+ int max_s = luaL_optint(L, 4, srcl+1);
+ int anchor = (*p == '^') ? (p++, 1) : 0;
+ int n = 0;
+ MatchState ms;
+ luaL_Buffer b;
+ luaL_argcheck(L, tr == LUA_TNUMBER || tr == LUA_TSTRING ||
+ tr == LUA_TFUNCTION || tr == LUA_TTABLE ||
+ tr == LUA_TLIGHTFUNCTION, 3,
+ "string/function/table/lightfunction expected");
+ luaL_buffinit(L, &b);
+ ms.L = L;
+ ms.src_init = src;
+ ms.src_end = src+srcl;
+ while (n < max_s) {
+ const char *e;
+ ms.level = 0;
+ e = match(&ms, src, p);
+ if (e) {
+ n++;
+ add_value(&ms, &b, src, e);
+ }
+ if (e && e>src) /* non empty match? */
+ src = e; /* skip it */
+ else if (src < ms.src_end)
+ luaL_addchar(&b, *src++);
+ else break;
+ if (anchor) break;
+ }
+ luaL_addlstring(&b, src, ms.src_end-src);
+ luaL_pushresult(&b);
+ lua_pushinteger(L, n); /* number of substitutions */
+ return 2;
+}
+
+/* }====================================================== */
+
+
+/* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */
+/* was 512, modified to 128 for eLua */
+#define MAX_ITEM 128
+/* valid flags in a format specification */
+#define FLAGS "-+ #0"
+/*
+** maximum size of each format specification (such as '%-099.99d')
+** (+10 accounts for %99.99x plus margin of error)
+*/
+#define MAX_FORMAT (sizeof(FLAGS) + sizeof(LUA_INTFRMLEN) + 10)
+
+
+static void ICACHE_FLASH_ATTR addquoted (lua_State *L, luaL_Buffer *b, int arg) {
+ size_t l;
+ const char *s = luaL_checklstring(L, arg, &l);
+ luaL_addchar(b, '"');
+ while (l--) {
+ switch (*s) {
+ case '"': case '\\': case '\n': {
+ luaL_addchar(b, '\\');
+ luaL_addchar(b, *s);
+ break;
+ }
+ case '\r': {
+ luaL_addlstring(b, "\\r", 2);
+ break;
+ }
+ case '\0': {
+ luaL_addlstring(b, "\\000", 4);
+ break;
+ }
+ default: {
+ luaL_addchar(b, *s);
+ break;
+ }
+ }
+ s++;
+ }
+ luaL_addchar(b, '"');
+}
+
+static const char *ICACHE_FLASH_ATTR scanformat (lua_State *L, const char *strfrmt, char *form) {
+ const char *p = strfrmt;
+ while (*p != '\0' && c_strchr(FLAGS, *p) != NULL) p++; /* skip flags */
+ if ((size_t)(p - strfrmt) >= sizeof(FLAGS))
+ luaL_error(L, "invalid format (repeated flags)");
+ if (isdigit(uchar(*p))) p++; /* skip width */
+ if (isdigit(uchar(*p))) p++; /* (2 digits at most) */
+ if (*p == '.') {
+ p++;
+ if (isdigit(uchar(*p))) p++; /* skip precision */
+ if (isdigit(uchar(*p))) p++; /* (2 digits at most) */
+ }
+ if (isdigit(uchar(*p)))
+ luaL_error(L, "invalid format (width or precision too long)");
+ *(form++) = '%';
+ c_strncpy(form, strfrmt, p - strfrmt + 1);
+ form += p - strfrmt + 1;
+ *form = '\0';
+ return p;
+}
+
+
+static void ICACHE_FLASH_ATTR addintlen (char *form) {
+ size_t l = c_strlen(form);
+ char spec = form[l - 1];
+ c_strcpy(form + l - 1, LUA_INTFRMLEN);
+ form[l + sizeof(LUA_INTFRMLEN) - 2] = spec;
+ form[l + sizeof(LUA_INTFRMLEN) - 1] = '\0';
+}
+
+
+static int ICACHE_FLASH_ATTR str_format (lua_State *L) {
+ int top = lua_gettop(L);
+ int arg = 1;
+ size_t sfl;
+ const char *strfrmt = luaL_checklstring(L, arg, &sfl);
+ const char *strfrmt_end = strfrmt+sfl;
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ while (strfrmt < strfrmt_end) {
+ if (*strfrmt != L_ESC)
+ luaL_addchar(&b, *strfrmt++);
+ else if (*++strfrmt == L_ESC)
+ luaL_addchar(&b, *strfrmt++); /* %% */
+ else { /* format item */
+ char form[MAX_FORMAT]; /* to store the format (`%...') */
+ char buff[MAX_ITEM]; /* to store the formatted item */
+ if (++arg > top)
+ luaL_argerror(L, arg, "no value");
+ strfrmt = scanformat(L, strfrmt, form);
+ switch (*strfrmt++) {
+ case 'c': {
+ c_sprintf(buff, form, (int)luaL_checknumber(L, arg));
+ break;
+ }
+ case 'd': case 'i': {
+ addintlen(form);
+ c_sprintf(buff, form, (LUA_INTFRM_T)luaL_checknumber(L, arg));
+ break;
+ }
+ case 'o': case 'u': case 'x': case 'X': {
+ addintlen(form);
+ c_sprintf(buff, form, (unsigned LUA_INTFRM_T)luaL_checknumber(L, arg));
+ break;
+ }
+#if !defined LUA_NUMBER_INTEGRAL
+ case 'e': case 'E': case 'f':
+ case 'g': case 'G': {
+ c_sprintf(buff, form, (double)luaL_checknumber(L, arg));
+ break;
+ }
+#endif
+ case 'q': {
+ addquoted(L, &b, arg);
+ continue; /* skip the 'addsize' at the end */
+ }
+ case 's': {
+ size_t l;
+ const char *s = luaL_checklstring(L, arg, &l);
+ if (!c_strchr(form, '.') && l >= 100) {
+ /* no precision and string is too long to be formatted;
+ keep original string */
+ lua_pushvalue(L, arg);
+ luaL_addvalue(&b);
+ continue; /* skip the `addsize' at the end */
+ }
+ else {
+ c_sprintf(buff, form, s);
+ break;
+ }
+ }
+ default: { /* also treat cases `pnLlh' */
+ return luaL_error(L, "invalid option " LUA_QL("%%%c") " to "
+ LUA_QL("format"), *(strfrmt - 1));
+ }
+ }
+ luaL_addlstring(&b, buff, c_strlen(buff));
+ }
+ }
+ luaL_pushresult(&b);
+ return 1;
+}
+
+#define MIN_OPT_LEVEL 1
+#include "lrodefs.h"
+const LUA_REG_TYPE strlib[] = {
+ {LSTRKEY("byte"), LFUNCVAL(str_byte)},
+ {LSTRKEY("char"), LFUNCVAL(str_char)},
+ {LSTRKEY("dump"), LFUNCVAL(str_dump)},
+ {LSTRKEY("find"), LFUNCVAL(str_find)},
+ {LSTRKEY("format"), LFUNCVAL(str_format)},
+#if LUA_OPTIMIZE_MEMORY > 0 && defined(LUA_COMPAT_GFIND)
+ {LSTRKEY("gfind"), LFUNCVAL(gmatch)},
+#else
+ {LSTRKEY("gfind"), LFUNCVAL(gfind_nodef)},
+#endif
+ {LSTRKEY("gmatch"), LFUNCVAL(gmatch)},
+ {LSTRKEY("gsub"), LFUNCVAL(str_gsub)},
+ {LSTRKEY("len"), LFUNCVAL(str_len)},
+ {LSTRKEY("lower"), LFUNCVAL(str_lower)},
+ {LSTRKEY("match"), LFUNCVAL(str_match)},
+ {LSTRKEY("rep"), LFUNCVAL(str_rep)},
+ {LSTRKEY("reverse"), LFUNCVAL(str_reverse)},
+ {LSTRKEY("sub"), LFUNCVAL(str_sub)},
+ {LSTRKEY("upper"), LFUNCVAL(str_upper)},
+#if LUA_OPTIMIZE_MEMORY > 0
+ {LSTRKEY("__index"), LROVAL(strlib)},
+#endif
+ {LNILKEY, LNILVAL}
+};
+
+
+#if LUA_OPTIMIZE_MEMORY != 2
+static void ICACHE_FLASH_ATTR createmetatable (lua_State *L) {
+ lua_createtable(L, 0, 1); /* create metatable for strings */
+ lua_pushliteral(L, ""); /* dummy string */
+ lua_pushvalue(L, -2);
+ lua_setmetatable(L, -2); /* set string metatable */
+ lua_pop(L, 1); /* pop dummy string */
+ lua_pushvalue(L, -2); /* string library... */
+ lua_setfield(L, -2, "__index"); /* ...is the __index metamethod */
+ lua_pop(L, 1); /* pop metatable */
+}
+#endif
+
+/*
+** Open string library
+*/
+LUALIB_API int ICACHE_FLASH_ATTR luaopen_string (lua_State *L) {
+#if LUA_OPTIMIZE_MEMORY == 0
+ luaL_register(L, LUA_STRLIBNAME, strlib);
+#if defined(LUA_COMPAT_GFIND)
+ lua_getfield(L, -1, "gmatch");
+ lua_setfield(L, -2, "gfind");
+#endif
+ createmetatable(L);
+ return 1;
+#else
+ lua_pushliteral(L,"");
+ lua_pushrotable(L, (void*)strlib);
+ lua_setmetatable(L, -2);
+ lua_pop(L,1);
+ return 0;
+#endif
+}
+
diff --git a/app/lua/ltable.c b/app/lua/ltable.c
new file mode 100644
index 00000000..0c1162cd
--- /dev/null
+++ b/app/lua/ltable.c
@@ -0,0 +1,759 @@
+/*
+** $Id: ltable.c,v 2.32.1.2 2007/12/28 15:32:23 roberto Exp $
+** Lua tables (hash)
+** See Copyright Notice in lua.h
+*/
+
+
+/*
+** Implementation of tables (aka arrays, objects, or hash tables).
+** Tables keep its elements in two parts: an array part and a hash part.
+** Non-negative integer keys are all candidates to be kept in the array
+** part. The actual size of the array is the largest `n' such that at
+** least half the slots between 0 and n are in use.
+** Hash uses a mix of chained scatter table with Brent's variation.
+** A main invariant of these tables is that, if an element is not
+** in its main position (i.e. the `original' position that its hash gives
+** to it), then the colliding element is in its own main position.
+** Hence even when the load factor reaches 100%, performance remains good.
+*/
+
+#include "c_math.h"
+#include "c_string.h"
+
+#define ltable_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "ldebug.h"
+#include "ldo.h"
+#include "lgc.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+#include "ltable.h"
+#include "lrotable.h"
+
+/*
+** max size of array part is 2^MAXBITS
+*/
+#if LUAI_BITSINT > 26
+#define MAXBITS 26
+#else
+#define MAXBITS (LUAI_BITSINT-2)
+#endif
+
+#define MAXASIZE (1 << MAXBITS)
+
+
+#define hashpow2(t,n) (gnode(t, lmod((n), sizenode(t))))
+
+#define hashstr(t,str) hashpow2(t, (str)->tsv.hash)
+#define hashboolean(t,p) hashpow2(t, p)
+
+
+/*
+** for some types, it is better to avoid modulus by power of 2, as
+** they tend to have many 2 factors.
+*/
+#define hashmod(t,n) (gnode(t, ((n) % ((sizenode(t)-1)|1))))
+
+
+#define hashpointer(t,p) hashmod(t, IntPoint(p))
+
+
+/*
+** number of ints inside a lua_Number
+*/
+#define numints cast_int(sizeof(lua_Number)/sizeof(int))
+
+
+
+#define dummynode (&dummynode_)
+
+static const Node dummynode_ = {
+ {LUA_TVALUE_NIL}, /* value */
+ {LUA_TKEY_NIL} /* key */
+};
+
+
+/*
+** hash for lua_Numbers
+*/
+static Node *ICACHE_FLASH_ATTR hashnum (const Table *t, lua_Number n) {
+ unsigned int a[numints];
+ int i;
+ if (luai_numeq(n, 0)) /* avoid problems with -0 */
+ return gnode(t, 0);
+ c_memcpy(a, &n, sizeof(a));
+ for (i = 1; i < numints; i++) a[0] += a[i];
+ return hashmod(t, a[0]);
+}
+
+
+
+/*
+** returns the `main' position of an element in a table (that is, the index
+** of its hash value)
+*/
+static Node *ICACHE_FLASH_ATTR mainposition (const Table *t, const TValue *key) {
+ switch (ttype(key)) {
+ case LUA_TNUMBER:
+ return hashnum(t, nvalue(key));
+ case LUA_TSTRING:
+ return hashstr(t, rawtsvalue(key));
+ case LUA_TBOOLEAN:
+ return hashboolean(t, bvalue(key));
+ case LUA_TLIGHTUSERDATA:
+ case LUA_TROTABLE:
+ case LUA_TLIGHTFUNCTION:
+ return hashpointer(t, pvalue(key));
+ default:
+ return hashpointer(t, gcvalue(key));
+ }
+}
+
+
+/*
+** returns the index for `key' if `key' is an appropriate key to live in
+** the array part of the table, -1 otherwise.
+*/
+static int ICACHE_FLASH_ATTR arrayindex (const TValue *key) {
+ if (ttisnumber(key)) {
+ lua_Number n = nvalue(key);
+ int k;
+ lua_number2int(k, n);
+ if (luai_numeq(cast_num(k), n))
+ return k;
+ }
+ return -1; /* `key' did not match some condition */
+}
+
+
+/*
+** returns the index of a `key' for table traversals. First goes all
+** elements in the array part, then elements in the hash part. The
+** beginning of a traversal is signalled by -1.
+*/
+static int ICACHE_FLASH_ATTR findindex (lua_State *L, Table *t, StkId key) {
+ int i;
+ if (ttisnil(key)) return -1; /* first iteration */
+ i = arrayindex(key);
+ if (0 < i && i <= t->sizearray) /* is `key' inside array part? */
+ return i-1; /* yes; that's the index (corrected to C) */
+ else {
+ Node *n = mainposition(t, key);
+ do { /* check whether `key' is somewhere in the chain */
+ /* key may be dead already, but it is ok to use it in `next' */
+ if (luaO_rawequalObj(key2tval(n), key) ||
+ (ttype(gkey(n)) == LUA_TDEADKEY && iscollectable(key) &&
+ gcvalue(gkey(n)) == gcvalue(key))) {
+ i = cast_int(n - gnode(t, 0)); /* key index in hash table */
+ /* hash elements are numbered after array ones */
+ return i + t->sizearray;
+ }
+ else n = gnext(n);
+ } while (n);
+ luaG_runerror(L, "invalid key to " LUA_QL("next")); /* key not found */
+ return 0; /* to avoid warnings */
+ }
+}
+
+
+int ICACHE_FLASH_ATTR luaH_next (lua_State *L, Table *t, StkId key) {
+ int i = findindex(L, t, key); /* find original element */
+ for (i++; i < t->sizearray; i++) { /* try first array part */
+ if (!ttisnil(&t->array[i])) { /* a non-nil value? */
+ setnvalue(key, cast_num(i+1));
+ setobj2s(L, key+1, &t->array[i]);
+ return 1;
+ }
+ }
+ for (i -= t->sizearray; i < sizenode(t); i++) { /* then hash part */
+ if (!ttisnil(gval(gnode(t, i)))) { /* a non-nil value? */
+ setobj2s(L, key, key2tval(gnode(t, i)));
+ setobj2s(L, key+1, gval(gnode(t, i)));
+ return 1;
+ }
+ }
+ return 0; /* no more elements */
+}
+
+
+int ICACHE_FLASH_ATTR luaH_next_ro (lua_State *L, void *t, StkId key) {
+ luaR_next(L, t, key, key+1);
+ return ttisnil(key) ? 0 : 1;
+}
+
+
+/*
+** {=============================================================
+** Rehash
+** ==============================================================
+*/
+
+
+static int ICACHE_FLASH_ATTR computesizes (int nums[], int *narray) {
+ int i;
+ int twotoi; /* 2^i */
+ int a = 0; /* number of elements smaller than 2^i */
+ int na = 0; /* number of elements to go to array part */
+ int n = 0; /* optimal size for array part */
+ for (i = 0, twotoi = 1; twotoi/2 < *narray; i++, twotoi *= 2) {
+ if (nums[i] > 0) {
+ a += nums[i];
+ if (a > twotoi/2) { /* more than half elements present? */
+ n = twotoi; /* optimal size (till now) */
+ na = a; /* all elements smaller than n will go to array part */
+ }
+ }
+ if (a == *narray) break; /* all elements already counted */
+ }
+ *narray = n;
+ lua_assert(*narray/2 <= na && na <= *narray);
+ return na;
+}
+
+
+static int ICACHE_FLASH_ATTR countint (const TValue *key, int *nums) {
+ int k = arrayindex(key);
+ if (0 < k && k <= MAXASIZE) { /* is `key' an appropriate array index? */
+ nums[ceillog2(k)]++; /* count as such */
+ return 1;
+ }
+ else
+ return 0;
+}
+
+
+static int ICACHE_FLASH_ATTR numusearray (const Table *t, int *nums) {
+ int lg;
+ int ttlg; /* 2^lg */
+ int ause = 0; /* summation of `nums' */
+ int i = 1; /* count to traverse all array keys */
+ for (lg=0, ttlg=1; lg<=MAXBITS; lg++, ttlg*=2) { /* for each slice */
+ int lc = 0; /* counter */
+ int lim = ttlg;
+ if (lim > t->sizearray) {
+ lim = t->sizearray; /* adjust upper limit */
+ if (i > lim)
+ break; /* no more elements to count */
+ }
+ /* count elements in range (2^(lg-1), 2^lg] */
+ for (; i <= lim; i++) {
+ if (!ttisnil(&t->array[i-1]))
+ lc++;
+ }
+ nums[lg] += lc;
+ ause += lc;
+ }
+ return ause;
+}
+
+
+static int ICACHE_FLASH_ATTR numusehash (const Table *t, int *nums, int *pnasize) {
+ int totaluse = 0; /* total number of elements */
+ int ause = 0; /* summation of `nums' */
+ int i = sizenode(t);
+ while (i--) {
+ Node *n = &t->node[i];
+ if (!ttisnil(gval(n))) {
+ ause += countint(key2tval(n), nums);
+ totaluse++;
+ }
+ }
+ *pnasize += ause;
+ return totaluse;
+}
+
+
+static void ICACHE_FLASH_ATTR setarrayvector (lua_State *L, Table *t, int size) {
+ int i;
+ luaM_reallocvector(L, t->array, t->sizearray, size, TValue);
+ for (i=t->sizearray; iarray[i]);
+ t->sizearray = size;
+}
+
+
+static Node *ICACHE_FLASH_ATTR getfreepos (Table *t) {
+ while (t->lastfree-- > t->node) {
+ if (ttisnil(gkey(t->lastfree)))
+ return t->lastfree;
+ }
+ return NULL; /* could not find a free place */
+}
+
+
+static void ICACHE_FLASH_ATTR resizenodevector (lua_State *L, Table *t, int oldsize, int newsize) {
+ int lsize;
+ if (newsize == 0) { /* no elements to hash part? */
+ t->node = cast(Node *, dummynode); /* use common `dummynode' */
+ lsize = 0;
+ }
+ else {
+ Node *node = t->node;
+ int i;
+ lsize = ceillog2(newsize);
+ if (lsize > MAXBITS)
+ luaG_runerror(L, "table overflow");
+ newsize = twoto(lsize);
+ if (node == dummynode) {
+ oldsize = 0;
+ node = NULL; /* don't try to realloc `dummynode' pointer. */
+ }
+ luaM_reallocvector(L, node, oldsize, newsize, Node);
+ t->node = node;
+ for (i=oldsize; ilsizenode = cast_byte(lsize);
+ t->lastfree = gnode(t, newsize); /* reset lastfree to end of table. */
+}
+
+
+static Node *ICACHE_FLASH_ATTR find_prev_node(Node *mp, Node *next) {
+ Node *prev = mp;
+ while (prev != NULL && gnext(prev) != next) prev = gnext(prev);
+ return prev;
+}
+
+
+/*
+** move a node from it's old position to it's new position during a rehash;
+** first, check whether the moving node's main position is free. If not, check whether
+** colliding node is in its main position or not: if it is not, move colliding
+** node to an empty place and put moving node in its main position; otherwise
+** (colliding node is in its main position), moving node goes to an empty position.
+*/
+static int ICACHE_FLASH_ATTR move_node (lua_State *L, Table *t, Node *node) {
+ Node *mp = mainposition(t, key2tval(node));
+ /* if node is in it's main position, don't need to move node. */
+ if (mp == node) return 1;
+ /* if node is in it's main position's chain, don't need to move node. */
+ if (find_prev_node(mp, node) != NULL) return 1;
+ /* is main position is free? */
+ if (!ttisnil(gval(mp)) || mp == dummynode) {
+ /* no; move main position node if it is out of its main position */
+ Node *othermp;
+ othermp = mainposition(t, key2tval(mp));
+ if (othermp != mp) { /* is colliding node out of its main position? */
+ /* yes; swap colliding node with the node that is being moved. */
+ Node *prev;
+ Node tmp;
+ tmp = *node;
+ prev = find_prev_node(othermp, mp); /* find previous */
+ if (prev != NULL) gnext(prev) = node; /* redo the chain with `n' in place of `mp' */
+ *node = *mp; /* copy colliding node into free pos. (mp->next also goes) */
+ *mp = tmp;
+ return (prev != NULL) ? 1 : 0; /* is colliding node part of its main position chain? */
+ }
+ else { /* colliding node is in its own main position */
+ /* add node to main position's chain. */
+ gnext(node) = gnext(mp); /* chain new position */
+ gnext(mp) = node;
+ }
+ }
+ else { /* main position is free, move node */
+ *mp = *node;
+ gnext(node) = NULL;
+ setnilvalue(gkey(node));
+ setnilvalue(gval(node));
+ }
+ return 1;
+}
+
+
+static int ICACHE_FLASH_ATTR move_number (lua_State *L, Table *t, Node *node) {
+ int key;
+ lua_Number n = nvalue(key2tval(node));
+ lua_number2int(key, n);
+ if (luai_numeq(cast_num(key), nvalue(key2tval(node)))) {/* index is int? */
+ /* (1 <= key && key <= t->sizearray) */
+ if (cast(unsigned int, key-1) < cast(unsigned int, t->sizearray)) {
+ setobjt2t(L, &t->array[key-1], gval(node));
+ setnilvalue(gkey(node));
+ setnilvalue(gval(node));
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+static void ICACHE_FLASH_ATTR resize_hashpart (lua_State *L, Table *t, int nhsize) {
+ int i;
+ int lsize=0;
+ int oldhsize = (t->node != dummynode) ? twoto(t->lsizenode) : 0;
+ if (nhsize > 0) { /* round new hashpart size up to next power of two. */
+ lsize=ceillog2(nhsize);
+ if (lsize > MAXBITS)
+ luaG_runerror(L, "table overflow");
+ }
+ nhsize = twoto(lsize);
+ /* grow hash part to new size. */
+ if (oldhsize < nhsize)
+ resizenodevector(L, t, oldhsize, nhsize);
+ else { /* hash part might be shrinking */
+ if (nhsize > 0) {
+ t->lsizenode = cast_byte(lsize);
+ t->lastfree = gnode(t, nhsize); /* reset lastfree back to end of table. */
+ }
+ else { /* new hashpart size is zero. */
+ resizenodevector(L, t, oldhsize, nhsize);
+ return;
+ }
+ }
+ /* break old chains, try moving int keys to array part and compact keys into new hashpart */
+ for (i = 0; i < oldhsize; i++) {
+ Node *old = gnode(t, i);
+ gnext(old) = NULL;
+ if (ttisnil(gval(old))) { /* clear nodes with nil values. */
+ setnilvalue(gkey(old));
+ continue;
+ }
+ if (ttisnumber(key2tval(old))) { /* try moving the int keys into array part. */
+ if(move_number(L, t, old))
+ continue;
+ }
+ if (i >= nhsize) { /* move all valid keys to indices < nhsize. */
+ Node *n = getfreepos(t); /* get a free place */
+ lua_assert(n != dummynode && n != NULL);
+ *n = *old;
+ }
+ }
+ /* shrink hash part */
+ if (oldhsize > nhsize)
+ resizenodevector(L, t, oldhsize, nhsize);
+ /* move nodes to their new mainposition and re-create node chains */
+ for (i = 0; i < nhsize; i++) {
+ Node *curr = gnode(t, i);
+ if (!ttisnil(gval(curr)))
+ while (move_node(L, t, curr) == 0);
+ }
+}
+
+
+static void ICACHE_FLASH_ATTR resize (lua_State *L, Table *t, int nasize, int nhsize) {
+ int i;
+ int oldasize = t->sizearray;
+ if (nasize > oldasize) /* array part must grow? */
+ setarrayvector(L, t, nasize);
+ resize_hashpart(L, t, nhsize);
+ if (nasize < oldasize) { /* array part must shrink? */
+ t->sizearray = nasize;
+ /* re-insert elements from vanishing slice */
+ for (i=nasize; iarray[i]))
+ setobjt2t(L, luaH_setnum(L, t, i+1), &t->array[i]);
+ }
+ /* shrink array */
+ luaM_reallocvector(L, t->array, oldasize, nasize, TValue);
+ }
+}
+
+
+void ICACHE_FLASH_ATTR luaH_resizearray (lua_State *L, Table *t, int nasize) {
+ int nsize = (t->node == dummynode) ? 0 : sizenode(t);
+ resize(L, t, nasize, nsize);
+}
+
+
+static void ICACHE_FLASH_ATTR rehash (lua_State *L, Table *t, const TValue *ek) {
+ int nasize, na;
+ int nums[MAXBITS+1]; /* nums[i] = number of keys between 2^(i-1) and 2^i */
+ int i;
+ int totaluse;
+ for (i=0; i<=MAXBITS; i++) nums[i] = 0; /* reset counts */
+ nasize = numusearray(t, nums); /* count keys in array part */
+ totaluse = nasize; /* all those keys are integer keys */
+ totaluse += numusehash(t, nums, &nasize); /* count keys in hash part */
+ /* count extra key */
+ nasize += countint(ek, nums);
+ totaluse++;
+ /* compute new size for array part */
+ na = computesizes(nums, &nasize);
+ /* resize the table to new computed sizes */
+ resize(L, t, nasize, totaluse - na);
+}
+
+
+
+/*
+** }=============================================================
+*/
+
+
+Table *ICACHE_FLASH_ATTR luaH_new (lua_State *L, int narray, int nhash) {
+ Table *t = luaM_new(L, Table);
+ luaC_link(L, obj2gco(t), LUA_TTABLE);
+ sethvalue2s(L, L->top, t); /* put table on stack */
+ incr_top(L);
+ t->metatable = NULL;
+ t->flags = cast_byte(~0);
+ /* temporary values (kept only if some malloc fails) */
+ t->array = NULL;
+ t->sizearray = 0;
+ t->lsizenode = 0;
+ t->node = cast(Node *, dummynode);
+ setarrayvector(L, t, narray);
+ resizenodevector(L, t, 0, nhash);
+ L->top--; /* remove table from stack */
+ return t;
+}
+
+
+void ICACHE_FLASH_ATTR luaH_free (lua_State *L, Table *t) {
+ if (t->node != dummynode)
+ luaM_freearray(L, t->node, sizenode(t), Node);
+ luaM_freearray(L, t->array, t->sizearray, TValue);
+ luaM_free(L, t);
+}
+
+
+
+/*
+** inserts a new key into a hash table; first, check whether key's main
+** position is free. If not, check whether colliding node is in its main
+** position or not: if it is not, move colliding node to an empty place and
+** put new key in its main position; otherwise (colliding node is in its main
+** position), new key goes to an empty position.
+*/
+static TValue *ICACHE_FLASH_ATTR newkey (lua_State *L, Table *t, const TValue *key) {
+ Node *mp = mainposition(t, key);
+ if (!ttisnil(gval(mp)) || mp == dummynode) {
+ Node *othern;
+ Node *n = getfreepos(t); /* get a free place */
+ if (n == NULL) { /* cannot find a free place? */
+ rehash(L, t, key); /* grow table */
+ return luaH_set(L, t, key); /* re-insert key into grown table */
+ }
+ lua_assert(n != dummynode);
+ othern = mainposition(t, key2tval(mp));
+ if (othern != mp) { /* is colliding node out of its main position? */
+ /* yes; move colliding node into free position */
+ while (gnext(othern) != mp) othern = gnext(othern); /* find previous */
+ gnext(othern) = n; /* redo the chain with `n' in place of `mp' */
+ *n = *mp; /* copy colliding node into free pos. (mp->next also goes) */
+ gnext(mp) = NULL; /* now `mp' is free */
+ setnilvalue(gval(mp));
+ }
+ else { /* colliding node is in its own main position */
+ /* new node will go into free position */
+ gnext(n) = gnext(mp); /* chain new position */
+ gnext(mp) = n;
+ mp = n;
+ }
+ }
+ setobj2t(L, gkey(mp), key);
+ luaC_barriert(L, t, key);
+ lua_assert(ttisnil(gval(mp)));
+ return gval(mp);
+}
+
+
+/*
+** search function for integers
+*/
+const TValue *ICACHE_FLASH_ATTR luaH_getnum (Table *t, int key) {
+ /* (1 <= key && key <= t->sizearray) */
+ if (cast(unsigned int, key-1) < cast(unsigned int, t->sizearray))
+ return &t->array[key-1];
+ else {
+ lua_Number nk = cast_num(key);
+ Node *n = hashnum(t, nk);
+ do { /* check whether `key' is somewhere in the chain */
+ if (ttisnumber(gkey(n)) && luai_numeq(nvalue(gkey(n)), nk))
+ return gval(n); /* that's it */
+ else n = gnext(n);
+ } while (n);
+ return luaO_nilobject;
+ }
+}
+
+/* same thing for rotables */
+const TValue *ICACHE_FLASH_ATTR luaH_getnum_ro (void *t, int key) {
+ const TValue *res = luaR_findentry(t, NULL, key, NULL);
+ return res ? res : luaO_nilobject;
+}
+
+
+/*
+** search function for strings
+*/
+const TValue *ICACHE_FLASH_ATTR luaH_getstr (Table *t, TString *key) {
+ Node *n = hashstr(t, key);
+ do { /* check whether `key' is somewhere in the chain */
+ if (ttisstring(gkey(n)) && rawtsvalue(gkey(n)) == key)
+ return gval(n); /* that's it */
+ else n = gnext(n);
+ } while (n);
+ return luaO_nilobject;
+}
+
+/* same thing for rotables */
+const TValue *ICACHE_FLASH_ATTR luaH_getstr_ro (void *t, TString *key) {
+ char keyname[LUA_MAX_ROTABLE_NAME + 1];
+ const TValue *res;
+ if (!t)
+ return luaO_nilobject;
+ luaR_getcstr(keyname, key, LUA_MAX_ROTABLE_NAME);
+ res = luaR_findentry(t, keyname, 0, NULL);
+ return res ? res : luaO_nilobject;
+}
+
+
+/*
+** main search function
+*/
+const TValue *ICACHE_FLASH_ATTR luaH_get (Table *t, const TValue *key) {
+ switch (ttype(key)) {
+ case LUA_TNIL: return luaO_nilobject;
+ case LUA_TSTRING: return luaH_getstr(t, rawtsvalue(key));
+ case LUA_TNUMBER: {
+ int k;
+ lua_Number n = nvalue(key);
+ lua_number2int(k, n);
+ if (luai_numeq(cast_num(k), nvalue(key))) /* index is int? */
+ return luaH_getnum(t, k); /* use specialized version */
+ /* else go through */
+ }
+ default: {
+ Node *n = mainposition(t, key);
+ do { /* check whether `key' is somewhere in the chain */
+ if (luaO_rawequalObj(key2tval(n), key))
+ return gval(n); /* that's it */
+ else n = gnext(n);
+ } while (n);
+ return luaO_nilobject;
+ }
+ }
+}
+
+/* same thing for rotables */
+const TValue *ICACHE_FLASH_ATTR luaH_get_ro (void *t, const TValue *key) {
+ switch (ttype(key)) {
+ case LUA_TNIL: return luaO_nilobject;
+ case LUA_TSTRING: return luaH_getstr_ro(t, rawtsvalue(key));
+ case LUA_TNUMBER: {
+ int k;
+ lua_Number n = nvalue(key);
+ lua_number2int(k, n);
+ if (luai_numeq(cast_num(k), nvalue(key))) /* index is int? */
+ return luaH_getnum_ro(t, k); /* use specialized version */
+ /* else go through */
+ }
+ default: {
+ return luaO_nilobject;
+ }
+ }
+}
+
+
+TValue *ICACHE_FLASH_ATTR luaH_set (lua_State *L, Table *t, const TValue *key) {
+ const TValue *p = luaH_get(t, key);
+ t->flags = 0;
+ if (p != luaO_nilobject)
+ return cast(TValue *, p);
+ else {
+ if (ttisnil(key)) luaG_runerror(L, "table index is nil");
+ else if (ttisnumber(key) && luai_numisnan(nvalue(key)))
+ luaG_runerror(L, "table index is NaN");
+ return newkey(L, t, key);
+ }
+}
+
+
+TValue *ICACHE_FLASH_ATTR luaH_setnum (lua_State *L, Table *t, int key) {
+ const TValue *p = luaH_getnum(t, key);
+ if (p != luaO_nilobject)
+ return cast(TValue *, p);
+ else {
+ TValue k;
+ setnvalue(&k, cast_num(key));
+ return newkey(L, t, &k);
+ }
+}
+
+
+TValue *ICACHE_FLASH_ATTR luaH_setstr (lua_State *L, Table *t, TString *key) {
+ const TValue *p = luaH_getstr(t, key);
+ if (p != luaO_nilobject)
+ return cast(TValue *, p);
+ else {
+ TValue k;
+ setsvalue(L, &k, key);
+ return newkey(L, t, &k);
+ }
+}
+
+
+static int ICACHE_FLASH_ATTR unbound_search (Table *t, unsigned int j) {
+ unsigned int i = j; /* i is zero or a present index */
+ j++;
+ /* find `i' and `j' such that i is present and j is not */
+ while (!ttisnil(luaH_getnum(t, j))) {
+ i = j;
+ j *= 2;
+ if (j > cast(unsigned int, MAX_INT)) { /* overflow? */
+ /* table was built with bad purposes: resort to linear search */
+ i = 1;
+ while (!ttisnil(luaH_getnum(t, i))) i++;
+ return i - 1;
+ }
+ }
+ /* now do a binary search between them */
+ while (j - i > 1) {
+ unsigned int m = (i+j)/2;
+ if (ttisnil(luaH_getnum(t, m))) j = m;
+ else i = m;
+ }
+ return i;
+}
+
+
+/*
+** Try to find a boundary in table `t'. A `boundary' is an integer index
+** such that t[i] is non-nil and t[i+1] is nil (and 0 if t[1] is nil).
+*/
+int ICACHE_FLASH_ATTR luaH_getn (Table *t) {
+ unsigned int j = t->sizearray;
+ if (j > 0 && ttisnil(&t->array[j - 1])) {
+ /* there is a boundary in the array part: (binary) search for it */
+ unsigned int i = 0;
+ while (j - i > 1) {
+ unsigned int m = (i+j)/2;
+ if (ttisnil(&t->array[m - 1])) j = m;
+ else i = m;
+ }
+ return i;
+ }
+ /* else must find a boundary in hash part */
+ else if (t->node == dummynode) /* hash part is empty? */
+ return j; /* that is easy... */
+ else return unbound_search(t, j);
+}
+
+/* same thing for rotables */
+int ICACHE_FLASH_ATTR luaH_getn_ro (void *t) {
+ int i = 1, len=0;
+
+ while(luaR_findentry(t, NULL, i ++, NULL))
+ len ++;
+ return len;
+}
+
+#if defined(LUA_DEBUG)
+
+Node *ICACHE_FLASH_ATTR luaH_mainposition (const Table *t, const TValue *key) {
+ return mainposition(t, key);
+}
+
+int ICACHE_FLASH_ATTR luaH_isdummy (Node *n) { return n == dummynode; }
+
+#endif
diff --git a/app/lua/ltable.h b/app/lua/ltable.h
new file mode 100644
index 00000000..4835f2c3
--- /dev/null
+++ b/app/lua/ltable.h
@@ -0,0 +1,44 @@
+/*
+** $Id: ltable.h,v 2.10.1.1 2007/12/27 13:02:25 roberto Exp $
+** Lua tables (hash)
+** See Copyright Notice in lua.h
+*/
+
+#ifndef ltable_h
+#define ltable_h
+
+#include "lobject.h"
+
+
+#define gnode(t,i) (&(t)->node[i])
+#define gkey(n) (&(n)->i_key.tvk)
+#define gval(n) (&(n)->i_val)
+#define gnext(n) ((n)->i_key.nk.next)
+
+#define key2tval(n) (&(n)->i_key.tvk)
+
+
+LUAI_FUNC const TValue *luaH_getnum (Table *t, int key);
+LUAI_FUNC const TValue *luaH_getnum_ro (void *t, int key);
+LUAI_FUNC TValue *luaH_setnum (lua_State *L, Table *t, int key);
+LUAI_FUNC const TValue *luaH_getstr (Table *t, TString *key);
+LUAI_FUNC const TValue *luaH_getstr_ro (void *t, TString *key);
+LUAI_FUNC TValue *luaH_setstr (lua_State *L, Table *t, TString *key);
+LUAI_FUNC const TValue *luaH_get (Table *t, const TValue *key);
+LUAI_FUNC const TValue *luaH_get_ro (void *t, const TValue *key);
+LUAI_FUNC TValue *luaH_set (lua_State *L, Table *t, const TValue *key);
+LUAI_FUNC Table *luaH_new (lua_State *L, int narray, int lnhash);
+LUAI_FUNC void luaH_resizearray (lua_State *L, Table *t, int nasize);
+LUAI_FUNC void luaH_free (lua_State *L, Table *t);
+LUAI_FUNC int luaH_next (lua_State *L, Table *t, StkId key);
+LUAI_FUNC int luaH_next_ro (lua_State *L, void *t, StkId key);
+LUAI_FUNC int luaH_getn (Table *t);
+LUAI_FUNC int luaH_getn_ro (void *t);
+
+#if defined(LUA_DEBUG)
+LUAI_FUNC Node *luaH_mainposition (const Table *t, const TValue *key);
+LUAI_FUNC int luaH_isdummy (Node *n);
+#endif
+
+
+#endif
diff --git a/app/lua/ltablib.c b/app/lua/ltablib.c
new file mode 100644
index 00000000..08e0d498
--- /dev/null
+++ b/app/lua/ltablib.c
@@ -0,0 +1,287 @@
+/*
+** $Id: ltablib.c,v 1.38.1.3 2008/02/14 16:46:58 roberto Exp $
+** Library for Table Manipulation
+** See Copyright Notice in lua.h
+*/
+
+
+#include "c_stddef.h"
+
+#define ltablib_c
+#define LUA_LIB
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+#include "lrotable.h"
+
+
+#define aux_getn(L,n) (luaL_checktype(L, n, LUA_TTABLE), luaL_getn(L, n))
+
+
+static int ICACHE_FLASH_ATTR foreachi (lua_State *L) {
+ int i;
+ int n = aux_getn(L, 1);
+ luaL_checkanyfunction(L, 2);
+ for (i=1; i <= n; i++) {
+ lua_pushvalue(L, 2); /* function */
+ lua_pushinteger(L, i); /* 1st argument */
+ lua_rawgeti(L, 1, i); /* 2nd argument */
+ lua_call(L, 2, 1);
+ if (!lua_isnil(L, -1))
+ return 1;
+ lua_pop(L, 1); /* remove nil result */
+ }
+ return 0;
+}
+
+
+static int ICACHE_FLASH_ATTR foreach (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TTABLE);
+ luaL_checkanyfunction(L, 2);
+ lua_pushnil(L); /* first key */
+ while (lua_next(L, 1)) {
+ lua_pushvalue(L, 2); /* function */
+ lua_pushvalue(L, -3); /* key */
+ lua_pushvalue(L, -3); /* value */
+ lua_call(L, 2, 1);
+ if (!lua_isnil(L, -1))
+ return 1;
+ lua_pop(L, 2); /* remove value and result */
+ }
+ return 0;
+}
+
+
+static int ICACHE_FLASH_ATTR maxn (lua_State *L) {
+ lua_Number max = 0;
+ luaL_checktype(L, 1, LUA_TTABLE);
+ lua_pushnil(L); /* first key */
+ while (lua_next(L, 1)) {
+ lua_pop(L, 1); /* remove value */
+ if (lua_type(L, -1) == LUA_TNUMBER) {
+ lua_Number v = lua_tonumber(L, -1);
+ if (v > max) max = v;
+ }
+ }
+ lua_pushnumber(L, max);
+ return 1;
+}
+
+
+static int ICACHE_FLASH_ATTR getn (lua_State *L) {
+ lua_pushinteger(L, aux_getn(L, 1));
+ return 1;
+}
+
+
+static int ICACHE_FLASH_ATTR setn (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TTABLE);
+#ifndef luaL_setn
+ luaL_setn(L, 1, luaL_checkint(L, 2));
+#else
+ luaL_error(L, LUA_QL("setn") " is obsolete");
+#endif
+ lua_pushvalue(L, 1);
+ return 1;
+}
+
+
+static int ICACHE_FLASH_ATTR tinsert (lua_State *L) {
+ int e = aux_getn(L, 1) + 1; /* first empty element */
+ int pos; /* where to insert new element */
+ switch (lua_gettop(L)) {
+ case 2: { /* called with only 2 arguments */
+ pos = e; /* insert new element at the end */
+ break;
+ }
+ case 3: {
+ int i;
+ pos = luaL_checkint(L, 2); /* 2nd argument is the position */
+ if (pos > e) e = pos; /* `grow' array if necessary */
+ for (i = e; i > pos; i--) { /* move up elements */
+ lua_rawgeti(L, 1, i-1);
+ lua_rawseti(L, 1, i); /* t[i] = t[i-1] */
+ }
+ break;
+ }
+ default: {
+ return luaL_error(L, "wrong number of arguments to " LUA_QL("insert"));
+ }
+ }
+ luaL_setn(L, 1, e); /* new size */
+ lua_rawseti(L, 1, pos); /* t[pos] = v */
+ return 0;
+}
+
+
+static int ICACHE_FLASH_ATTR tremove (lua_State *L) {
+ int e = aux_getn(L, 1);
+ int pos = luaL_optint(L, 2, e);
+ if (!(1 <= pos && pos <= e)) /* position is outside bounds? */
+ return 0; /* nothing to remove */
+ luaL_setn(L, 1, e - 1); /* t.n = n-1 */
+ lua_rawgeti(L, 1, pos); /* result = t[pos] */
+ for ( ;pos= P */
+ while (lua_rawgeti(L, 1, ++i), sort_comp(L, -1, -2)) {
+ if (i>u) luaL_error(L, "invalid order function for sorting");
+ lua_pop(L, 1); /* remove a[i] */
+ }
+ /* repeat --j until a[j] <= P */
+ while (lua_rawgeti(L, 1, --j), sort_comp(L, -3, -1)) {
+ if (jtmname[i] = luaS_new(L, luaT_eventname[i]);
+ luaS_fix(G(L)->tmname[i]); /* never collect these names */
+ }
+}
+
+
+/*
+** function to be used with macro "fasttm": optimized for absence of
+** tag methods
+*/
+const TValue *ICACHE_FLASH_ATTR luaT_gettm (Table *events, TMS event, TString *ename) {
+ const TValue *tm = luaR_isrotable(events) ? luaH_getstr_ro(events, ename) : luaH_getstr(events, ename);
+ lua_assert(event <= TM_EQ);
+ if (ttisnil(tm)) { /* no tag method? */
+ if (!luaR_isrotable(events))
+ events->flags |= cast_byte(1u<metatable;
+ break;
+ case LUA_TROTABLE:
+ mt = (Table*)luaR_getmeta(rvalue(o));
+ break;
+ case LUA_TUSERDATA:
+ mt = uvalue(o)->metatable;
+ break;
+ default:
+ mt = G(L)->mt[ttype(o)];
+ }
+ if (!mt)
+ return luaO_nilobject;
+ else if (luaR_isrotable(mt))
+ return luaH_getstr_ro(mt, G(L)->tmname[event]);
+ else
+ return luaH_getstr(mt, G(L)->tmname[event]);
+}
diff --git a/app/lua/ltm.h b/app/lua/ltm.h
new file mode 100644
index 00000000..662fa2ae
--- /dev/null
+++ b/app/lua/ltm.h
@@ -0,0 +1,54 @@
+/*
+** $Id: ltm.h,v 2.6.1.1 2007/12/27 13:02:25 roberto Exp $
+** Tag methods
+** See Copyright Notice in lua.h
+*/
+
+#ifndef ltm_h
+#define ltm_h
+
+
+#include "lobject.h"
+
+
+/*
+* WARNING: if you change the order of this enumeration,
+* grep "ORDER TM"
+*/
+typedef enum {
+ TM_INDEX,
+ TM_NEWINDEX,
+ TM_GC,
+ TM_MODE,
+ TM_EQ, /* last tag method with `fast' access */
+ TM_ADD,
+ TM_SUB,
+ TM_MUL,
+ TM_DIV,
+ TM_MOD,
+ TM_POW,
+ TM_UNM,
+ TM_LEN,
+ TM_LT,
+ TM_LE,
+ TM_CONCAT,
+ TM_CALL,
+ TM_N /* number of elements in the enum */
+} TMS;
+
+
+
+#define gfasttm(g,et,e) ((et) == NULL ? NULL : \
+ !luaR_isrotable(et) && ((et)->flags & (1u<<(e))) ? NULL : luaT_gettm(et, e, (g)->tmname[e]))
+
+#define fasttm(l,et,e) gfasttm(G(l), et, e)
+
+LUAI_DATA const char *const luaT_typenames[];
+
+
+LUAI_FUNC const TValue *luaT_gettm (Table *events, TMS event, TString *ename);
+LUAI_FUNC const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o,
+ TMS event);
+LUAI_FUNC void luaT_init (lua_State *L);
+
+#endif
diff --git a/app/lua/lua.c b/app/lua/lua.c
new file mode 100644
index 00000000..6eab7724
--- /dev/null
+++ b/app/lua/lua.c
@@ -0,0 +1,699 @@
+/*
+** $Id: lua.c,v 1.160.1.2 2007/12/28 15:32:23 roberto Exp $
+** Lua stand-alone interpreter
+** See Copyright Notice in lua.h
+*/
+
+
+// #include "c_signal.h"
+#include "c_stdio.h"
+#include "c_stdlib.h"
+#include "c_string.h"
+#include "flash_fs.h"
+
+#define lua_c
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+#include "legc.h"
+
+#include "os_type.h"
+
+os_timer_t lua_timer;
+LOCAL os_timer_t readline_timer;
+
+lua_State *globalL = NULL;
+
+lua_Load gLoad;
+
+static const char *progname = LUA_PROGNAME;
+
+static int key_press_count = 0;
+int led_high_count = LED_HIGH_COUNT_DEFAULT;
+int led_low_count = LED_LOW_COUNT_DEFAULT;
+static int led_count = 0;
+#if 0
+static void ICACHE_FLASH_ATTR lstop (lua_State *L, lua_Debug *ar) {
+ (void)ar; /* unused arg. */
+ lua_sethook(L, NULL, 0, 0);
+ luaL_error(L, "interrupted!");
+}
+
+
+static void ICACHE_FLASH_ATTR laction (int i) {
+ // signal(i, SIG_DFL);
+ /* if another SIGINT happens before lstop,
+ terminate process (default action) */
+ lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1);
+}
+
+
+static void ICACHE_FLASH_ATTR print_usage (void) {
+#if defined(LUA_USE_STDIO)
+ c_fprintf(c_stderr,
+#else
+ luai_writestringerror(
+#endif
+ "usage: %s [options] [script [args]].\n"
+ "Available options are:\n"
+ " -e stat execute string " LUA_QL("stat") "\n"
+ " -l name require library " LUA_QL("name") "\n"
+ " -m limit set memory limit. (units are in Kbytes)\n"
+ " -i enter interactive mode after executing " LUA_QL("script") "\n"
+ " -v show version information\n"
+ " -- stop handling options\n"
+ " - execute stdin and stop handling options\n"
+ ,
+ progname);
+#if defined(LUA_USE_STDIO)
+ c_fflush(c_stderr);
+#endif
+}
+#endif
+
+static void ICACHE_FLASH_ATTR l_message (const char *pname, const char *msg) {
+#if defined(LUA_USE_STDIO)
+ if (pname) c_fprintf(c_stderr, "%s: ", pname);
+ c_fprintf(c_stderr, "%s\n", msg);
+ c_fflush(c_stderr);
+#else
+ if (pname) luai_writestringerror("%s: ", pname);
+ luai_writestringerror("%s\n", msg);
+#endif
+}
+
+
+static int ICACHE_FLASH_ATTR report (lua_State *L, int status) {
+ if (status && !lua_isnil(L, -1)) {
+ const char *msg = lua_tostring(L, -1);
+ if (msg == NULL) msg = "(error object is not a string)";
+ l_message(progname, msg);
+ lua_pop(L, 1);
+ }
+ return status;
+}
+
+
+static int ICACHE_FLASH_ATTR traceback (lua_State *L) {
+ if (!lua_isstring(L, 1)) /* 'message' not a string? */
+ return 1; /* keep it intact */
+ lua_getfield(L, LUA_GLOBALSINDEX, "debug");
+ if (!lua_istable(L, -1) && !lua_isrotable(L, -1)) {
+ lua_pop(L, 1);
+ return 1;
+ }
+ lua_getfield(L, -1, "traceback");
+ if (!lua_isfunction(L, -1) && !lua_islightfunction(L, -1)) {
+ lua_pop(L, 2);
+ return 1;
+ }
+ lua_pushvalue(L, 1); /* pass error message */
+ lua_pushinteger(L, 2); /* skip this function and traceback */
+ lua_call(L, 2, 1); /* call debug.traceback */
+ return 1;
+}
+
+
+static int ICACHE_FLASH_ATTR docall (lua_State *L, int narg, int clear) {
+ int status;
+ int base = lua_gettop(L) - narg; /* function index */
+ lua_pushcfunction(L, traceback); /* push traceback function */
+ lua_insert(L, base); /* put it under chunk and args */
+ // signal(SIGINT, laction);
+ status = lua_pcall(L, narg, (clear ? 0 : LUA_MULTRET), base);
+ // signal(SIGINT, SIG_DFL);
+ lua_remove(L, base); /* remove traceback function */
+ /* force a complete garbage collection in case of errors */
+ if (status != 0) lua_gc(L, LUA_GCCOLLECT, 0);
+ return status;
+}
+
+
+static void ICACHE_FLASH_ATTR print_version (void) {
+ // l_message(NULL, LUA_RELEASE " " LUA_COPYRIGHT);
+ l_message(NULL, NODE_VERSION " " BUILD_DATE " powered by " LUA_RELEASE);
+}
+
+
+static int ICACHE_FLASH_ATTR getargs (lua_State *L, char **argv, int n) {
+ int narg;
+ int i;
+ int argc = 0;
+ while (argv[argc]) argc++; /* count total number of arguments */
+ narg = argc - (n + 1); /* number of arguments to the script */
+ luaL_checkstack(L, narg + 3, "too many arguments to script");
+ for (i=n+1; i < argc; i++)
+ lua_pushstring(L, argv[i]);
+ lua_createtable(L, narg, n + 1);
+ for (i=0; i < argc; i++) {
+ lua_pushstring(L, argv[i]);
+ lua_rawseti(L, -2, i - n);
+ }
+ return narg;
+}
+
+#if 0
+static int ICACHE_FLASH_ATTR dofile (lua_State *L, const char *name) {
+ int status = luaL_loadfile(L, name) || docall(L, 0, 1);
+ return report(L, status);
+}
+#else
+static int ICACHE_FLASH_ATTR dofsfile (lua_State *L, const char *name) {
+ int status = luaL_loadfsfile(L, name) || docall(L, 0, 1);
+ return report(L, status);
+}
+#endif
+
+static int ICACHE_FLASH_ATTR dostring (lua_State *L, const char *s, const char *name) {
+ int status = luaL_loadbuffer(L, s, c_strlen(s), name) || docall(L, 0, 1);
+ return report(L, status);
+}
+
+
+static int ICACHE_FLASH_ATTR dolibrary (lua_State *L, const char *name) {
+ lua_getglobal(L, "require");
+ lua_pushstring(L, name);
+ return report(L, docall(L, 1, 1));
+}
+
+static const char *ICACHE_FLASH_ATTR get_prompt (lua_State *L, int firstline) {
+ const char *p;
+ lua_getfield(L, LUA_GLOBALSINDEX, firstline ? "_PROMPT" : "_PROMPT2");
+ p = lua_tostring(L, -1);
+ if (p == NULL) p = (firstline ? LUA_PROMPT : LUA_PROMPT2);
+ lua_pop(L, 1); /* remove global */
+ return p;
+}
+
+
+static int ICACHE_FLASH_ATTR incomplete (lua_State *L, int status) {
+ if (status == LUA_ERRSYNTAX) {
+ size_t lmsg;
+ const char *msg = lua_tolstring(L, -1, &lmsg);
+ const char *tp = msg + lmsg - (sizeof(LUA_QL("")) - 1);
+ if (c_strstr(msg, LUA_QL("")) == tp) {
+ lua_pop(L, 1);
+ return 1;
+ }
+ }
+ return 0; /* else... */
+}
+
+#if 0
+static int ICACHE_FLASH_ATTR pushline (lua_State *L, int firstline) {
+ char buffer[LUA_MAXINPUT];
+ char *b = buffer;
+ size_t l;
+ const char *prmt = get_prompt(L, firstline);
+ if (lua_readline(L, b, prmt) == 0)
+ return 0; /* no input */
+ l = c_strlen(b);
+ if (l > 0 && b[l-1] == '\n') /* line ends with newline? */
+ b[l-1] = '\0'; /* remove it */
+ if (firstline && b[0] == '=') /* first line starts with `=' ? */
+ lua_pushfstring(L, "return %s", b+1); /* change it to `return' */
+ else
+ lua_pushstring(L, b);
+ lua_freeline(L, b);
+ return 1;
+}
+
+
+static int ICACHE_FLASH_ATTR loadline (lua_State *L) {
+ int status;
+ lua_settop(L, 0);
+ if (!pushline(L, 1))
+ return -1; /* no input */
+ for (;;) { /* repeat until gets a complete line */
+ status = luaL_loadbuffer(L, lua_tostring(L, 1), lua_strlen(L, 1), "=stdin");
+ if (!incomplete(L, status)) break; /* cannot try to add lines? */
+ if (!pushline(L, 0)) /* no more input? */
+ return -1;
+ lua_pushliteral(L, "\n"); /* add a new line... */
+ lua_insert(L, -2); /* ...between the two lines */
+ lua_concat(L, 3); /* join them */
+ }
+ lua_saveline(L, 1);
+ lua_remove(L, 1); /* remove line */
+ return status;
+}
+
+
+static void ICACHE_FLASH_ATTR dotty (lua_State *L) {
+ int status;
+ const char *oldprogname = progname;
+ progname = NULL;
+ while ((status = loadline(L)) != -1) {
+ if (status == 0) status = docall(L, 0, 0);
+ report(L, status);
+ if (status == 0 && lua_gettop(L) > 0) { /* any result to print? */
+ lua_getglobal(L, "print");
+ lua_insert(L, 1);
+ if (lua_pcall(L, lua_gettop(L)-1, 0, 0) != 0)
+ l_message(progname, lua_pushfstring(L,
+ "error calling " LUA_QL("print") " (%s)",
+ lua_tostring(L, -1)));
+ }
+ }
+ lua_settop(L, 0); /* clear stack */
+
+#if defined(LUA_USE_STDIO)
+ c_fputs("\n", c_stdout);
+ c_fflush(c_stdout);
+#else
+ luai_writeline();
+#endif
+
+ progname = oldprogname;
+}
+
+
+static int ICACHE_FLASH_ATTR handle_script (lua_State *L, char **argv, int n) {
+ int status;
+ const char *fname;
+ int narg = getargs(L, argv, n); /* collect arguments */
+ lua_setglobal(L, "arg");
+ fname = argv[n];
+ if (c_strcmp(fname, "-") == 0 && c_strcmp(argv[n-1], "--") != 0)
+ fname = NULL; /* stdin */
+ status = luaL_loadfile(L, fname);
+ lua_insert(L, -(narg+1));
+ if (status == 0)
+ status = docall(L, narg, 0);
+ else
+ lua_pop(L, narg);
+ return report(L, status);
+}
+#endif
+
+/* check that argument has no extra characters at the end */
+#define notail(x) {if ((x)[2] != '\0') return -1;}
+
+
+static int ICACHE_FLASH_ATTR collectargs (char **argv, int *pi, int *pv, int *pe) {
+ int i;
+ for (i = 1; argv[i] != NULL; i++) {
+ if (argv[i][0] != '-') /* not an option? */
+ return i;
+ switch (argv[i][1]) { /* option */
+ case '-':
+ notail(argv[i]);
+ return (argv[i+1] != NULL ? i+1 : 0);
+ case '\0':
+ return i;
+ case 'i':
+ notail(argv[i]);
+ *pi = 1; /* go through */
+ case 'v':
+ notail(argv[i]);
+ *pv = 1;
+ break;
+ case 'e':
+ *pe = 1; /* go through */
+ case 'm': /* go through */
+ case 'l':
+ if (argv[i][2] == '\0') {
+ i++;
+ if (argv[i] == NULL) return -1;
+ }
+ break;
+ default: return -1; /* invalid option */
+ }
+ }
+ return 0;
+}
+
+
+static int ICACHE_FLASH_ATTR runargs (lua_State *L, char **argv, int n) {
+ int i;
+ for (i = 1; i < n; i++) {
+ if (argv[i] == NULL) continue;
+ lua_assert(argv[i][0] == '-');
+ switch (argv[i][1]) { /* option */
+ case 'e': {
+ const char *chunk = argv[i] + 2;
+ if (*chunk == '\0') chunk = argv[++i];
+ lua_assert(chunk != NULL);
+ if (dostring(L, chunk, "=(command line)") != 0)
+ return 1;
+ break;
+ }
+ case 'm': {
+ const char *limit = argv[i] + 2;
+ int memlimit=0;
+ if (*limit == '\0') limit = argv[++i];
+ lua_assert(limit != NULL);
+ memlimit = c_atoi(limit);
+ lua_gc(L, LUA_GCSETMEMLIMIT, memlimit);
+ break;
+ }
+ case 'l': {
+ const char *filename = argv[i] + 2;
+ if (*filename == '\0') filename = argv[++i];
+ lua_assert(filename != NULL);
+ if (dolibrary(L, filename))
+ return 1; /* stop if file fails */
+ break;
+ }
+ default: break;
+ }
+ }
+ return 0;
+}
+
+
+static int ICACHE_FLASH_ATTR handle_luainit (lua_State *L) {
+ const char *init = c_getenv(LUA_INIT);
+ if (init == NULL) return 0; /* status OK */
+ else if (init[0] == '@')
+#if 0
+ return dofile(L, init+1);
+#else
+ return dofsfile(L, init+1);
+#endif
+ else
+ return dostring(L, init, "=" LUA_INIT);
+}
+
+
+struct Smain {
+ int argc;
+ char **argv;
+ int status;
+};
+
+
+static int ICACHE_FLASH_ATTR pmain (lua_State *L) {
+ struct Smain *s = (struct Smain *)lua_touserdata(L, 1);
+ char **argv = s->argv;
+ int script;
+ int has_i = 0, has_v = 0, has_e = 0;
+ globalL = L;
+ if (argv[0] && argv[0][0]) progname = argv[0];
+ lua_gc(L, LUA_GCSTOP, 0); /* stop collector during initialization */
+ luaL_openlibs(L); /* open libraries */
+ lua_gc(L, LUA_GCRESTART, 0);
+ print_version();
+ s->status = handle_luainit(L);
+#if 0
+ if (s->status != 0) return 0;
+#endif
+ script = collectargs(argv, &has_i, &has_v, &has_e);
+ if (script < 0) { /* invalid args? */
+#if 0
+ print_usage();
+#endif
+ s->status = 1;
+ return 0;
+ }
+ // if (has_v) print_version();
+ s->status = runargs(L, argv, (script > 0) ? script : s->argc);
+ if (s->status != 0) return 0;
+#if 0
+ if (script)
+ s->status = handle_script(L, argv, script);
+ if (s->status != 0) return 0;
+ if (has_i)
+ dotty(L);
+ else if (script == 0 && !has_e && !has_v) {
+ if (lua_stdin_is_tty()) {
+ print_version();
+ dotty(L);
+ }
+ else dofile(L, NULL); /* executes stdin as a file */
+ }
+#endif
+ return 0;
+}
+
+void dojob(lua_Load *load);
+void readline(lua_Load *load);
+char line_buffer[LUA_MAXINPUT];
+
+#ifdef LUA_RPC
+int ICACHE_FLASH_ATTR main (int argc, char **argv) {
+#else
+int ICACHE_FLASH_ATTR lua_main (int argc, char **argv) {
+#endif
+ int status;
+ struct Smain s;
+ lua_State *L = lua_open(); /* create state */
+ if (L == NULL) {
+ l_message(argv[0], "cannot create state: not enough memory");
+ return EXIT_FAILURE;
+ }
+ s.argc = argc;
+ s.argv = argv;
+ status = lua_cpcall(L, &pmain, &s);
+ report(L, status);
+
+ gLoad.L = L;
+ gLoad.firstline = 1;
+ gLoad.done = 0;
+ gLoad.line = line_buffer;
+ gLoad.len = LUA_MAXINPUT;
+ gLoad.line_position = 0;
+ gLoad.prmt = get_prompt(L, 1);
+
+ // dojob(&gLoad);
+ os_timer_disarm(&lua_timer);
+ os_timer_setfn(&lua_timer, (os_timer_func_t *)dojob, &gLoad);
+ os_timer_arm(&lua_timer, READLINE_INTERVAL, 0); // no repeat
+
+ NODE_DBG("Heap size::%d.\n",system_get_free_heap_size());
+ legc_set_mode( L, EGC_ALWAYS, 4096 );
+ // legc_set_mode( L, EGC_ON_MEM_LIMIT, 4096 );
+ // lua_close(L);
+ return (status || s.status) ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+
+void ICACHE_FLASH_ATTR donejob(lua_Load *load){
+ lua_close(load->L);
+}
+
+#if 0
+int log_fd = -1;
+int noparse = 0;
+#endif
+
+void ICACHE_FLASH_ATTR dojob(lua_Load *load){
+ size_t l, rs;
+ int status;
+ char *b = load->line;
+ lua_State *L = load->L;
+
+ const char *oldprogname = progname;
+ progname = NULL;
+
+ do{
+ if(load->done == 1){
+ l = c_strlen(b);
+#if 0
+ if(log_fd!=-1 && l>0)
+ {
+ if(l>=10 && (c_strstr(b,"log.stop()")) )
+ {
+ fs_close(log_fd);
+ log_fd = -1;
+ noparse = 0;
+ NODE_ERR( "log stopping.\n" );
+ }
+ if(log_fd!=-1){ // test again
+ rs = fs_write(log_fd, b, l);
+ if(rs!=l){
+ fs_close(log_fd);
+ log_fd = -1;
+ } else {
+ rs = fs_write(log_fd, "\r\n", 2);
+ if(rs!=2){
+ fs_close(log_fd);
+ log_fd = -1;
+ }
+ }
+ }
+ if(noparse){
+ break;
+ }
+ }
+#endif
+ if (l > 0 && b[l-1] == '\n') /* line ends with newline? */
+ b[l-1] = '\0'; /* remove it */
+ if (load->firstline && b[0] == '=') /* first line starts with `=' ? */
+ lua_pushfstring(L, "return %s", b+1); /* change it to `return' */
+ else
+ lua_pushstring(L, b);
+ if(load->firstline != 1){
+ lua_pushliteral(L, "\n"); /* add a new line... */
+ lua_insert(L, -2); /* ...between the two lines */
+ lua_concat(L, 3); /* join them */
+ }
+
+ status = luaL_loadbuffer(L, lua_tostring(L, 1), lua_strlen(L, 1), "=stdin");
+ if (!incomplete(L, status)) { /* cannot try to add lines? */
+ lua_remove(L, 1); /* remove line */
+ if (status == 0) {
+ status = docall(L, 0, 0);
+ }
+ report(L, status);
+ if (status == 0 && lua_gettop(L) > 0) { /* any result to print? */
+ lua_getglobal(L, "print");
+ lua_insert(L, 1);
+ if (lua_pcall(L, lua_gettop(L)-1, 0, 0) != 0)
+ l_message(progname, lua_pushfstring(L,
+ "error calling " LUA_QL("print") " (%s)",
+ lua_tostring(L, -1)));
+ }
+ load->firstline = 1;
+ load->prmt = get_prompt(L, 1);
+ lua_settop(L, 0);
+ /* force a complete garbage collection in case of errors */
+ if (status != 0) lua_gc(L, LUA_GCCOLLECT, 0);
+ } else {
+ load->firstline = 0;
+ load->prmt = get_prompt(L, 0);
+ }
+ }
+ }while(0);
+
+ progname = oldprogname;
+
+ load->done = 0;
+ load->line_position = 0;
+ c_memset(load->line, 0, load->len);
+ os_timer_disarm(&readline_timer);
+ os_timer_setfn(&readline_timer, (os_timer_func_t *)readline, load);
+ os_timer_arm(&readline_timer, READLINE_INTERVAL, 0); // no repeat
+ c_puts(load->prmt);
+ // NODE_DBG("dojob() is called with firstline.\n");
+}
+
+extern void key_long_press(void *arg);
+extern void key_short_press(void *arg);
+static bool key_short_pressed = false;
+static bool key_long_pressed = false;
+void ICACHE_FLASH_ATTR update_key_led(){
+ uint8_t temp = 1, level = 1;
+ led_count++;
+ if(led_count>led_low_count+led_high_count){
+ led_count = 0; // reset led_count, the level still high
+ } else if(led_count>led_low_count && led_count <=led_high_count+led_low_count){
+ level = 1; // output high level
+ } else if(led_count<=led_low_count){
+ level = 0; // output low level
+ }
+ temp = platform_key_led(level);
+ if(temp == 0){ // key is pressed
+ key_press_count++;
+ if(key_press_count>=KEY_LONG_COUNT){
+ // key_long_press(NULL);
+ key_long_pressed = true;
+ key_short_pressed = false;
+ // key_press_count = 0;
+ } else if(key_press_count>=KEY_SHORT_COUNT){ // < KEY_LONG_COUNT
+ // key_short_press(NULL);
+ key_short_pressed = true;
+ }
+ }else{ // key is released
+ key_press_count = 0;
+ if(key_long_pressed){
+ key_long_press(NULL);
+ key_long_pressed = false;
+ }
+ if(key_short_pressed){
+ key_short_press(NULL);
+ key_short_pressed = false;
+ }
+ }
+}
+
+#ifndef uart_putc
+#define uart_putc uart0_putc
+#endif
+extern bool uart_on_data_cb(const char *buf, size_t len);
+extern bool uart0_echo;
+void ICACHE_FLASH_ATTR readline(lua_Load *load){
+ // NODE_DBG("readline() is called.\n");
+ update_key_led();
+ char ch;
+ while ((ch = uart_getc()) != 0)
+ {
+ /* handle CR key */
+ if (ch == '\r')
+ {
+ char next;
+ if ((next = uart_getc()) != 0)
+ ch = next;
+ }
+ /* backspace key */
+ else if (ch == 0x7f || ch == 0x08)
+ {
+ if (load->line_position > 0)
+ {
+ if(uart0_echo) uart_putc(0x08);
+ if(uart0_echo) uart_putc(' ');
+ if(uart0_echo) uart_putc(0x08);
+ load->line_position--;
+ }
+ load->line[load->line_position] = 0;
+ continue;
+ }
+ /* EOF(ctrl+d) */
+ else if (ch == 0x04)
+ {
+ if (load->line_position == 0)
+ // No input which makes lua interpreter close
+ donejob(load);
+ else
+ continue;
+ }
+ /* other control character or not an acsii character */
+ else if (ch < 0x20 || ch >= 0x80)
+ {
+ continue;
+ }
+ /* end of line */
+ if (ch == '\r' || ch == '\n')
+ {
+ load->line[load->line_position] = 0;
+ if(uart0_echo) uart_putc('\n');
+ if(uart_on_data_cb(load->line, load->line_position)){
+ load->line_position = 0;
+ }
+ if (load->line_position == 0)
+ {
+ /* Get a empty line, then go to get a new line */
+ c_puts(load->prmt);
+ os_timer_disarm(&readline_timer);
+ os_timer_setfn(&readline_timer, (os_timer_func_t *)readline, load);
+ os_timer_arm(&readline_timer, READLINE_INTERVAL, 0); // no repeat
+ } else {
+ load->done = 1;
+ os_timer_disarm(&lua_timer);
+ os_timer_setfn(&lua_timer, (os_timer_func_t *)dojob, load);
+ os_timer_arm(&lua_timer, READLINE_INTERVAL, 0); // no repeat
+ }
+ }
+
+ /* echo */
+ if(uart0_echo) uart_putc(ch);
+ load->line[load->line_position] = ch;
+ ch = 0;
+ load->line_position++;
+
+ /* it's a large line, discard it */
+ if (load->line_position >= load->len)
+ load->line_position = 0;
+ }
+ // if there is no input from user, repeat readline()
+ os_timer_disarm(&readline_timer);
+ os_timer_setfn(&readline_timer, (os_timer_func_t *)readline, load);
+ os_timer_arm(&readline_timer, READLINE_INTERVAL, 0); // no repeat
+}
+
+void ICACHE_FLASH_ATTR dogc(void){
+ if(globalL){
+ lua_gc(globalL, LUA_GCCOLLECT, 0);
+ }
+}
diff --git a/app/lua/lua.h b/app/lua/lua.h
new file mode 100644
index 00000000..8f883f6f
--- /dev/null
+++ b/app/lua/lua.h
@@ -0,0 +1,409 @@
+/*
+** $Id: lua.h,v 1.218.1.5 2008/08/06 13:30:12 roberto Exp $
+** Lua - An Extensible Extension Language
+** Lua.org, PUC-Rio, Brazil (http://www.lua.org)
+** See Copyright Notice at the end of this file
+*/
+
+
+#ifndef lua_h
+#define lua_h
+
+#include "c_stdarg.h"
+#include "c_stddef.h"
+#include "c_types.h"
+
+#include "luaconf.h"
+
+
+#define LUA_VERSION "Lua 5.1"
+#define LUA_RELEASE "Lua 5.1.4"
+#define LUA_VERSION_NUM 501
+#define LUA_COPYRIGHT "Copyright (C) 1994-2011 Lua.org, PUC-Rio"
+#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo & W. Celes"
+
+
+/* mark for precompiled code (`Lua') */
+#define LUA_SIGNATURE "\033Lua"
+
+/* option for multiple returns in `lua_pcall' and `lua_call' */
+#define LUA_MULTRET (-1)
+
+
+/*
+** pseudo-indices
+*/
+#define LUA_REGISTRYINDEX (-10000)
+#define LUA_ENVIRONINDEX (-10001)
+#define LUA_GLOBALSINDEX (-10002)
+#define lua_upvalueindex(i) (LUA_GLOBALSINDEX-(i))
+
+
+/* thread status; 0 is OK */
+#define LUA_YIELD 1
+#define LUA_ERRRUN 2
+#define LUA_ERRSYNTAX 3
+#define LUA_ERRMEM 4
+#define LUA_ERRERR 5
+
+
+typedef struct lua_State lua_State;
+
+typedef int (*lua_CFunction) (lua_State *L);
+
+
+/*
+** functions that read/write blocks when loading/dumping Lua chunks
+*/
+typedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz);
+
+typedef int (*lua_Writer) (lua_State *L, const void* p, size_t sz, void* ud);
+
+
+/*
+** prototype for memory-allocation functions
+*/
+typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);
+
+
+/*
+** basic types
+*/
+#define LUA_TNONE (-1)
+
+#define LUA_TNIL 0
+#define LUA_TBOOLEAN 1
+#define LUA_TROTABLE 2
+#define LUA_TLIGHTFUNCTION 3
+#define LUA_TLIGHTUSERDATA 4
+#define LUA_TNUMBER 5
+#define LUA_TSTRING 6
+#define LUA_TTABLE 7
+#define LUA_TFUNCTION 8
+#define LUA_TUSERDATA 9
+#define LUA_TTHREAD 10
+
+/* minimum Lua stack available to a C function */
+#define LUA_MINSTACK 20
+
+
+/*
+** generic extra include file
+*/
+#if defined(LUA_USER_H)
+#include LUA_USER_H
+#endif
+
+
+/* type of numbers in Lua */
+typedef LUA_NUMBER lua_Number;
+
+
+/* type for integer functions */
+typedef LUA_INTEGER lua_Integer;
+
+
+
+/*
+** state manipulation
+*/
+LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud);
+LUA_API void (lua_close) (lua_State *L);
+LUA_API lua_State *(lua_newthread) (lua_State *L);
+
+LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf);
+
+
+/*
+** basic stack manipulation
+*/
+LUA_API int (lua_gettop) (lua_State *L);
+LUA_API void (lua_settop) (lua_State *L, int idx);
+LUA_API void (lua_pushvalue) (lua_State *L, int idx);
+LUA_API void (lua_remove) (lua_State *L, int idx);
+LUA_API void (lua_insert) (lua_State *L, int idx);
+LUA_API void (lua_replace) (lua_State *L, int idx);
+LUA_API int (lua_checkstack) (lua_State *L, int sz);
+
+LUA_API void (lua_xmove) (lua_State *from, lua_State *to, int n);
+
+
+/*
+** access functions (stack -> C)
+*/
+
+LUA_API int (lua_isnumber) (lua_State *L, int idx);
+LUA_API int (lua_isstring) (lua_State *L, int idx);
+LUA_API int (lua_iscfunction) (lua_State *L, int idx);
+LUA_API int (lua_isuserdata) (lua_State *L, int idx);
+LUA_API int (lua_type) (lua_State *L, int idx);
+LUA_API const char *(lua_typename) (lua_State *L, int tp);
+
+LUA_API int (lua_equal) (lua_State *L, int idx1, int idx2);
+LUA_API int (lua_rawequal) (lua_State *L, int idx1, int idx2);
+LUA_API int (lua_lessthan) (lua_State *L, int idx1, int idx2);
+
+LUA_API lua_Number (lua_tonumber) (lua_State *L, int idx);
+LUA_API lua_Integer (lua_tointeger) (lua_State *L, int idx);
+LUA_API int (lua_toboolean) (lua_State *L, int idx);
+LUA_API const char *(lua_tolstring) (lua_State *L, int idx, size_t *len);
+LUA_API size_t (lua_objlen) (lua_State *L, int idx);
+LUA_API lua_CFunction (lua_tocfunction) (lua_State *L, int idx);
+LUA_API void *(lua_touserdata) (lua_State *L, int idx);
+LUA_API lua_State *(lua_tothread) (lua_State *L, int idx);
+LUA_API const void *(lua_topointer) (lua_State *L, int idx);
+
+
+/*
+** push functions (C -> stack)
+*/
+LUA_API void (lua_pushnil) (lua_State *L);
+LUA_API void (lua_pushnumber) (lua_State *L, lua_Number n);
+LUA_API void (lua_pushinteger) (lua_State *L, lua_Integer n);
+LUA_API void (lua_pushlstring) (lua_State *L, const char *s, size_t l);
+LUA_API void (lua_pushrolstring) (lua_State *L, const char *s, size_t l);
+LUA_API void (lua_pushstring) (lua_State *L, const char *s);
+LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt,
+ va_list argp);
+LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...);
+LUA_API void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n);
+LUA_API void (lua_pushboolean) (lua_State *L, int b);
+LUA_API void (lua_pushlightuserdata) (lua_State *L, void *p);
+LUA_API void (lua_pushlightfunction) (lua_State *L, void *p);
+LUA_API void (lua_pushrotable) (lua_State *L, void *p);
+LUA_API int (lua_pushthread) (lua_State *L);
+
+
+/*
+** get functions (Lua -> stack)
+*/
+LUA_API void (lua_gettable) (lua_State *L, int idx);
+LUA_API void (lua_getfield) (lua_State *L, int idx, const char *k);
+LUA_API void (lua_rawget) (lua_State *L, int idx);
+LUA_API void (lua_rawgeti) (lua_State *L, int idx, int n);
+LUA_API void (lua_createtable) (lua_State *L, int narr, int nrec);
+LUA_API void *(lua_newuserdata) (lua_State *L, size_t sz);
+LUA_API int (lua_getmetatable) (lua_State *L, int objindex);
+LUA_API void (lua_getfenv) (lua_State *L, int idx);
+
+
+/*
+** set functions (stack -> Lua)
+*/
+LUA_API void (lua_settable) (lua_State *L, int idx);
+LUA_API void (lua_setfield) (lua_State *L, int idx, const char *k);
+LUA_API void (lua_rawset) (lua_State *L, int idx);
+LUA_API void (lua_rawseti) (lua_State *L, int idx, int n);
+LUA_API int (lua_setmetatable) (lua_State *L, int objindex);
+LUA_API int (lua_setfenv) (lua_State *L, int idx);
+
+
+/*
+** `load' and `call' functions (load and run Lua code)
+*/
+LUA_API void (lua_call) (lua_State *L, int nargs, int nresults);
+LUA_API int (lua_pcall) (lua_State *L, int nargs, int nresults, int errfunc);
+LUA_API int (lua_cpcall) (lua_State *L, lua_CFunction func, void *ud);
+LUA_API int (lua_load) (lua_State *L, lua_Reader reader, void *dt,
+ const char *chunkname);
+
+LUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data);
+
+
+/*
+** coroutine functions
+*/
+LUA_API int (lua_yield) (lua_State *L, int nresults);
+LUA_API int (lua_resume) (lua_State *L, int narg);
+LUA_API int (lua_status) (lua_State *L);
+
+/*
+** garbage-collection function and options
+*/
+
+#define LUA_GCSTOP 0
+#define LUA_GCRESTART 1
+#define LUA_GCCOLLECT 2
+#define LUA_GCCOUNT 3
+#define LUA_GCCOUNTB 4
+#define LUA_GCSTEP 5
+#define LUA_GCSETPAUSE 6
+#define LUA_GCSETSTEPMUL 7
+#define LUA_GCSETMEMLIMIT 8
+#define LUA_GCGETMEMLIMIT 9
+
+LUA_API int (lua_gc) (lua_State *L, int what, int data);
+
+
+/*
+** miscellaneous functions
+*/
+
+LUA_API int (lua_error) (lua_State *L);
+
+LUA_API int (lua_next) (lua_State *L, int idx);
+
+LUA_API void (lua_concat) (lua_State *L, int n);
+
+LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud);
+LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud);
+
+
+
+/*
+** ===============================================================
+** some useful macros
+** ===============================================================
+*/
+
+#define lua_pop(L,n) lua_settop(L, -(n)-1)
+
+#define lua_newtable(L) lua_createtable(L, 0, 0)
+
+#define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n)))
+
+#define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0)
+
+#define lua_strlen(L,i) lua_objlen(L, (i))
+
+#define lua_isfunction(L,n) (lua_type(L, (n)) == LUA_TFUNCTION)
+#define lua_islightfunction(L,n) (lua_type(L, (n)) == LUA_TLIGHTFUNCTION)
+#define lua_istable(L,n) (lua_type(L, (n)) == LUA_TTABLE)
+#define lua_isrotable(L,n) (lua_type(L, (n)) == LUA_TROTABLE)
+#define lua_islightuserdata(L,n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA)
+#define lua_isnil(L,n) (lua_type(L, (n)) == LUA_TNIL)
+#define lua_isboolean(L,n) (lua_type(L, (n)) == LUA_TBOOLEAN)
+#define lua_isthread(L,n) (lua_type(L, (n)) == LUA_TTHREAD)
+#define lua_isnone(L,n) (lua_type(L, (n)) == LUA_TNONE)
+#define lua_isnoneornil(L, n) (lua_type(L, (n)) <= 0)
+
+#define lua_pushliteral(L, s) \
+ lua_pushlstring(L, "" s, (sizeof(s)/sizeof(char))-1)
+
+#define lua_setglobal(L,s) lua_setfield(L, LUA_GLOBALSINDEX, (s))
+#define lua_getglobal(L,s) lua_getfield(L, LUA_GLOBALSINDEX, (s))
+
+#define lua_tostring(L,i) lua_tolstring(L, (i), NULL)
+
+
+
+/*
+** compatibility macros and functions
+*/
+
+// BogdanM: modified for eLua interrupt support
+//#define lua_open() luaL_newstate()
+lua_State* lua_open(void);
+lua_State* lua_getstate(void);
+
+#define lua_getregistry(L) lua_pushvalue(L, LUA_REGISTRYINDEX)
+
+#define lua_getgccount(L) lua_gc(L, LUA_GCCOUNT, 0)
+
+#define lua_Chunkreader lua_Reader
+#define lua_Chunkwriter lua_Writer
+
+
+/* hack */
+LUA_API void lua_setlevel (lua_State *from, lua_State *to);
+
+
+/*
+** {======================================================================
+** Debug API
+** =======================================================================
+*/
+
+
+/*
+** Event codes
+*/
+#define LUA_HOOKCALL 0
+#define LUA_HOOKRET 1
+#define LUA_HOOKLINE 2
+#define LUA_HOOKCOUNT 3
+#define LUA_HOOKTAILRET 4
+
+
+/*
+** Event masks
+*/
+#define LUA_MASKCALL (1 << LUA_HOOKCALL)
+#define LUA_MASKRET (1 << LUA_HOOKRET)
+#define LUA_MASKLINE (1 << LUA_HOOKLINE)
+#define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT)
+
+typedef struct lua_Debug lua_Debug; /* activation record */
+
+
+/* Functions to be called by the debuger in specific events */
+typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);
+
+
+LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar);
+LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar);
+LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n);
+LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n);
+LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n);
+LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n);
+
+LUA_API int lua_sethook (lua_State *L, lua_Hook func, int mask, int count);
+LUA_API lua_Hook lua_gethook (lua_State *L);
+LUA_API int lua_gethookmask (lua_State *L);
+LUA_API int lua_gethookcount (lua_State *L);
+
+
+struct lua_Debug {
+ int event;
+ const char *name; /* (n) */
+ const char *namewhat; /* (n) `global', `local', `field', `method' */
+ const char *what; /* (S) `Lua', `C', `main', `tail' */
+ const char *source; /* (S) */
+ int currentline; /* (l) */
+ int nups; /* (u) number of upvalues */
+ int linedefined; /* (S) */
+ int lastlinedefined; /* (S) */
+ char short_src[LUA_IDSIZE]; /* (S) */
+ /* private part */
+ int i_ci; /* active function */
+};
+
+/* }====================================================================== */
+
+typedef struct __lua_load{
+ lua_State *L;
+ int firstline;
+ char *line;
+ int line_position;
+ size_t len;
+ int done;
+ const char *prmt;
+}lua_Load;
+
+int lua_main( int argc, char **argv );
+
+/******************************************************************************
+* Copyright (C) 1994-2008 Lua.org, PUC-Rio. All rights reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining
+* a copy of this software and associated documentation files (the
+* "Software"), to deal in the Software without restriction, including
+* without limitation the rights to use, copy, modify, merge, publish,
+* distribute, sublicense, and/or sell copies of the Software, and to
+* permit persons to whom the Software is furnished to do so, subject to
+* the following conditions:
+*
+* The above copyright notice and this permission notice shall be
+* included in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+******************************************************************************/
+
+
+#endif
diff --git a/app/lua/luaconf.h b/app/lua/luaconf.h
new file mode 100644
index 00000000..90da40dc
--- /dev/null
+++ b/app/lua/luaconf.h
@@ -0,0 +1,888 @@
+/*
+** $Id: luaconf.h,v 1.82.1.7 2008/02/11 16:25:08 roberto Exp $
+** Configuration file for Lua
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef lconfig_h
+#define lconfig_h
+
+#include "c_limits.h"
+#include "c_stddef.h"
+#include "user_config.h"
+
+/*
+** ==================================================================
+** Search for "@@" to find all configurable definitions.
+** ===================================================================
+*/
+
+
+/*
+@@ LUA_ANSI controls the use of non-ansi features.
+** CHANGE it (define it) if you want Lua to avoid the use of any
+** non-ansi feature or library.
+*/
+#if defined(__STRICT_ANSI__)
+#define LUA_ANSI
+#endif
+
+
+#if !defined(LUA_ANSI) && defined(_WIN32)
+#define LUA_WIN
+#endif
+
+#if defined(LUA_USE_LINUX)
+#define LUA_USE_POSIX
+#define LUA_USE_DLOPEN /* needs an extra library: -ldl */
+#define LUA_USE_READLINE /* needs some extra libraries */
+#endif
+
+#if defined(LUA_USE_MACOSX)
+#define LUA_USE_POSIX
+#define LUA_DL_DYLD /* does not need extra library */
+#endif
+
+
+
+/*
+@@ LUA_USE_POSIX includes all functionallity listed as X/Open System
+@* Interfaces Extension (XSI).
+** CHANGE it (define it) if your system is XSI compatible.
+*/
+#if defined(LUA_USE_POSIX)
+#define LUA_USE_MKSTEMP
+#define LUA_USE_ISATTY
+#define LUA_USE_POPEN
+#define LUA_USE_ULONGJMP
+#endif
+
+
+/*
+@@ LUA_PATH and LUA_CPATH are the names of the environment variables that
+@* Lua check to set its paths.
+@@ LUA_INIT is the name of the environment variable that Lua
+@* checks for initialization code.
+** CHANGE them if you want different names.
+*/
+#define LUA_PATH "LUA_PATH"
+#define LUA_CPATH "LUA_CPATH"
+#define LUA_INIT "LUA_INIT"
+
+
+/*
+@@ LUA_PATH_DEFAULT is the default path that Lua uses to look for
+@* Lua libraries.
+@@ LUA_CPATH_DEFAULT is the default path that Lua uses to look for
+@* C libraries.
+** CHANGE them if your machine has a non-conventional directory
+** hierarchy or if you want to install your libraries in
+** non-conventional directories.
+*/
+#if defined(_WIN32)
+/*
+** In Windows, any exclamation mark ('!') in the path is replaced by the
+** path of the directory of the executable file of the current process.
+*/
+#define LUA_LDIR "!\\lua\\"
+#define LUA_CDIR "!\\"
+
+//## Modified for eLua
+//## Defaults search modules path to our ROM File System
+#ifndef LUA_RPC
+#define LUA_PATH_DEFAULT "/rfs/?.lua;/rfs/?.lc;/mmc/?.lua;/mmc/?.lc;/rom/?.lua;/rom/?.lc"
+#define LUA_CPATH_DEFAULT ""
+#else // #ifndef LUA_RPC
+#define LUA_PATH_DEFAULT \
+ ".\\?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" \
+ LUA_CDIR"?.lua;" LUA_CDIR"?\\init.lua"
+#define LUA_CPATH_DEFAULT \
+ ".\\?.dll;" LUA_CDIR"?.dll;" LUA_CDIR"loadall.dll"
+#endif // #ifndef LUA_RPC
+
+#else // #if defined(_WIN32)
+
+#define LUA_ROOT "/usr/local/"
+#define LUA_LDIR LUA_ROOT "share/lua/5.1/"
+#define LUA_CDIR LUA_ROOT "lib/lua/5.1/"
+
+#ifndef LUA_RPC
+#define LUA_PATH_DEFAULT "?.lua;?.lc"
+#define LUA_CPATH_DEFAULT ""
+#else // #ifndef LUA_RPC
+#define LUA_PATH_DEFAULT \
+ "./?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" \
+ LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua"
+#define LUA_CPATH_DEFAULT \
+ "./?.so;" LUA_CDIR"?.so;" LUA_CDIR"loadall.so"
+#endif // #ifndef LUA_RPC
+
+#endif // #if defined(_WIN32)
+
+
+/*
+@@ LUA_DIRSEP is the directory separator (for submodules).
+** CHANGE it if your machine does not use "/" as the directory separator
+** and is not Windows. (On Windows Lua automatically uses "\".)
+*/
+#if defined(_WIN32)
+#define LUA_DIRSEP "\\"
+#else
+#define LUA_DIRSEP "/"
+#endif
+
+
+/*
+@@ LUA_PATHSEP is the character that separates templates in a path.
+@@ LUA_PATH_MARK is the string that marks the substitution points in a
+@* template.
+@@ LUA_EXECDIR in a Windows path is replaced by the executable's
+@* directory.
+@@ LUA_IGMARK is a mark to ignore all before it when bulding the
+@* luaopen_ function name.
+** CHANGE them if for some reason your system cannot use those
+** characters. (E.g., if one of those characters is a common character
+** in file/directory names.) Probably you do not need to change them.
+*/
+#define LUA_PATHSEP ";"
+#define LUA_PATH_MARK "?"
+#define LUA_EXECDIR "!"
+#define LUA_IGMARK "-"
+
+
+/*
+@@ LUA_INTEGER is the integral type used by lua_pushinteger/lua_tointeger.
+** CHANGE that if ptrdiff_t is not adequate on your machine. (On most
+** machines, ptrdiff_t gives a good choice between int or long.)
+*/
+
+/* Changed to long for use with integral Lua numbers. */
+#if !defined LUA_NUMBER_INTEGRAL
+#define LUA_INTEGER ptrdiff_t
+#else
+ #if !defined LUA_INTEGRAL_LONGLONG
+ #define LUA_INTEGER long
+ #else
+ #define LUA_INTEGER long long
+ #endif // #if !defined LUA_INTEGRAL_LONGLONG
+#endif // #if !defined LUA_NUMBER_INTEGRAL
+
+/*
+@@ LUA_API is a mark for all core API functions.
+@@ LUALIB_API is a mark for all standard library functions.
+** CHANGE them if you need to define those functions in some special way.
+** For instance, if you want to create one Windows DLL with the core and
+** the libraries, you may want to use the following definition (define
+** LUA_BUILD_AS_DLL to get it).
+*/
+#if defined(LUA_BUILD_AS_DLL)
+
+#if defined(LUA_CORE) || defined(LUA_LIB)
+#define LUA_API __declspec(dllexport)
+#else
+#define LUA_API __declspec(dllimport)
+#endif
+
+#else
+
+#define LUA_API extern
+
+#endif
+
+/* more often than not the libs go together with the core */
+#define LUALIB_API LUA_API
+
+
+/*
+@@ LUAI_FUNC is a mark for all extern functions that are not to be
+@* exported to outside modules.
+@@ LUAI_DATA is a mark for all extern (const) variables that are not to
+@* be exported to outside modules.
+** CHANGE them if you need to mark them in some special way. Elf/gcc
+** (versions 3.2 and later) mark them as "hidden" to optimize access
+** when Lua is compiled as a shared library.
+*/
+#if defined(luaall_c)
+#define LUAI_FUNC static
+#define LUAI_DATA /* empty */
+
+#elif defined(__GNUC__) && ((__GNUC__*100 + __GNUC_MINOR__) >= 302) && \
+ defined(__ELF__)
+#define LUAI_FUNC __attribute__((visibility("hidden"))) extern
+#define LUAI_DATA LUAI_FUNC
+
+#else
+#define LUAI_FUNC extern
+#define LUAI_DATA extern
+#endif
+
+
+
+/*
+@@ LUA_QL describes how error messages quote program elements.
+** CHANGE it if you want a different appearance.
+*/
+#define LUA_QL(x) "'" x "'"
+#define LUA_QS LUA_QL("%s")
+
+
+/*
+@@ LUA_IDSIZE gives the maximum size for the description of the source
+@* of a function in debug information.
+** CHANGE it if you want a different size.
+*/
+#define LUA_IDSIZE 60
+
+
+/*
+** {==================================================================
+** Stand-alone configuration
+** ===================================================================
+*/
+
+/*
+@@ lua_stdin_is_tty detects whether the standard input is a 'tty' (that
+@* is, whether we're running lua interactively).
+** CHANGE it if you have a better definition for non-POSIX/non-Windows
+** systems.
+*/
+#if defined(LUA_USE_ISATTY)
+#include
+#define lua_stdin_is_tty() isatty(0)
+#elif defined(LUA_WIN)
+#include
+#include "c_stdio.h"
+#define lua_stdin_is_tty() _isatty(_fileno(stdin))
+#else
+#define lua_stdin_is_tty() 1 /* assume stdin is a tty */
+#endif
+
+
+/*
+@@ LUA_PROMPT is the default prompt used by stand-alone Lua.
+@@ LUA_PROMPT2 is the default continuation prompt used by stand-alone Lua.
+** CHANGE them if you want different prompts. (You can also change the
+** prompts dynamically, assigning to globals _PROMPT/_PROMPT2.)
+*/
+#define LUA_PROMPT "> "
+#define LUA_PROMPT2 ">> "
+
+
+/*
+@@ LUA_PROGNAME is the default name for the stand-alone Lua program.
+** CHANGE it if your stand-alone interpreter has a different name and
+** your system is not able to detect that name automatically.
+*/
+#define LUA_PROGNAME "lua"
+
+
+/*
+@@ LUA_MAXINPUT is the maximum length for an input line in the
+@* stand-alone interpreter.
+** CHANGE it if you need longer lines.
+*/
+#define LUA_MAXINPUT 256
+
+
+/*
+@@ lua_readline defines how to show a prompt and then read a line from
+@* the standard input.
+@@ lua_saveline defines how to "save" a read line in a "history".
+@@ lua_freeline defines how to free a line read by lua_readline.
+** CHANGE them if you want to improve this functionality (e.g., by using
+** GNU readline and history facilities).
+*/
+#if defined(LUA_USE_STDIO)
+#if defined(LUA_USE_READLINE)
+#include "c_stdio.h"
+#include
+#include
+#define lua_readline(L,b,p) ((void)L, ((b)=readline(p)) != NULL)
+#define lua_saveline(L,idx) \
+ if (lua_strlen(L,idx) > 0) /* non-empty line? */ \
+ add_history(lua_tostring(L, idx)); /* add it to history */
+#define lua_freeline(L,b) ((void)L, c_free(b))
+#else // #if defined(LUA_USE_READLINE)
+#define lua_readline(L,b,p) \
+ ((void)L, c_fputs(p, c_stdout), c_fflush(c_stdout), /* show prompt */ \
+ c_fgets(b, LUA_MAXINPUT, c_stdin) != NULL) /* get line */
+#define lua_saveline(L,idx) { (void)L; (void)idx; }
+#define lua_freeline(L,b) { (void)L; (void)b; }
+#endif // #if defined(LUA_USE_READLINE)
+
+#else // #if defined(LUA_USE_STDIO)
+
+#define lua_readline(L,b,p) (readline4lua(p, b, LUA_MAXINPUT))
+#define lua_saveline(L,idx) { (void)L; (void)idx; }
+#define lua_freeline(L,b) { (void)L; (void)b; }
+
+extern int readline4lua(const char *prompt, char *buffer, int length);
+
+#endif // #if defined(LUA_USE_STDIO)
+
+/*
+@@ luai_writestring/luai_writeline define how 'print' prints its results.
+** They are only used in libraries and the stand-alone program. (The #if
+** avoids including 'stdio.h' everywhere.)
+*/
+#if !defined(LUA_USE_STDIO)
+#define luai_writestring(s, l) c_puts(s)
+#define luai_writeline() c_puts("\n")
+#endif // defined(LUA_USE_STDIO)
+
+/*
+@@ luai_writestringerror defines how to print error messages.
+** (A format string with one argument is enough for Lua...)
+*/
+#if !defined(LUA_USE_STDIO)
+#define luai_writestringerror(s,p) c_printf((s), (p))
+#endif // defined(LUA_USE_STDIO)
+
+
+/* }================================================================== */
+
+
+/*
+@@ LUAI_GCPAUSE defines the default pause between garbage-collector cycles
+@* as a percentage.
+** CHANGE it if you want the GC to run faster or slower (higher values
+** mean larger pauses which mean slower collection.) You can also change
+** this value dynamically.
+*/
+#define LUAI_GCPAUSE 110 /* 110% (wait memory to grow 10% before next gc) */
+
+
+/*
+@@ LUAI_GCMUL defines the default speed of garbage collection relative to
+@* memory allocation as a percentage.
+** CHANGE it if you want to change the granularity of the garbage
+** collection. (Higher values mean coarser collections. 0 represents
+** infinity, where each step performs a full collection.) You can also
+** change this value dynamically.
+*/
+#define LUAI_GCMUL 200 /* GC runs 'twice the speed' of memory allocation */
+
+
+
+/*
+@@ LUA_COMPAT_GETN controls compatibility with old getn behavior.
+** CHANGE it (define it) if you want exact compatibility with the
+** behavior of setn/getn in Lua 5.0.
+*/
+#undef LUA_COMPAT_GETN
+
+/*
+@@ LUA_COMPAT_LOADLIB controls compatibility about global loadlib.
+** CHANGE it to undefined as soon as you do not need a global 'loadlib'
+** function (the function is still available as 'package.loadlib').
+*/
+#undef LUA_COMPAT_LOADLIB
+
+/*
+@@ LUA_COMPAT_VARARG controls compatibility with old vararg feature.
+** CHANGE it to undefined as soon as your programs use only '...' to
+** access vararg parameters (instead of the old 'arg' table).
+*/
+#define LUA_COMPAT_VARARG
+
+/*
+@@ LUA_COMPAT_MOD controls compatibility with old math.mod function.
+** CHANGE it to undefined as soon as your programs use 'math.fmod' or
+** the new '%' operator instead of 'math.mod'.
+*/
+#define LUA_COMPAT_MOD
+
+/*
+@@ LUA_COMPAT_LSTR controls compatibility with old long string nesting
+@* facility.
+** CHANGE it to 2 if you want the old behaviour, or undefine it to turn
+** off the advisory error when nesting [[...]].
+*/
+#define LUA_COMPAT_LSTR 1
+
+/*
+@@ LUA_COMPAT_GFIND controls compatibility with old 'string.gfind' name.
+** CHANGE it to undefined as soon as you rename 'string.gfind' to
+** 'string.gmatch'.
+*/
+#define LUA_COMPAT_GFIND
+
+/*
+@@ LUA_COMPAT_OPENLIB controls compatibility with old 'luaL_openlib'
+@* behavior.
+** CHANGE it to undefined as soon as you replace to 'luaL_register'
+** your uses of 'luaL_openlib'
+*/
+#define LUA_COMPAT_OPENLIB
+
+/*
+@@ LUA_STRESS_EMERGENCY_GC enables stress testing code for the Emergency GC.
+** CHANGE it to defined if you want to test for Emergency GC related bugs.
+** Note that this will make the Lua vm very slow, since it will force a
+** full GC on every new allocation.
+*/
+#undef LUA_STRESS_EMERGENCY_GC
+
+/*
+@@ luai_apicheck is the assert macro used by the Lua-C API.
+** CHANGE luai_apicheck if you want Lua to perform some checks in the
+** parameters it gets from API calls. This may slow down the interpreter
+** a bit, but may be quite useful when debugging C code that interfaces
+** with Lua. A useful redefinition is to use assert.h.
+*/
+#if defined(LUA_USE_APICHECK)
+#include
+#define luai_apicheck(L,o) { (void)L; assert(o); }
+#else
+#define luai_apicheck(L,o) { (void)L; }
+#endif
+
+
+/*
+@@ LUAI_BITSINT defines the number of bits in an int.
+** CHANGE here if Lua cannot automatically detect the number of bits of
+** your machine. Probably you do not need to change this.
+*/
+/* avoid overflows in comparison */
+#if INT_MAX-20 < 32760
+#define LUAI_BITSINT 16
+#elif INT_MAX > 2147483640L
+/* int has at least 32 bits */
+#define LUAI_BITSINT 32
+#else
+#error "you must define LUA_BITSINT with number of bits in an integer"
+#endif
+
+
+/*
+@@ LUAI_UINT32 is an unsigned integer with at least 32 bits.
+@@ LUAI_INT32 is an signed integer with at least 32 bits.
+@@ LUAI_UMEM is an unsigned integer big enough to count the total
+@* memory used by Lua.
+@@ LUAI_MEM is a signed integer big enough to count the total memory
+@* used by Lua.
+** CHANGE here if for some weird reason the default definitions are not
+** good enough for your machine. (The definitions in the 'else'
+** part always works, but may waste space on machines with 64-bit
+** longs.) Probably you do not need to change this.
+*/
+#if LUAI_BITSINT >= 32
+#define LUAI_UINT32 unsigned int
+#define LUAI_INT32 int
+#define LUAI_MAXINT32 INT_MAX
+#define LUAI_UMEM size_t
+#define LUAI_MEM ptrdiff_t
+#else
+/* 16-bit ints */
+#define LUAI_UINT32 unsigned long
+#define LUAI_INT32 long
+#define LUAI_MAXINT32 LONG_MAX
+#define LUAI_UMEM unsigned long
+#define LUAI_MEM long
+#endif
+
+
+/*
+@@ LUAI_MAXCALLS limits the number of nested calls.
+** CHANGE it if you need really deep recursive calls. This limit is
+** arbitrary; its only purpose is to stop infinite recursion before
+** exhausting memory.
+*/
+#define LUAI_MAXCALLS 20000
+
+
+/*
+@@ LUAI_MAXCSTACK limits the number of Lua stack slots that a C function
+@* can use.
+** CHANGE it if you need lots of (Lua) stack space for your C
+** functions. This limit is arbitrary; its only purpose is to stop C
+** functions to consume unlimited stack space. (must be smaller than
+** -LUA_REGISTRYINDEX)
+*/
+#define LUAI_MAXCSTACK 8000
+
+
+
+/*
+** {==================================================================
+** CHANGE (to smaller values) the following definitions if your system
+** has a small C stack. (Or you may want to change them to larger
+** values if your system has a large C stack and these limits are
+** too rigid for you.) Some of these constants control the size of
+** stack-allocated arrays used by the compiler or the interpreter, while
+** others limit the maximum number of recursive calls that the compiler
+** or the interpreter can perform. Values too large may cause a C stack
+** overflow for some forms of deep constructs.
+** ===================================================================
+*/
+
+
+/*
+@@ LUAI_MAXCCALLS is the maximum depth for nested C calls (short) and
+@* syntactical nested non-terminals in a program.
+*/
+#define LUAI_MAXCCALLS 200
+
+
+/*
+@@ LUAI_MAXVARS is the maximum number of local variables per function
+@* (must be smaller than 250).
+*/
+#define LUAI_MAXVARS 50
+
+
+/*
+@@ LUAI_MAXUPVALUES is the maximum number of upvalues per function
+@* (must be smaller than 250).
+*/
+#define LUAI_MAXUPVALUES 60
+
+
+/*
+@@ LUAL_BUFFERSIZE is the buffer size used by the lauxlib buffer system.
+*/
+#define LUAL_BUFFERSIZE BUFSIZ
+
+/* }================================================================== */
+
+
+
+
+/*
+** {==================================================================
+@@ LUA_NUMBER is the type of numbers in Lua.
+** CHANGE the following definitions only if you want to build Lua
+** with a number type different from double. You may also need to
+** change lua_number2int & lua_number2integer.
+** ===================================================================
+*/
+
+/* Define LUA_NUMBER_INTEGRAL to produce a system that uses no
+ floating point operations by changing the type of Lua numbers from
+ double to long. It implements division and modulus so that
+
+ x == (x / y) * y + x % y.
+
+ The exponentiation function returns zero for negative exponents.
+ Defining LUA_NUMBER_INTEGRAL also removes the difftime function,
+ and the math module should not be used. The string.format function
+ no longer handles the floating point directives %e, %E, %f, %g, and
+ %G. */
+
+#if defined LUA_NUMBER_INTEGRAL
+#define LUA_NUMBER LUA_INTEGER
+#else
+#define LUA_NUMBER_DOUBLE
+#define LUA_NUMBER double
+#endif
+
+/*
+@@ LUAI_UACNUMBER is the result of an 'usual argument conversion'
+@* over a number.
+*/
+#define LUAI_UACNUMBER LUA_NUMBER
+
+
+/*
+@@ LUA_NUMBER_SCAN is the format for reading numbers.
+@@ LUA_NUMBER_FMT is the format for writing numbers.
+@@ lua_number2str converts a number to a string.
+@@ LUAI_MAXNUMBER2STR is maximum size of previous conversion.
+@@ lua_str2number converts a string to a number.
+*/
+#if defined LUA_NUMBER_INTEGRAL
+ #if !defined LUA_INTEGRAL_LONGLONG
+ #define LUA_NUMBER_SCAN "%ld"
+ #define LUA_NUMBER_FMT "%ld"
+ #else
+ #define LUA_NUMBER_SCAN "%lld"
+ #define LUA_NUMBER_FMT "%lld"
+ #endif // #if !defined LUA_INTEGRAL_LONGLONG
+#else
+#define LUA_NUMBER_SCAN "%lf"
+#define LUA_NUMBER_FMT "%.14g"
+#endif // #if defined LUA_NUMBER_INTEGRAL
+#define lua_number2str(s,n) c_sprintf((s), LUA_NUMBER_FMT, (n))
+#define LUAI_MAXNUMBER2STR 32 /* 16 digits, sign, point, and \0 */
+#if defined LUA_NUMBER_INTEGRAL
+ #if !defined LUA_INTEGRAL_LONGLONG
+ #define lua_str2number(s,p) c_strtol((s), (p), 10)
+ #else
+ #define lua_str2number(s,p) c_strtoll((s), (p), 10)
+ #endif // #if !defined LUA_INTEGRAL_LONGLONG
+#else
+#define lua_str2number(s,p) c_strtod((s), (p))
+#endif // #if defined LUA_NUMBER_INTEGRAL
+
+/*
+@@ The luai_num* macros define the primitive operations over numbers.
+*/
+#if defined(LUA_CORE)
+#include "c_math.h"
+#define luai_numadd(a,b) ((a)+(b))
+#define luai_numsub(a,b) ((a)-(b))
+#define luai_nummul(a,b) ((a)*(b))
+#if defined LUA_NUMBER_INTEGRAL
+#define luai_numdiv(a,b) \
+ (-1/2? \
+ (a)/(b): \
+ ((((a)<0)==((b)<0))||(((a)%(b))==0)? \
+ (a)/(b): \
+ (a)/(b)-1))
+#define luai_nummod(a,b) \
+ (-1/2? \
+ (a)%(b): \
+ ((((a)<0)==((b)<0))||(((a)%(b))==0)? \
+ (a)%(b): \
+ (a)%(b)+(b)))
+#define luai_lnumdiv(a,b) \
+ ((b)==0? \
+ (luaG_runerror(L,"divide by zero"),0): \
+ luai_numdiv(a,b))
+#define luai_lnummod(a,b) \
+ ((b)==0? \
+ (luaG_runerror(L,"modulo by zero"),0): \
+ luai_nummod(a,b))
+LUA_NUMBER luai_ipow(LUA_NUMBER, LUA_NUMBER);
+#define luai_numpow(a,b) (luai_ipow(a,b))
+#else
+#define luai_numdiv(a,b) ((a)/(b))
+#define luai_nummod(a,b) ((a) - floor((a)/(b))*(b))
+#define luai_lnumdiv(a,b) (luai_numdiv(a,b))
+#define luai_lnummod(a,b) (luai_nummod(a,b))
+#define luai_numpow(a,b) (pow(a,b))
+#endif
+#define luai_numunm(a) (-(a))
+#define luai_numeq(a,b) ((a)==(b))
+#define luai_numlt(a,b) ((a)<(b))
+#define luai_numle(a,b) ((a)<=(b))
+#define luai_numisnan(a) (!luai_numeq((a), (a)))
+#endif
+
+
+/*
+@@ lua_number2int is a macro to convert lua_Number to int.
+@@ lua_number2integer is a macro to convert lua_Number to lua_Integer.
+** CHANGE them if you know a faster way to convert a lua_Number to
+** int (with any rounding method and without throwing errors) in your
+** system. In Pentium machines, a naive typecast from double to int
+** in C is extremely slow, so any alternative is worth trying.
+*/
+
+/* On a Pentium, resort to a trick */
+#if defined(LUA_NUMBER_DOUBLE) && !defined(LUA_ANSI) && !defined(__SSE2__) && \
+ (defined(__i386) || defined (_M_IX86) || defined(__i386__))
+
+/* On a Microsoft compiler, use assembler */
+#if defined(_MSC_VER)
+
+#define lua_number2int(i,d) __asm fld d __asm fistp i
+#define lua_number2integer(i,n) lua_number2int(i, n)
+
+/* the next trick should work on any Pentium, but sometimes clashes
+ with a DirectX idiosyncrasy */
+#else
+
+union luai_Cast { double l_d; long l_l; };
+#define lua_number2int(i,d) \
+ { volatile union luai_Cast u; u.l_d = (d) + 6755399441055744.0; (i) = u.l_l; }
+#define lua_number2integer(i,n) lua_number2int(i, n)
+
+#endif
+
+
+/* this option always works, but may be slow */
+#else
+#define lua_number2int(i,d) ((i)=(int)(d))
+#define lua_number2integer(i,d) ((i)=(lua_Integer)(d))
+
+#endif
+
+/* }================================================================== */
+
+
+/*
+@@ LUAI_USER_ALIGNMENT_T is a type that requires maximum alignment.
+** CHANGE it if your system requires alignments larger than double. (For
+** instance, if your system supports long doubles and they must be
+** aligned in 16-byte boundaries, then you should add long double in the
+** union.) Probably you do not need to change this.
+*/
+#define LUAI_USER_ALIGNMENT_T union { double u; void *s; long l; }
+
+
+/*
+@@ LUAI_THROW/LUAI_TRY define how Lua does exception handling.
+** CHANGE them if you prefer to use longjmp/setjmp even with C++
+** or if want/don't to use _longjmp/_setjmp instead of regular
+** longjmp/setjmp. By default, Lua handles errors with exceptions when
+** compiling as C++ code, with _longjmp/_setjmp when asked to use them,
+** and with longjmp/setjmp otherwise.
+*/
+#if defined(__cplusplus)
+/* C++ exceptions */
+#define LUAI_THROW(L,c) throw(c)
+#define LUAI_TRY(L,c,a) try { a } catch(...) \
+ { if ((c)->status == 0) (c)->status = -1; }
+#define luai_jmpbuf int /* dummy variable */
+
+#elif defined(LUA_USE_ULONGJMP)
+/* in Unix, try _longjmp/_setjmp (more efficient) */
+#define LUAI_THROW(L,c) _longjmp((c)->b, 1)
+#define LUAI_TRY(L,c,a) if (_setjmp((c)->b) == 0) { a }
+#define luai_jmpbuf jmp_buf
+
+#else
+/* default handling with long jumps */
+#define LUAI_THROW(L,c) longjmp((c)->b, 1)
+#define LUAI_TRY(L,c,a) if (setjmp((c)->b) == 0) { a }
+#define luai_jmpbuf jmp_buf
+
+#endif
+
+
+/*
+@@ LUA_MAXCAPTURES is the maximum number of captures that a pattern
+@* can do during pattern-matching.
+** CHANGE it if you need more captures. This limit is arbitrary.
+*/
+#define LUA_MAXCAPTURES 10
+
+
+/*
+@@ lua_tmpnam is the function that the OS library uses to create a
+@* temporary name.
+@@ LUA_TMPNAMBUFSIZE is the maximum size of a name created by lua_tmpnam.
+** CHANGE them if you have an alternative to tmpnam (which is considered
+** insecure) or if you want the original tmpnam anyway. By default, Lua
+** uses tmpnam except when POSIX is available, where it uses mkstemp.
+*/
+#if defined(loslib_c) || defined(luaall_c)
+
+#if defined(LUA_USE_MKSTEMP)
+#include
+#define LUA_TMPNAMBUFSIZE 32
+#define lua_tmpnam(b,e) { \
+ c_strcpy(b, "/tmp/lua_XXXXXX"); \
+ e = mkstemp(b); \
+ if (e != -1) close(e); \
+ e = (e == -1); }
+
+#else
+#define LUA_TMPNAMBUFSIZE L_tmpnam
+#define lua_tmpnam(b,e) { e = (tmpnam(b) == NULL); }
+#endif
+
+#endif
+
+
+/*
+@@ lua_popen spawns a new process connected to the current one through
+@* the file streams.
+** CHANGE it if you have a way to implement it in your system.
+*/
+#if defined(LUA_USE_POPEN)
+
+#define lua_popen(L,c,m) ((void)L, c_fflush(NULL), popen(c,m))
+#define lua_pclose(L,file) ((void)L, (pclose(file) != -1))
+
+#elif defined(LUA_WIN)
+
+#define lua_popen(L,c,m) ((void)L, _popen(c,m))
+#define lua_pclose(L,file) ((void)L, (_pclose(file) != -1))
+
+#else
+
+#define lua_popen(L,c,m) ((void)((void)c, m), \
+ luaL_error(L, LUA_QL("popen") " not supported"), (int)0)
+#define lua_pclose(L,file) ((void)((void)L, file), 0)
+
+#endif
+
+/*
+@@ LUA_DL_* define which dynamic-library system Lua should use.
+** CHANGE here if Lua has problems choosing the appropriate
+** dynamic-library system for your platform (either Windows' DLL, Mac's
+** dyld, or Unix's dlopen). If your system is some kind of Unix, there
+** is a good chance that it has dlopen, so LUA_DL_DLOPEN will work for
+** it. To use dlopen you also need to adapt the src/Makefile (probably
+** adding -ldl to the linker options), so Lua does not select it
+** automatically. (When you change the makefile to add -ldl, you must
+** also add -DLUA_USE_DLOPEN.)
+** If you do not want any kind of dynamic library, undefine all these
+** options.
+** By default, _WIN32 gets LUA_DL_DLL and MAC OS X gets LUA_DL_DYLD.
+*/
+#if defined(LUA_USE_DLOPEN)
+#define LUA_DL_DLOPEN
+#endif
+
+#if defined(LUA_WIN)
+#define LUA_DL_DLL
+#endif
+
+
+/*
+@@ LUAI_EXTRASPACE allows you to add user-specific data in a lua_State
+@* (the data goes just *before* the lua_State pointer).
+** CHANGE (define) this if you really need that. This value must be
+** a multiple of the maximum alignment required for your machine.
+*/
+#define LUAI_EXTRASPACE 0
+
+
+/*
+@@ luai_userstate* allow user-specific actions on threads.
+** CHANGE them if you defined LUAI_EXTRASPACE and need to do something
+** extra when a thread is created/deleted/resumed/yielded.
+*/
+#define luai_userstateopen(L) ((void)L)
+#define luai_userstateclose(L) ((void)L)
+#define luai_userstatethread(L,L1) ((void)L)
+#define luai_userstatefree(L) ((void)L)
+#define luai_userstateresume(L,n) ((void)L)
+#define luai_userstateyield(L,n) ((void)L)
+
+
+/*
+@@ LUA_INTFRMLEN is the length modifier for integer conversions
+@* in 'string.format'.
+@@ LUA_INTFRM_T is the integer type correspoding to the previous length
+@* modifier.
+** CHANGE them if your system supports long long or does not support long.
+*/
+
+#if defined(LUA_USELONGLONG) || defined(LUA_INTEGRAL_LONGLONG)
+
+#define LUA_INTFRMLEN "ll"
+#define LUA_INTFRM_T long long
+
+#else
+
+#define LUA_INTFRMLEN "l"
+#define LUA_INTFRM_T long
+
+#endif
+
+
+
+/* =================================================================== */
+
+/*
+** Local configuration. You can use this space to add your redefinitions
+** without modifying the main part of the file.
+*/
+
+/* If you define the next macro you'll get the ability to set rotables as
+ metatables for tables/userdata/types (but the VM might run slower)
+*/
+#if (LUA_OPTIMIZE_MEMORY == 2) && !defined(LUA_CROSS_COMPILER)
+#define LUA_META_ROTABLES
+#endif
+
+#if LUA_OPTIMIZE_MEMORY == 2 && defined(LUA_USE_POPEN)
+#error "Pipes not supported in aggresive optimization mode (LUA_OPTIMIZE_MEMORY=2)"
+#endif
+
+#endif
diff --git a/app/lua/lualib.h b/app/lua/lualib.h
new file mode 100644
index 00000000..a475d887
--- /dev/null
+++ b/app/lua/lualib.h
@@ -0,0 +1,53 @@
+/*
+** $Id: lualib.h,v 1.36.1.1 2007/12/27 13:02:25 roberto Exp $
+** Lua standard libraries
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef lualib_h
+#define lualib_h
+
+#include "lua.h"
+
+
+/* Key to file-handle type */
+#define LUA_FILEHANDLE "INT"
+
+
+#define LUA_COLIBNAME "coroutine"
+LUALIB_API int (luaopen_base) (lua_State *L);
+
+#define LUA_TABLIBNAME "table"
+LUALIB_API int (luaopen_table) (lua_State *L);
+
+#define LUA_IOLIBNAME "io"
+LUALIB_API int (luaopen_io) (lua_State *L);
+
+// #define LUA_OSLIBNAME "os"
+// LUALIB_API int (luaopen_os) (lua_State *L);
+
+#define LUA_STRLIBNAME "string"
+LUALIB_API int (luaopen_string) (lua_State *L);
+
+// #define LUA_MATHLIBNAME "math"
+// LUALIB_API int (luaopen_math) (lua_State *L);
+
+// #define LUA_DBLIBNAME "debug"
+// LUALIB_API int (luaopen_debug) (lua_State *L);
+
+#define LUA_LOADLIBNAME "package"
+LUALIB_API int (luaopen_package) (lua_State *L);
+
+
+/* open all previous libraries */
+LUALIB_API void (luaL_openlibs) (lua_State *L);
+
+
+
+#ifndef lua_assert
+#define lua_assert(x) ((void)0)
+#endif
+
+
+#endif
diff --git a/app/lua/lundump.c b/app/lua/lundump.c
new file mode 100644
index 00000000..f2062b29
--- /dev/null
+++ b/app/lua/lundump.c
@@ -0,0 +1,338 @@
+/*
+** $Id: lundump.c,v 2.7.1.4 2008/04/04 19:51:41 roberto Exp $
+** load precompiled Lua chunks
+** See Copyright Notice in lua.h
+*/
+
+#include "c_string.h"
+
+#define lundump_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lstring.h"
+#include "lundump.h"
+#include "lzio.h"
+
+typedef struct {
+ lua_State* L;
+ ZIO* Z;
+ Mbuffer* b;
+ const char* name;
+ int swap;
+ int numsize;
+ int toflt;
+ size_t total;
+} LoadState;
+
+#ifdef LUAC_TRUST_BINARIES
+#define IF(c,s)
+#define error(S,s)
+#else
+#define IF(c,s) if (c) error(S,s)
+
+static void ICACHE_FLASH_ATTR error(LoadState* S, const char* why)
+{
+ luaO_pushfstring(S->L,"%s: %s in precompiled chunk",S->name,why);
+ luaD_throw(S->L,LUA_ERRSYNTAX);
+}
+#endif
+
+#define LoadByte(S) (lu_byte)LoadChar(S)
+#define LoadVar(S,x) LoadMem(S,&x,1,sizeof(x))
+#define LoadVector(S,b,n,size) LoadMem(S,b,n,size)
+
+static void ICACHE_FLASH_ATTR LoadBlock(LoadState* S, void* b, size_t size)
+{
+ size_t r=luaZ_read(S->Z,b,size);
+ IF (r!=0, "unexpected end");
+ S->total+=size;
+}
+
+static void ICACHE_FLASH_ATTR LoadMem (LoadState* S, void* b, int n, size_t size)
+{
+ LoadBlock(S,b,n*size);
+ if (S->swap && b)
+ {
+ char* p=(char*) b;
+ char c;
+ switch (size)
+ {
+ case 1:
+ break;
+ case 2:
+ while (n--)
+ {
+ c=p[0]; p[0]=p[1]; p[1]=c;
+ p+=2;
+ }
+ break;
+ case 4:
+ while (n--)
+ {
+ c=p[0]; p[0]=p[3]; p[3]=c;
+ c=p[1]; p[1]=p[2]; p[2]=c;
+ p+=4;
+ }
+ break;
+ case 8:
+ while (n--)
+ {
+ c=p[0]; p[0]=p[7]; p[7]=c;
+ c=p[1]; p[1]=p[6]; p[6]=c;
+ c=p[2]; p[2]=p[5]; p[5]=c;
+ c=p[3]; p[3]=p[4]; p[4]=c;
+ p+=8;
+ }
+ break;
+ default:
+ IF(1, "bad size");
+ break;
+ }
+ }
+}
+
+static int ICACHE_FLASH_ATTR LoadChar(LoadState* S)
+{
+ char x;
+ LoadVar(S,x);
+ return x;
+}
+
+static void ICACHE_FLASH_ATTR Align4(LoadState* S)
+{
+ while(S->total&3)
+ LoadChar(S);
+}
+
+static int ICACHE_FLASH_ATTR LoadInt(LoadState* S)
+{
+ int x;
+ LoadVar(S,x);
+ IF (x<0, "bad integer");
+ return x;
+}
+
+static lua_Number ICACHE_FLASH_ATTR LoadNumber(LoadState* S)
+{
+ lua_Number x;
+ if(S->toflt)
+ {
+ switch(S->numsize)
+ {
+ case 1: {
+ int8_t y;
+ LoadVar(S,y);
+ x = (lua_Number)y;
+ } break;
+ case 2: {
+ int16_t y;
+ LoadVar(S,y);
+ x = (lua_Number)y;
+ } break;
+ case 4: {
+ int32_t y;
+ LoadVar(S,y);
+ x = (lua_Number)y;
+ } break;
+#if 0
+ case 8: {
+ int64_t y;
+ LoadVar(S,y);
+ x = (lua_Number)y;
+ } break;
+#endif
+ default: lua_assert(0);
+ }
+ }
+ else
+ {
+ LoadVar(S,x); /* should probably handle more cases for float here... */
+ }
+ return x;
+}
+
+static TString* ICACHE_FLASH_ATTR LoadString(LoadState* S)
+{
+ int32_t size;
+ LoadVar(S,size);
+ if (size==0)
+ return NULL;
+ else
+ {
+ char* s;
+ if (!luaZ_direct_mode(S->Z)) {
+ s = luaZ_openspace(S->L,S->b,size);
+ LoadBlock(S,s,size);
+ return luaS_newlstr(S->L,s,size-1); /* remove trailing zero */
+ } else {
+ s = (char*)luaZ_get_crt_address(S->Z);
+ LoadBlock(S,NULL,size);
+ return luaS_newrolstr(S->L,s,size-1);
+ }
+ }
+}
+
+static void ICACHE_FLASH_ATTR LoadCode(LoadState* S, Proto* f)
+{
+ int n=LoadInt(S);
+ Align4(S);
+ if (!luaZ_direct_mode(S->Z)) {
+ f->code=luaM_newvector(S->L,n,Instruction);
+ LoadVector(S,f->code,n,sizeof(Instruction));
+ } else {
+ f->code=(Instruction*)luaZ_get_crt_address(S->Z);
+ LoadVector(S,NULL,n,sizeof(Instruction));
+ }
+ f->sizecode=n;
+}
+
+static Proto* LoadFunction(LoadState* S, TString* p);
+
+static void ICACHE_FLASH_ATTR LoadConstants(LoadState* S, Proto* f)
+{
+ int i,n;
+ n=LoadInt(S);
+ f->k=luaM_newvector(S->L,n,TValue);
+ f->sizek=n;
+ for (i=0; ik[i]);
+ for (i=0; ik[i];
+ int t=LoadChar(S);
+ switch (t)
+ {
+ case LUA_TNIL:
+ setnilvalue(o);
+ break;
+ case LUA_TBOOLEAN:
+ setbvalue(o,LoadChar(S)!=0);
+ break;
+ case LUA_TNUMBER:
+ setnvalue(o,LoadNumber(S));
+ break;
+ case LUA_TSTRING:
+ setsvalue2n(S->L,o,LoadString(S));
+ break;
+ default:
+ error(S,"bad constant");
+ break;
+ }
+ }
+ n=LoadInt(S);
+ f->p=luaM_newvector(S->L,n,Proto*);
+ f->sizep=n;
+ for (i=0; ip[i]=NULL;
+ for (i=0; ip[i]=LoadFunction(S,f->source);
+}
+
+static void ICACHE_FLASH_ATTR LoadDebug(LoadState* S, Proto* f)
+{
+ int i,n;
+ n=LoadInt(S);
+ Align4(S);
+ if (!luaZ_direct_mode(S->Z)) {
+ f->lineinfo=luaM_newvector(S->L,n,int);
+ LoadVector(S,f->lineinfo,n,sizeof(int));
+ } else {
+ f->lineinfo=(int*)luaZ_get_crt_address(S->Z);
+ LoadVector(S,NULL,n,sizeof(int));
+ }
+ f->sizelineinfo=n;
+ n=LoadInt(S);
+ f->locvars=luaM_newvector(S->L,n,LocVar);
+ f->sizelocvars=n;
+ for (i=0; ilocvars[i].varname=NULL;
+ for (i=0; ilocvars[i].varname=LoadString(S);
+ f->locvars[i].startpc=LoadInt(S);
+ f->locvars[i].endpc=LoadInt(S);
+ }
+ n=LoadInt(S);
+ f->upvalues=luaM_newvector(S->L,n,TString*);
+ f->sizeupvalues=n;
+ for (i=0; iupvalues[i]=NULL;
+ for (i=0; iupvalues[i]=LoadString(S);
+}
+
+static Proto* ICACHE_FLASH_ATTR LoadFunction(LoadState* S, TString* p)
+{
+ Proto* f;
+ if (++S->L->nCcalls > LUAI_MAXCCALLS) error(S,"code too deep");
+ f=luaF_newproto(S->L);
+ if (luaZ_direct_mode(S->Z)) proto_readonly(f);
+ setptvalue2s(S->L,S->L->top,f); incr_top(S->L);
+ f->source=LoadString(S); if (f->source==NULL) f->source=p;
+ f->linedefined=LoadInt(S);
+ f->lastlinedefined=LoadInt(S);
+ f->nups=LoadByte(S);
+ f->numparams=LoadByte(S);
+ f->is_vararg=LoadByte(S);
+ f->maxstacksize=LoadByte(S);
+ LoadCode(S,f);
+ LoadConstants(S,f);
+ LoadDebug(S,f);
+ IF (!luaG_checkcode(f), "bad code");
+ S->L->top--;
+ S->L->nCcalls--;
+ return f;
+}
+
+static void ICACHE_FLASH_ATTR LoadHeader(LoadState* S)
+{
+ char h[LUAC_HEADERSIZE];
+ char s[LUAC_HEADERSIZE];
+ int intck = (((lua_Number)0.5)==0); /* 0=float, 1=int */
+ luaU_header(h);
+ LoadBlock(S,s,LUAC_HEADERSIZE);
+ S->swap=(s[6]!=h[6]); s[6]=h[6]; /* Check if byte-swapping is needed */
+ S->numsize=h[10]=s[10]; /* length of lua_Number */
+ S->toflt=(s[11]>intck); /* check if conversion from int lua_Number to flt is needed */
+ if(S->toflt) s[11]=h[11];
+ IF (c_memcmp(h,s,LUAC_HEADERSIZE)!=0, "bad header");
+}
+
+/*
+** load precompiled chunk
+*/
+Proto* ICACHE_FLASH_ATTR luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name)
+{
+ LoadState S;
+ if (*name=='@' || *name=='=')
+ S.name=name+1;
+ else if (*name==LUA_SIGNATURE[0])
+ S.name="binary string";
+ else
+ S.name=name;
+ S.L=L;
+ S.Z=Z;
+ S.b=buff;
+ LoadHeader(&S);
+ S.total=0;
+ return LoadFunction(&S,luaS_newliteral(L,"=?"));
+}
+
+/*
+* make header
+*/
+void ICACHE_FLASH_ATTR luaU_header (char* h)
+{
+ int x=1;
+ c_memcpy(h,LUA_SIGNATURE,sizeof(LUA_SIGNATURE)-1);
+ h+=sizeof(LUA_SIGNATURE)-1;
+ *h++=(char)LUAC_VERSION;
+ *h++=(char)LUAC_FORMAT;
+ *h++=(char)*(char*)&x; /* endianness */
+ *h++=(char)sizeof(int);
+ *h++=(char)sizeof(int32_t);
+ *h++=(char)sizeof(Instruction);
+ *h++=(char)sizeof(lua_Number);
+ *h++=(char)(((lua_Number)0.5)==0); /* is lua_Number integral? */
+}
diff --git a/app/lua/lundump.h b/app/lua/lundump.h
new file mode 100644
index 00000000..852bdc62
--- /dev/null
+++ b/app/lua/lundump.h
@@ -0,0 +1,60 @@
+/*
+** $Id: lundump.h,v 1.37.1.1 2007/12/27 13:02:25 roberto Exp $
+** load precompiled Lua chunks
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lundump_h
+#define lundump_h
+
+#include "c_stdint.h"
+
+#include "lobject.h"
+#include "lzio.h"
+
+typedef uint32_t strsize_t;
+
+/* info about target machine for cross-compilation */
+typedef struct {
+ int little_endian;
+ int sizeof_int;
+ int sizeof_strsize_t;
+ int sizeof_lua_Number;
+ int lua_Number_integral;
+ int is_arm_fpa;
+} DumpTargetInfo;
+
+/* load one chunk; from lundump.c */
+LUAI_FUNC Proto* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name);
+
+/* make header; from lundump.c */
+LUAI_FUNC void luaU_header (char* h);
+
+/* dump one chunk to a different target; from ldump.c */
+int luaU_dump_crosscompile (lua_State* L, const Proto* f, lua_Writer w, void* data, int strip, DumpTargetInfo target);
+
+/* dump one chunk; from ldump.c */
+LUAI_FUNC int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, void* data, int strip);
+
+#ifdef luac_c
+/* print one chunk; from print.c */
+LUAI_FUNC void luaU_print (const Proto* f, int full);
+#endif
+
+/* for header of binary files -- this is Lua 5.1 */
+#define LUAC_VERSION 0x51
+
+/* for header of binary files -- this is the official format */
+#define LUAC_FORMAT 0
+
+/* size of header of binary files */
+#define LUAC_HEADERSIZE 12
+
+/* error codes from cross-compiler */
+/* target integer is too small to hold a value */
+#define LUA_ERR_CC_INTOVERFLOW 101
+
+/* target lua_Number is integral but a constant is non-integer */
+#define LUA_ERR_CC_NOTINTEGER 102
+
+#endif
diff --git a/app/lua/lvm.c b/app/lua/lvm.c
new file mode 100644
index 00000000..729706c0
--- /dev/null
+++ b/app/lua/lvm.c
@@ -0,0 +1,825 @@
+/*
+** $Id: lvm.c,v 2.63.1.4 2009/07/01 21:10:33 roberto Exp $
+** Lua virtual machine
+** See Copyright Notice in lua.h
+*/
+
+
+#include "c_stdio.h"
+#include "c_stdlib.h"
+#include "c_string.h"
+
+#define lvm_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lgc.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+#include "lvm.h"
+#include "lrotable.h"
+
+
+/* limit for table tag-method chains (to avoid loops) */
+#define MAXTAGLOOP 100
+
+#if defined LUA_NUMBER_INTEGRAL
+LUA_NUMBER ICACHE_FLASH_ATTR luai_ipow(LUA_NUMBER a, LUA_NUMBER b) {
+ if (b < 0)
+ return 0;
+ else if (b == 0)
+ return 1;
+ else {
+ LUA_NUMBER c = 1;
+ for (;;) {
+ if (b & 1)
+ c *= a;
+ b = b >> 1;
+ if (b == 0)
+ return c;
+ a *= a;
+ }
+ }
+}
+#endif
+
+const TValue *ICACHE_FLASH_ATTR luaV_tonumber (const TValue *obj, TValue *n) {
+ lua_Number num;
+ if (ttisnumber(obj)) return obj;
+ if (ttisstring(obj) && luaO_str2d(svalue(obj), &num)) {
+ setnvalue(n, num);
+ return n;
+ }
+ else
+ return NULL;
+}
+
+
+int ICACHE_FLASH_ATTR luaV_tostring (lua_State *L, StkId obj) {
+ if (!ttisnumber(obj))
+ return 0;
+ else {
+ char s[LUAI_MAXNUMBER2STR];
+ ptrdiff_t objr = savestack(L, obj);
+ lua_Number n = nvalue(obj);
+ lua_number2str(s, n);
+ setsvalue2s(L, restorestack(L, objr), luaS_new(L, s));
+ return 1;
+ }
+}
+
+
+static void ICACHE_FLASH_ATTR traceexec (lua_State *L, const Instruction *pc) {
+ lu_byte mask = L->hookmask;
+ const Instruction *oldpc = L->savedpc;
+ L->savedpc = pc;
+ if ((mask & LUA_MASKCOUNT) && L->hookcount == 0) {
+ resethookcount(L);
+ luaD_callhook(L, LUA_HOOKCOUNT, -1);
+ }
+ if (mask & LUA_MASKLINE) {
+ Proto *p = ci_func(L->ci)->l.p;
+ int npc = pcRel(pc, p);
+ int newline = getline(p, npc);
+ /* call linehook when enter a new function, when jump back (loop),
+ or when enter a new line */
+ if (npc == 0 || pc <= oldpc || newline != getline(p, pcRel(oldpc, p)))
+ luaD_callhook(L, LUA_HOOKLINE, newline);
+ }
+}
+
+
+static void ICACHE_FLASH_ATTR callTMres (lua_State *L, StkId res, const TValue *f,
+ const TValue *p1, const TValue *p2) {
+ ptrdiff_t result = savestack(L, res);
+ setobj2s(L, L->top, f); /* push function */
+ setobj2s(L, L->top+1, p1); /* 1st argument */
+ setobj2s(L, L->top+2, p2); /* 2nd argument */
+ luaD_checkstack(L, 3);
+ L->top += 3;
+ luaD_call(L, L->top - 3, 1);
+ res = restorestack(L, result);
+ L->top--;
+ setobjs2s(L, res, L->top);
+}
+
+
+
+static void ICACHE_FLASH_ATTR callTM (lua_State *L, const TValue *f, const TValue *p1,
+ const TValue *p2, const TValue *p3) {
+ setobj2s(L, L->top, f); /* push function */
+ setobj2s(L, L->top+1, p1); /* 1st argument */
+ setobj2s(L, L->top+2, p2); /* 2nd argument */
+ setobj2s(L, L->top+3, p3); /* 3th argument */
+ luaD_checkstack(L, 4);
+ L->top += 4;
+ luaD_call(L, L->top - 4, 0);
+}
+
+
+void ICACHE_FLASH_ATTR luaV_gettable (lua_State *L, const TValue *t, TValue *key, StkId val) {
+ int loop;
+ TValue temp;
+ for (loop = 0; loop < MAXTAGLOOP; loop++) {
+ const TValue *tm;
+ if (ttistable(t) || ttisrotable(t)) { /* `t' is a table? */
+ void *h = ttistable(t) ? hvalue(t) : rvalue(t);
+ const TValue *res = ttistable(t) ? luaH_get((Table*)h, key) : luaH_get_ro(h, key); /* do a primitive get */
+ if (!ttisnil(res) || /* result is no nil? */
+ (tm = fasttm(L, ttistable(t) ? ((Table*)h)->metatable : (Table*)luaR_getmeta(h), TM_INDEX)) == NULL) { /* or no TM? */
+ setobj2s(L, val, res);
+ return;
+ }
+ /* else will try the tag method */
+ }
+ else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_INDEX)))
+ luaG_typeerror(L, t, "index");
+ if (ttisfunction(tm) || ttislightfunction(tm)) {
+ callTMres(L, val, tm, t, key);
+ return;
+ }
+ /* else repeat with `tm' */
+ setobj(L, &temp, tm); /* avoid pointing inside table (may rehash) */
+ t = &temp;
+ }
+ luaG_runerror(L, "loop in gettable");
+}
+
+
+void ICACHE_FLASH_ATTR luaV_settable (lua_State *L, const TValue *t, TValue *key, StkId val) {
+ int loop;
+ TValue temp;
+ setnilvalue(L->top);
+ L->top++;
+ fixedstack(L);
+ for (loop = 0; loop < MAXTAGLOOP; loop++) {
+ const TValue *tm;
+ if (ttistable(t) || ttisrotable(t)) { /* `t' is a table? */
+ void *h = ttistable(t) ? hvalue(t) : rvalue(t);
+ TValue *oldval = ttistable(t) ? luaH_set(L, (Table*)h, key) : NULL; /* do a primitive set */
+ if ((oldval && !ttisnil(oldval)) || /* result is no nil? */
+ (tm = fasttm(L, ttistable(t) ? ((Table*)h)->metatable : (Table*)luaR_getmeta(h), TM_NEWINDEX)) == NULL) { /* or no TM? */
+ if(oldval) {
+ L->top--;
+ unfixedstack(L);
+ setobj2t(L, oldval, val);
+ ((Table *)h)->flags = 0;
+ luaC_barriert(L, (Table*)h, val);
+ }
+ return;
+ }
+ /* else will try the tag method */
+ }
+ else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX)))
+ luaG_typeerror(L, t, "index");
+ if (ttisfunction(tm) || ttislightfunction(tm)) {
+ L->top--;
+ unfixedstack(L);
+ callTM(L, tm, t, key, val);
+ return;
+ }
+ /* else repeat with `tm' */
+ setobj(L, &temp, tm); /* avoid pointing inside table (may rehash) */
+ t = &temp;
+ setobj2s(L, L->top-1, t); /* need to protect value from EGC. */
+ }
+ luaG_runerror(L, "loop in settable");
+}
+
+
+static int ICACHE_FLASH_ATTR call_binTM (lua_State *L, const TValue *p1, const TValue *p2,
+ StkId res, TMS event) {
+ const TValue *tm = luaT_gettmbyobj(L, p1, event); /* try first operand */
+ if (ttisnil(tm))
+ tm = luaT_gettmbyobj(L, p2, event); /* try second operand */
+ if (ttisnil(tm)) return 0;
+ callTMres(L, res, tm, p1, p2);
+ return 1;
+}
+
+
+static const TValue *ICACHE_FLASH_ATTR get_compTM (lua_State *L, Table *mt1, Table *mt2,
+ TMS event) {
+ const TValue *tm1 = fasttm(L, mt1, event);
+ const TValue *tm2;
+ if (tm1 == NULL) return NULL; /* no metamethod */
+ if (mt1 == mt2) return tm1; /* same metatables => same metamethods */
+ tm2 = fasttm(L, mt2, event);
+ if (tm2 == NULL) return NULL; /* no metamethod */
+ if (luaO_rawequalObj(tm1, tm2)) /* same metamethods? */
+ return tm1;
+ return NULL;
+}
+
+
+static int ICACHE_FLASH_ATTR call_orderTM (lua_State *L, const TValue *p1, const TValue *p2,
+ TMS event) {
+ const TValue *tm1 = luaT_gettmbyobj(L, p1, event);
+ const TValue *tm2;
+ if (ttisnil(tm1)) return -1; /* no metamethod? */
+ tm2 = luaT_gettmbyobj(L, p2, event);
+ if (!luaO_rawequalObj(tm1, tm2)) /* different metamethods? */
+ return -1;
+ callTMres(L, L->top, tm1, p1, p2);
+ return !l_isfalse(L->top);
+}
+
+
+static int ICACHE_FLASH_ATTR l_strcmp (const TString *ls, const TString *rs) {
+ const char *l = getstr(ls);
+ size_t ll = ls->tsv.len;
+ const char *r = getstr(rs);
+ size_t lr = rs->tsv.len;
+ for (;;) {
+ int temp = c_strcoll(l, r);
+ if (temp != 0) return temp;
+ else { /* strings are equal up to a `\0' */
+ size_t len = c_strlen(l); /* index of first `\0' in both strings */
+ if (len == lr) /* r is finished? */
+ return (len == ll) ? 0 : 1;
+ else if (len == ll) /* l is finished? */
+ return -1; /* l is smaller than r (because r is not finished) */
+ /* both strings longer than `len'; go on comparing (after the `\0') */
+ len++;
+ l += len; ll -= len; r += len; lr -= len;
+ }
+ }
+}
+
+
+int ICACHE_FLASH_ATTR luaV_lessthan (lua_State *L, const TValue *l, const TValue *r) {
+ int res;
+ if (ttype(l) != ttype(r))
+ return luaG_ordererror(L, l, r);
+ else if (ttisnumber(l))
+ return luai_numlt(nvalue(l), nvalue(r));
+ else if (ttisstring(l))
+ return l_strcmp(rawtsvalue(l), rawtsvalue(r)) < 0;
+ else if ((res = call_orderTM(L, l, r, TM_LT)) != -1)
+ return res;
+ return luaG_ordererror(L, l, r);
+}
+
+
+static int ICACHE_FLASH_ATTR lessequal (lua_State *L, const TValue *l, const TValue *r) {
+ int res;
+ if (ttype(l) != ttype(r))
+ return luaG_ordererror(L, l, r);
+ else if (ttisnumber(l))
+ return luai_numle(nvalue(l), nvalue(r));
+ else if (ttisstring(l))
+ return l_strcmp(rawtsvalue(l), rawtsvalue(r)) <= 0;
+ else if ((res = call_orderTM(L, l, r, TM_LE)) != -1) /* first try `le' */
+ return res;
+ else if ((res = call_orderTM(L, r, l, TM_LT)) != -1) /* else try `lt' */
+ return !res;
+ return luaG_ordererror(L, l, r);
+}
+
+
+int ICACHE_FLASH_ATTR luaV_equalval (lua_State *L, const TValue *t1, const TValue *t2) {
+ const TValue *tm;
+ lua_assert(ttype(t1) == ttype(t2));
+ switch (ttype(t1)) {
+ case LUA_TNIL: return 1;
+ case LUA_TNUMBER: return luai_numeq(nvalue(t1), nvalue(t2));
+ case LUA_TBOOLEAN: return bvalue(t1) == bvalue(t2); /* true must be 1 !! */
+ case LUA_TLIGHTUSERDATA:
+ case LUA_TROTABLE:
+ case LUA_TLIGHTFUNCTION:
+ return pvalue(t1) == pvalue(t2);
+ case LUA_TUSERDATA: {
+ if (uvalue(t1) == uvalue(t2)) return 1;
+ tm = get_compTM(L, uvalue(t1)->metatable, uvalue(t2)->metatable,
+ TM_EQ);
+ break; /* will try TM */
+ }
+ case LUA_TTABLE: {
+ if (hvalue(t1) == hvalue(t2)) return 1;
+ tm = get_compTM(L, hvalue(t1)->metatable, hvalue(t2)->metatable, TM_EQ);
+ break; /* will try TM */
+ }
+ default: return gcvalue(t1) == gcvalue(t2);
+ }
+ if (tm == NULL) return 0; /* no TM? */
+ callTMres(L, L->top, tm, t1, t2); /* call TM */
+ return !l_isfalse(L->top);
+}
+
+
+void ICACHE_FLASH_ATTR luaV_concat (lua_State *L, int total, int last) {
+ lu_mem max_sizet = MAX_SIZET;
+ if (G(L)->memlimit < max_sizet) max_sizet = G(L)->memlimit;
+ do {
+ StkId top = L->base + last + 1;
+ int n = 2; /* number of elements handled in this pass (at least 2) */
+ if (!(ttisstring(top-2) || ttisnumber(top-2)) || !tostring(L, top-1)) {
+ if (!call_binTM(L, top-2, top-1, top-2, TM_CONCAT)) {
+ /* restore 'top' pointer, since stack might have been reallocted */
+ top = L->base + last + 1;
+ luaG_concaterror(L, top-2, top-1);
+ }
+ } else if (tsvalue(top-1)->len == 0) /* second op is empty? */
+ (void)tostring(L, top - 2); /* result is first op (as string) */
+ else {
+ /* at least two string values; get as many as possible */
+ size_t tl = tsvalue(top-1)->len;
+ char *buffer;
+ int i;
+ fixedstack(L);
+ /* collect total length */
+ for (n = 1; n < total && tostring(L, top-n-1); n++) {
+ size_t l = tsvalue(top-n-1)->len;
+ if (l >= max_sizet - tl) luaG_runerror(L, "string length overflow");
+ tl += l;
+ }
+ G(L)->buff.n = tl;
+ buffer = luaZ_openspace(L, &G(L)->buff, tl);
+ tl = 0;
+ for (i=n; i>0; i--) { /* concat all strings */
+ size_t l = tsvalue(top-i)->len;
+ c_memcpy(buffer+tl, svalue(top-i), l);
+ tl += l;
+ }
+ setsvalue2s(L, top-n, luaS_newlstr(L, buffer, tl));
+ luaZ_resetbuffer(&G(L)->buff);
+ unfixedstack(L);
+ }
+ total -= n-1; /* got `n' strings to create 1 new */
+ last -= n-1;
+ } while (total > 1); /* repeat until only 1 result left */
+}
+
+
+static void ICACHE_FLASH_ATTR Arith (lua_State *L, StkId ra, const TValue *rb,
+ const TValue *rc, TMS op) {
+ TValue tempb, tempc;
+ const TValue *b, *c;
+ if ((b = luaV_tonumber(rb, &tempb)) != NULL &&
+ (c = luaV_tonumber(rc, &tempc)) != NULL) {
+ lua_Number nb = nvalue(b), nc = nvalue(c);
+ switch (op) {
+ case TM_ADD: setnvalue(ra, luai_numadd(nb, nc)); break;
+ case TM_SUB: setnvalue(ra, luai_numsub(nb, nc)); break;
+ case TM_MUL: setnvalue(ra, luai_nummul(nb, nc)); break;
+ case TM_DIV: setnvalue(ra, luai_lnumdiv(nb, nc)); break;
+ case TM_MOD: setnvalue(ra, luai_lnummod(nb, nc)); break;
+ case TM_POW: setnvalue(ra, luai_numpow(nb, nc)); break;
+ case TM_UNM: setnvalue(ra, luai_numunm(nb)); break;
+ default: lua_assert(0); break;
+ }
+ }
+ else {
+ ptrdiff_t br = savestack(L, rb);
+ ptrdiff_t cr = savestack(L, rc);
+ if (!call_binTM(L, rb, rc, ra, op)) {
+ luaG_aritherror(L, restorestack(L, br), restorestack(L, cr));
+ }
+ }
+}
+
+
+
+/*
+** some macros for common tasks in `luaV_execute'
+*/
+
+#define runtime_check(L, c) { if (!(c)) break; }
+
+#define RA(i) (base+GETARG_A(i))
+/* to be used after possible stack reallocation */
+#define RB(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgR, base+GETARG_B(i))
+#define RC(i) check_exp(getCMode(GET_OPCODE(i)) == OpArgR, base+GETARG_C(i))
+#define RKB(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgK, \
+ ISK(GETARG_B(i)) ? k+INDEXK(GETARG_B(i)) : base+GETARG_B(i))
+#define RKC(i) check_exp(getCMode(GET_OPCODE(i)) == OpArgK, \
+ ISK(GETARG_C(i)) ? k+INDEXK(GETARG_C(i)) : base+GETARG_C(i))
+#define KBx(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgK, k+GETARG_Bx(i))
+
+
+#define dojump(L,pc,i) {(pc) += (i); luai_threadyield(L);}
+
+
+#define Protect(x) { L->savedpc = pc; {x;}; base = L->base; }
+
+
+#define arith_op(op,tm) { \
+ TValue *rb = RKB(i); \
+ TValue *rc = RKC(i); \
+ if (ttisnumber(rb) && ttisnumber(rc)) { \
+ lua_Number nb = nvalue(rb), nc = nvalue(rc); \
+ setnvalue(ra, op(nb, nc)); \
+ } \
+ else \
+ Protect(Arith(L, ra, rb, rc, tm)); \
+ }
+
+
+
+void ICACHE_FLASH_ATTR luaV_execute (lua_State *L, int nexeccalls) {
+ LClosure *cl;
+ StkId base;
+ TValue *k;
+ const Instruction *pc;
+ reentry: /* entry point */
+ lua_assert(isLua(L->ci));
+ pc = L->savedpc;
+ cl = &clvalue(L->ci->func)->l;
+ base = L->base;
+ k = cl->p->k;
+ /* main loop of interpreter */
+ for (;;) {
+ const Instruction i = *pc++;
+ StkId ra;
+ if ((L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT)) &&
+ (--L->hookcount == 0 || L->hookmask & LUA_MASKLINE)) {
+ traceexec(L, pc);
+ if (L->status == LUA_YIELD) { /* did hook yield? */
+ L->savedpc = pc - 1;
+ return;
+ }
+ base = L->base;
+ }
+ /* warning!! several calls may realloc the stack and invalidate `ra' */
+ ra = RA(i);
+ lua_assert(base == L->base && L->base == L->ci->base);
+ lua_assert(base <= L->top && L->top <= L->stack + L->stacksize);
+ lua_assert(L->top == L->ci->top || luaG_checkopenop(i));
+ switch (GET_OPCODE(i)) {
+ case OP_MOVE: {
+ setobjs2s(L, ra, RB(i));
+ continue;
+ }
+ case OP_LOADK: {
+ setobj2s(L, ra, KBx(i));
+ continue;
+ }
+ case OP_LOADBOOL: {
+ setbvalue(ra, GETARG_B(i));
+ if (GETARG_C(i)) pc++; /* skip next instruction (if C) */
+ continue;
+ }
+ case OP_LOADNIL: {
+ TValue *rb = RB(i);
+ do {
+ setnilvalue(rb--);
+ } while (rb >= ra);
+ continue;
+ }
+ case OP_GETUPVAL: {
+ int b = GETARG_B(i);
+ setobj2s(L, ra, cl->upvals[b]->v);
+ continue;
+ }
+ case OP_GETGLOBAL: {
+ TValue g;
+ TValue *rb = KBx(i);
+ sethvalue(L, &g, cl->env);
+ lua_assert(ttisstring(rb));
+ Protect(luaV_gettable(L, &g, rb, ra));
+ continue;
+ }
+ case OP_GETTABLE: {
+ Protect(luaV_gettable(L, RB(i), RKC(i), ra));
+ continue;
+ }
+ case OP_SETGLOBAL: {
+ TValue g;
+ sethvalue(L, &g, cl->env);
+ lua_assert(ttisstring(KBx(i)));
+ Protect(luaV_settable(L, &g, KBx(i), ra));
+ continue;
+ }
+ case OP_SETUPVAL: {
+ UpVal *uv = cl->upvals[GETARG_B(i)];
+ setobj(L, uv->v, ra);
+ luaC_barrier(L, uv, ra);
+ continue;
+ }
+ case OP_SETTABLE: {
+ Protect(luaV_settable(L, ra, RKB(i), RKC(i)));
+ continue;
+ }
+ case OP_NEWTABLE: {
+ int b = GETARG_B(i);
+ int c = GETARG_C(i);
+ Table *h;
+ Protect(h = luaH_new(L, luaO_fb2int(b), luaO_fb2int(c)));
+ sethvalue(L, RA(i), h);
+ Protect(luaC_checkGC(L));
+ continue;
+ }
+ case OP_SELF: {
+ StkId rb = RB(i);
+ setobjs2s(L, ra+1, rb);
+ Protect(luaV_gettable(L, rb, RKC(i), ra));
+ continue;
+ }
+ case OP_ADD: {
+ arith_op(luai_numadd, TM_ADD);
+ continue;
+ }
+ case OP_SUB: {
+ arith_op(luai_numsub, TM_SUB);
+ continue;
+ }
+ case OP_MUL: {
+ arith_op(luai_nummul, TM_MUL);
+ continue;
+ }
+ case OP_DIV: {
+ arith_op(luai_lnumdiv, TM_DIV);
+ continue;
+ }
+ case OP_MOD: {
+ arith_op(luai_lnummod, TM_MOD);
+ continue;
+ }
+ case OP_POW: {
+ arith_op(luai_numpow, TM_POW);
+ continue;
+ }
+ case OP_UNM: {
+ TValue *rb = RB(i);
+ if (ttisnumber(rb)) {
+ lua_Number nb = nvalue(rb);
+ setnvalue(ra, luai_numunm(nb));
+ }
+ else {
+ Protect(Arith(L, ra, rb, rb, TM_UNM));
+ }
+ continue;
+ }
+ case OP_NOT: {
+ int res = l_isfalse(RB(i)); /* next assignment may change this value */
+ setbvalue(ra, res);
+ continue;
+ }
+ case OP_LEN: {
+ const TValue *rb = RB(i);
+ switch (ttype(rb)) {
+ case LUA_TTABLE:
+ case LUA_TROTABLE: {
+ setnvalue(ra, ttistable(rb) ? cast_num(luaH_getn(hvalue(rb))) : cast_num(luaH_getn_ro(rvalue(rb))));
+ break;
+ }
+ case LUA_TSTRING: {
+ setnvalue(ra, cast_num(tsvalue(rb)->len));
+ break;
+ }
+ default: { /* try metamethod */
+ ptrdiff_t br = savestack(L, rb);
+ Protect(
+ if (!call_binTM(L, rb, luaO_nilobject, ra, TM_LEN))
+ luaG_typeerror(L, restorestack(L, br), "get length of");
+ )
+ }
+ }
+ continue;
+ }
+ case OP_CONCAT: {
+ int b = GETARG_B(i);
+ int c = GETARG_C(i);
+ Protect(luaV_concat(L, c-b+1, c); luaC_checkGC(L));
+ setobjs2s(L, RA(i), base+b);
+ continue;
+ }
+ case OP_JMP: {
+ dojump(L, pc, GETARG_sBx(i));
+ continue;
+ }
+ case OP_EQ: {
+ TValue *rb = RKB(i);
+ TValue *rc = RKC(i);
+ Protect(
+ if (equalobj(L, rb, rc) == GETARG_A(i))
+ dojump(L, pc, GETARG_sBx(*pc));
+ )
+ pc++;
+ continue;
+ }
+ case OP_LT: {
+ Protect(
+ if (luaV_lessthan(L, RKB(i), RKC(i)) == GETARG_A(i))
+ dojump(L, pc, GETARG_sBx(*pc));
+ )
+ pc++;
+ continue;
+ }
+ case OP_LE: {
+ Protect(
+ if (lessequal(L, RKB(i), RKC(i)) == GETARG_A(i))
+ dojump(L, pc, GETARG_sBx(*pc));
+ )
+ pc++;
+ continue;
+ }
+ case OP_TEST: {
+ if (l_isfalse(ra) != GETARG_C(i))
+ dojump(L, pc, GETARG_sBx(*pc));
+ pc++;
+ continue;
+ }
+ case OP_TESTSET: {
+ TValue *rb = RB(i);
+ if (l_isfalse(rb) != GETARG_C(i)) {
+ setobjs2s(L, ra, rb);
+ dojump(L, pc, GETARG_sBx(*pc));
+ }
+ pc++;
+ continue;
+ }
+ case OP_CALL: {
+ int b = GETARG_B(i);
+ int nresults = GETARG_C(i) - 1;
+ if (b != 0) L->top = ra+b; /* else previous instruction set top */
+ L->savedpc = pc;
+ switch (luaD_precall(L, ra, nresults)) {
+ case PCRLUA: {
+ nexeccalls++;
+ goto reentry; /* restart luaV_execute over new Lua function */
+ }
+ case PCRC: {
+ /* it was a C function (`precall' called it); adjust results */
+ if (nresults >= 0) L->top = L->ci->top;
+ base = L->base;
+ continue;
+ }
+ default: {
+ return; /* yield */
+ }
+ }
+ }
+ case OP_TAILCALL: {
+ int b = GETARG_B(i);
+ if (b != 0) L->top = ra+b; /* else previous instruction set top */
+ L->savedpc = pc;
+ lua_assert(GETARG_C(i) - 1 == LUA_MULTRET);
+ switch (luaD_precall(L, ra, LUA_MULTRET)) {
+ case PCRLUA: {
+ /* tail call: put new frame in place of previous one */
+ CallInfo *ci = L->ci - 1; /* previous frame */
+ int aux;
+ StkId func = ci->func;
+ StkId pfunc = (ci+1)->func; /* previous function index */
+ if (L->openupval) luaF_close(L, ci->base);
+ L->base = ci->base = ci->func + ((ci+1)->base - pfunc);
+ for (aux = 0; pfunc+aux < L->top; aux++) /* move frame down */
+ setobjs2s(L, func+aux, pfunc+aux);
+ ci->top = L->top = func+aux; /* correct top */
+ lua_assert(L->top == L->base + clvalue(func)->l.p->maxstacksize);
+ ci->savedpc = L->savedpc;
+ ci->tailcalls++; /* one more call lost */
+ L->ci--; /* remove new frame */
+ goto reentry;
+ }
+ case PCRC: { /* it was a C function (`precall' called it) */
+ base = L->base;
+ continue;
+ }
+ default: {
+ return; /* yield */
+ }
+ }
+ }
+ case OP_RETURN: {
+ int b = GETARG_B(i);
+ if (b != 0) L->top = ra+b-1;
+ if (L->openupval) luaF_close(L, base);
+ L->savedpc = pc;
+ b = luaD_poscall(L, ra);
+ if (--nexeccalls == 0) /* was previous function running `here'? */
+ return; /* no: return */
+ else { /* yes: continue its execution */
+ if (b) L->top = L->ci->top;
+ lua_assert(isLua(L->ci));
+ lua_assert(GET_OPCODE(*((L->ci)->savedpc - 1)) == OP_CALL);
+ goto reentry;
+ }
+ }
+ case OP_FORLOOP: {
+ lua_Number step = nvalue(ra+2);
+ lua_Number idx = luai_numadd(nvalue(ra), step); /* increment index */
+ lua_Number limit = nvalue(ra+1);
+ if (luai_numlt(0, step) ? luai_numle(idx, limit)
+ : luai_numle(limit, idx)) {
+ dojump(L, pc, GETARG_sBx(i)); /* jump back */
+ setnvalue(ra, idx); /* update internal index... */
+ setnvalue(ra+3, idx); /* ...and external index */
+ }
+ continue;
+ }
+ case OP_FORPREP: {
+ const TValue *init = ra;
+ const TValue *plimit = ra+1;
+ const TValue *pstep = ra+2;
+ L->savedpc = pc; /* next steps may throw errors */
+ if (!tonumber(init, ra))
+ luaG_runerror(L, LUA_QL("for") " initial value must be a number");
+ else if (!tonumber(plimit, ra+1))
+ luaG_runerror(L, LUA_QL("for") " limit must be a number");
+ else if (!tonumber(pstep, ra+2))
+ luaG_runerror(L, LUA_QL("for") " step must be a number");
+ setnvalue(ra, luai_numsub(nvalue(ra), nvalue(pstep)));
+ dojump(L, pc, GETARG_sBx(i));
+ continue;
+ }
+ case OP_TFORLOOP: {
+ StkId cb = ra + 3; /* call base */
+ setobjs2s(L, cb+2, ra+2);
+ setobjs2s(L, cb+1, ra+1);
+ setobjs2s(L, cb, ra);
+ L->top = cb+3; /* func. + 2 args (state and index) */
+ Protect(luaD_call(L, cb, GETARG_C(i)));
+ L->top = L->ci->top;
+ cb = RA(i) + 3; /* previous call may change the stack */
+ if (!ttisnil(cb)) { /* continue loop? */
+ setobjs2s(L, cb-1, cb); /* save control variable */
+ dojump(L, pc, GETARG_sBx(*pc)); /* jump back */
+ }
+ pc++;
+ continue;
+ }
+ case OP_SETLIST: {
+ int n = GETARG_B(i);
+ int c = GETARG_C(i);
+ int last;
+ Table *h;
+ fixedstack(L);
+ if (n == 0) {
+ n = cast_int(L->top - ra) - 1;
+ L->top = L->ci->top;
+ }
+ if (c == 0) c = cast_int(*pc++);
+ runtime_check(L, ttistable(ra));
+ h = hvalue(ra);
+ last = ((c-1)*LFIELDS_PER_FLUSH) + n;
+ if (last > h->sizearray) /* needs more space? */
+ luaH_resizearray(L, h, last); /* pre-alloc it at once */
+ for (; n > 0; n--) {
+ TValue *val = ra+n;
+ setobj2t(L, luaH_setnum(L, h, last--), val);
+ luaC_barriert(L, h, val);
+ }
+ unfixedstack(L);
+ continue;
+ }
+ case OP_CLOSE: {
+ luaF_close(L, ra);
+ continue;
+ }
+ case OP_CLOSURE: {
+ Proto *p;
+ Closure *ncl;
+ int nup, j;
+ p = cl->p->p[GETARG_Bx(i)];
+ nup = p->nups;
+ fixedstack(L);
+ ncl = luaF_newLclosure(L, nup, cl->env);
+ setclvalue(L, ra, ncl);
+ ncl->l.p = p;
+ for (j=0; jl.upvals[j] = cl->upvals[GETARG_B(*pc)];
+ else {
+ lua_assert(GET_OPCODE(*pc) == OP_MOVE);
+ ncl->l.upvals[j] = luaF_findupval(L, base + GETARG_B(*pc));
+ }
+ }
+ unfixedstack(L);
+ Protect(luaC_checkGC(L));
+ continue;
+ }
+ case OP_VARARG: {
+ int b = GETARG_B(i) - 1;
+ int j;
+ CallInfo *ci = L->ci;
+ int n = cast_int(ci->base - ci->func) - cl->p->numparams - 1;
+ if (b == LUA_MULTRET) {
+ Protect(luaD_checkstack(L, n));
+ ra = RA(i); /* previous call may change the stack */
+ b = n;
+ L->top = ra + n;
+ }
+ for (j = 0; j < b; j++) {
+ if (j < n) {
+ setobjs2s(L, ra + j, ci->base - n + j);
+ }
+ else {
+ setnilvalue(ra + j);
+ }
+ }
+ continue;
+ }
+ }
+ }
+}
+
diff --git a/app/lua/lvm.h b/app/lua/lvm.h
new file mode 100644
index 00000000..bfe4f567
--- /dev/null
+++ b/app/lua/lvm.h
@@ -0,0 +1,36 @@
+/*
+** $Id: lvm.h,v 2.5.1.1 2007/12/27 13:02:25 roberto Exp $
+** Lua virtual machine
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lvm_h
+#define lvm_h
+
+
+#include "ldo.h"
+#include "lobject.h"
+#include "ltm.h"
+
+
+#define tostring(L,o) ((ttype(o) == LUA_TSTRING) || (luaV_tostring(L, o)))
+
+#define tonumber(o,n) (ttype(o) == LUA_TNUMBER || \
+ (((o) = luaV_tonumber(o,n)) != NULL))
+
+#define equalobj(L,o1,o2) \
+ (ttype(o1) == ttype(o2) && luaV_equalval(L, o1, o2))
+
+
+LUAI_FUNC int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r);
+LUAI_FUNC int luaV_equalval (lua_State *L, const TValue *t1, const TValue *t2);
+LUAI_FUNC const TValue *luaV_tonumber (const TValue *obj, TValue *n);
+LUAI_FUNC int luaV_tostring (lua_State *L, StkId obj);
+LUAI_FUNC void luaV_gettable (lua_State *L, const TValue *t, TValue *key,
+ StkId val);
+LUAI_FUNC void luaV_settable (lua_State *L, const TValue *t, TValue *key,
+ StkId val);
+LUAI_FUNC void luaV_execute (lua_State *L, int nexeccalls);
+LUAI_FUNC void luaV_concat (lua_State *L, int total, int last);
+
+#endif
diff --git a/app/lua/lzio.c b/app/lua/lzio.c
new file mode 100644
index 00000000..fc3ce0c1
--- /dev/null
+++ b/app/lua/lzio.c
@@ -0,0 +1,85 @@
+/*
+** $Id: lzio.c,v 1.31.1.1 2007/12/27 13:02:25 roberto Exp $
+** a generic input stream interface
+** See Copyright Notice in lua.h
+*/
+
+
+#include "c_string.h"
+
+#define lzio_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "llimits.h"
+#include "lmem.h"
+#include "lstate.h"
+#include "lzio.h"
+
+
+int ICACHE_FLASH_ATTR luaZ_fill (ZIO *z) {
+ size_t size;
+ lua_State *L = z->L;
+ const char *buff;
+ lua_unlock(L);
+ buff = z->reader(L, z->data, &size);
+ lua_lock(L);
+ if (buff == NULL || size == 0) return EOZ;
+ z->n = size - 1;
+ z->p = buff;
+ return char2int(*(z->p++));
+}
+
+
+int ICACHE_FLASH_ATTR luaZ_lookahead (ZIO *z) {
+ if (z->n == 0) {
+ if (luaZ_fill(z) == EOZ)
+ return EOZ;
+ else {
+ z->n++; /* luaZ_fill removed first byte; put back it */
+ z->p--;
+ }
+ }
+ return char2int(*z->p);
+}
+
+
+void ICACHE_FLASH_ATTR luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, void *data) {
+ z->L = L;
+ z->reader = reader;
+ z->data = data;
+ z->n = z->i = 0;
+ z->p = NULL;
+}
+
+
+/* --------------------------------------------------------------- read --- */
+size_t ICACHE_FLASH_ATTR luaZ_read (ZIO *z, void *b, size_t n) {
+ while (n) {
+ size_t m;
+ if (luaZ_lookahead(z) == EOZ)
+ return n; /* return number of missing bytes */
+ m = (n <= z->n) ? n : z->n; /* min. between n and z->n */
+ if (b)
+ c_memcpy(b, z->p, m);
+ z->n -= m;
+ z->i += m;
+ z->p += m;
+ if (b)
+ b = (char *)b + m;
+ n -= m;
+ }
+ return 0;
+}
+
+/* ------------------------------------------------------------------------ */
+char *ICACHE_FLASH_ATTR luaZ_openspace (lua_State *L, Mbuffer *buff, size_t n) {
+ if (n > buff->buffsize) {
+ if (n < LUA_MINBUFFER) n = LUA_MINBUFFER;
+ luaZ_resizebuffer(L, buff, n);
+ }
+ return buff->buffer;
+}
+
+
diff --git a/app/lua/lzio.h b/app/lua/lzio.h
new file mode 100644
index 00000000..7a31475e
--- /dev/null
+++ b/app/lua/lzio.h
@@ -0,0 +1,72 @@
+/*
+** $Id: lzio.h,v 1.21.1.1 2007/12/27 13:02:25 roberto Exp $
+** Buffered streams
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef lzio_h
+#define lzio_h
+
+#include "lua.h"
+
+#include "lmem.h"
+
+
+#define EOZ (-1) /* end of stream */
+
+typedef struct Zio ZIO;
+
+#define char2int(c) cast(int, cast(unsigned char, (c)))
+
+#define zgetc(z) (((z)->n--)>0 ? char2int(*(z)->p++) : luaZ_fill(z))
+
+typedef struct Mbuffer {
+ char *buffer;
+ size_t n;
+ size_t buffsize;
+} Mbuffer;
+
+#define luaZ_initbuffer(L, buff) ((buff)->buffer = NULL, (buff)->n = 0, (buff)->buffsize = 0)
+
+#define luaZ_buffer(buff) ((buff)->buffer)
+#define luaZ_sizebuffer(buff) ((buff)->buffsize)
+#define luaZ_bufflen(buff) ((buff)->n)
+
+#define luaZ_resetbuffer(buff) ((buff)->n = 0)
+
+
+#define luaZ_resizebuffer(L, buff, size) \
+ (luaM_reallocvector(L, (buff)->buffer, (buff)->buffsize, size, char), \
+ (buff)->buffsize = size)
+
+#define luaZ_freebuffer(L, buff) luaZ_resizebuffer(L, buff, 0)
+
+#define luaZ_freebuffer(L, buff) luaZ_resizebuffer(L, buff, 0)
+#define luaZ_get_base_address(zio) ((const char *)((zio)->reader(NULL, (zio)->data, NULL)))
+#define luaZ_direct_mode(zio) (luaZ_get_base_address(zio) != NULL)
+#define luaZ_get_crt_address(zio) (luaZ_get_base_address(zio) + (zio)->i)
+
+LUAI_FUNC char *luaZ_openspace (lua_State *L, Mbuffer *buff, size_t n);
+LUAI_FUNC void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader,
+ void *data);
+LUAI_FUNC size_t luaZ_read (ZIO* z, void* b, size_t n); /* read next n bytes */
+LUAI_FUNC int luaZ_lookahead (ZIO *z);
+
+
+
+/* --------- Private Part ------------------ */
+
+struct Zio {
+ size_t n; /* bytes still unread */
+ size_t i; /* buffer offset */
+ const char *p; /* current position in buffer */
+ lua_Reader reader;
+ void* data; /* additional data */
+ lua_State *L; /* Lua state (for reader) */
+};
+
+
+LUAI_FUNC int luaZ_fill (ZIO *z);
+
+#endif
diff --git a/app/lua/not_ported/loslib.c b/app/lua/not_ported/loslib.c
new file mode 100644
index 00000000..3484180a
--- /dev/null
+++ b/app/lua/not_ported/loslib.c
@@ -0,0 +1,247 @@
+/*
+** $Id: loslib.c,v 1.19.1.3 2008/01/18 16:38:18 roberto Exp $
+** Standard Operating System library
+** See Copyright Notice in lua.h
+*/
+
+
+#include
+#include
+#include
+#include
+#include
+
+#define loslib_c
+#define LUA_LIB
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+#include "lrotable.h"
+
+
+static int os_pushresult (lua_State *L, int i, const char *filename) {
+ int en = errno; /* calls to Lua API may change this value */
+ if (i) {
+ lua_pushboolean(L, 1);
+ return 1;
+ }
+ else {
+ lua_pushnil(L);
+ lua_pushfstring(L, "%s: %s", filename, strerror(en));
+ lua_pushinteger(L, en);
+ return 3;
+ }
+}
+
+
+static int os_execute (lua_State *L) {
+ lua_pushinteger(L, system(luaL_optstring(L, 1, NULL)));
+ return 1;
+}
+
+
+static int os_remove (lua_State *L) {
+ const char *filename = luaL_checkstring(L, 1);
+ return os_pushresult(L, remove(filename) == 0, filename);
+}
+
+
+static int os_rename (lua_State *L) {
+ const char *fromname = luaL_checkstring(L, 1);
+ const char *toname = luaL_checkstring(L, 2);
+ return os_pushresult(L, rename(fromname, toname) == 0, fromname);
+}
+
+
+static int os_tmpname (lua_State *L) {
+ char buff[LUA_TMPNAMBUFSIZE];
+ int err;
+ lua_tmpnam(buff, err);
+ if (err)
+ return luaL_error(L, "unable to generate a unique filename");
+ lua_pushstring(L, buff);
+ return 1;
+}
+
+
+static int os_getenv (lua_State *L) {
+ lua_pushstring(L, getenv(luaL_checkstring(L, 1))); /* if NULL push nil */
+ return 1;
+}
+
+
+static int os_clock (lua_State *L) {
+ lua_pushnumber(L, ((lua_Number)clock())/(lua_Number)CLOCKS_PER_SEC);
+ return 1;
+}
+
+
+/*
+** {======================================================
+** Time/Date operations
+** { year=%Y, month=%m, day=%d, hour=%H, min=%M, sec=%S,
+** wday=%w+1, yday=%j, isdst=? }
+** =======================================================
+*/
+
+static void setfield (lua_State *L, const char *key, int value) {
+ lua_pushinteger(L, value);
+ lua_setfield(L, -2, key);
+}
+
+static void setboolfield (lua_State *L, const char *key, int value) {
+ if (value < 0) /* undefined? */
+ return; /* does not set field */
+ lua_pushboolean(L, value);
+ lua_setfield(L, -2, key);
+}
+
+static int getboolfield (lua_State *L, const char *key) {
+ int res;
+ lua_getfield(L, -1, key);
+ res = lua_isnil(L, -1) ? -1 : lua_toboolean(L, -1);
+ lua_pop(L, 1);
+ return res;
+}
+
+
+static int getfield (lua_State *L, const char *key, int d) {
+ int res;
+ lua_getfield(L, -1, key);
+ if (lua_isnumber(L, -1))
+ res = (int)lua_tointeger(L, -1);
+ else {
+ if (d < 0)
+ return luaL_error(L, "field " LUA_QS " missing in date table", key);
+ res = d;
+ }
+ lua_pop(L, 1);
+ return res;
+}
+
+
+static int os_date (lua_State *L) {
+ const char *s = luaL_optstring(L, 1, "%c");
+ time_t t = luaL_opt(L, (time_t)luaL_checknumber, 2, time(NULL));
+ struct tm *stm;
+ if (*s == '!') { /* UTC? */
+ stm = gmtime(&t);
+ s++; /* skip `!' */
+ }
+ else
+ stm = localtime(&t);
+ if (stm == NULL) /* invalid date? */
+ lua_pushnil(L);
+ else if (strcmp(s, "*t") == 0) {
+ lua_createtable(L, 0, 9); /* 9 = number of fields */
+ setfield(L, "sec", stm->tm_sec);
+ setfield(L, "min", stm->tm_min);
+ setfield(L, "hour", stm->tm_hour);
+ setfield(L, "day", stm->tm_mday);
+ setfield(L, "month", stm->tm_mon+1);
+ setfield(L, "year", stm->tm_year+1900);
+ setfield(L, "wday", stm->tm_wday+1);
+ setfield(L, "yday", stm->tm_yday+1);
+ setboolfield(L, "isdst", stm->tm_isdst);
+ }
+ else {
+ char cc[3];
+ luaL_Buffer b;
+ cc[0] = '%'; cc[2] = '\0';
+ luaL_buffinit(L, &b);
+ for (; *s; s++) {
+ if (*s != '%' || *(s + 1) == '\0') /* no conversion specifier? */
+ luaL_addchar(&b, *s);
+ else {
+ size_t reslen;
+ char buff[200]; /* should be big enough for any conversion result */
+ cc[1] = *(++s);
+ reslen = strftime(buff, sizeof(buff), cc, stm);
+ luaL_addlstring(&b, buff, reslen);
+ }
+ }
+ luaL_pushresult(&b);
+ }
+ return 1;
+}
+
+
+static int os_time (lua_State *L) {
+ time_t t;
+ if (lua_isnoneornil(L, 1)) /* called without args? */
+ t = time(NULL); /* get current time */
+ else {
+ struct tm ts;
+ luaL_checktype(L, 1, LUA_TTABLE);
+ lua_settop(L, 1); /* make sure table is at the top */
+ ts.tm_sec = getfield(L, "sec", 0);
+ ts.tm_min = getfield(L, "min", 0);
+ ts.tm_hour = getfield(L, "hour", 12);
+ ts.tm_mday = getfield(L, "day", -1);
+ ts.tm_mon = getfield(L, "month", -1) - 1;
+ ts.tm_year = getfield(L, "year", -1) - 1900;
+ ts.tm_isdst = getboolfield(L, "isdst");
+ t = mktime(&ts);
+ }
+ if (t == (time_t)(-1))
+ lua_pushnil(L);
+ else
+ lua_pushnumber(L, (lua_Number)t);
+ return 1;
+}
+
+#if !defined LUA_NUMBER_INTEGRAL
+static int os_difftime (lua_State *L) {
+ lua_pushnumber(L, difftime((time_t)(luaL_checknumber(L, 1)),
+ (time_t)(luaL_optnumber(L, 2, 0))));
+ return 1;
+}
+#endif
+
+/* }====================================================== */
+
+
+static int os_setlocale (lua_State *L) {
+ static const int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY,
+ LC_NUMERIC, LC_TIME};
+ static const char *const catnames[] = {"all", "collate", "ctype", "monetary",
+ "numeric", "time", NULL};
+ const char *l = luaL_optstring(L, 1, NULL);
+ int op = luaL_checkoption(L, 2, "all", catnames);
+ lua_pushstring(L, setlocale(cat[op], l));
+ return 1;
+}
+
+
+static int os_exit (lua_State *L) {
+ c_exit(luaL_optint(L, 1, EXIT_SUCCESS));
+}
+
+#define MIN_OPT_LEVEL 1
+#include "lrodefs.h"
+const LUA_REG_TYPE syslib[] = {
+ {LSTRKEY("clock"), LFUNCVAL(os_clock)},
+ {LSTRKEY("date"), LFUNCVAL(os_date)},
+#if !defined LUA_NUMBER_INTEGRAL
+ {LSTRKEY("difftime"), LFUNCVAL(os_difftime)},
+#endif
+ {LSTRKEY("execute"), LFUNCVAL(os_execute)},
+ {LSTRKEY("exit"), LFUNCVAL(os_exit)},
+ {LSTRKEY("getenv"), LFUNCVAL(os_getenv)},
+ {LSTRKEY("remove"), LFUNCVAL(os_remove)},
+ {LSTRKEY("rename"), LFUNCVAL(os_rename)},
+ {LSTRKEY("setlocale"), LFUNCVAL(os_setlocale)},
+ {LSTRKEY("time"), LFUNCVAL(os_time)},
+ {LSTRKEY("tmpname"), LFUNCVAL(os_tmpname)},
+ {LNILKEY, LNILVAL}
+};
+
+/* }====================================================== */
+
+
+
+LUALIB_API int luaopen_os (lua_State *L) {
+ LREGISTER(L, LUA_OSLIBNAME, syslib);
+}
diff --git a/app/lua/not_ported/luac.c b/app/lua/not_ported/luac.c
new file mode 100644
index 00000000..7063c425
--- /dev/null
+++ b/app/lua/not_ported/luac.c
@@ -0,0 +1,242 @@
+/*
+** $Id: luac.c,v 1.54 2006/06/02 17:37:11 lhf Exp $
+** Lua compiler (saves bytecodes to files; also list bytecodes)
+** See Copyright Notice in lua.h
+*/
+
+#include "c_errno.h"
+#include "c_stdio.h"
+#include "c_stdlib.h"
+#include "c_string.h"
+
+#define luac_c
+#define LUA_CORE
+
+#include "lua.h"
+#include "lauxlib.h"
+
+#include "ldo.h"
+#include "lfunc.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lstring.h"
+#include "lundump.h"
+
+#define PROGNAME "luac" /* default program name */
+#define OUTPUT PROGNAME ".out" /* default output file */
+
+static int listing=0; /* list bytecodes? */
+static int dumping=1; /* dump bytecodes? */
+static int stripping=0; /* strip debug information? */
+static char Output[]={ OUTPUT }; /* default output file name */
+static const char* output=Output; /* actual output file name */
+static const char* progname=PROGNAME; /* actual program name */
+static DumpTargetInfo target;
+
+static void fatal(const char* message)
+{
+ fprintf(stderr,"%s: %s\n",progname,message);
+ exit(EXIT_FAILURE);
+}
+
+static void cannot(const char* what)
+{
+ fprintf(stderr,"%s: cannot %s %s: %s\n",progname,what,output,strerror(errno));
+ exit(EXIT_FAILURE);
+}
+
+static void usage(const char* message)
+{
+ if (*message=='-')
+ fprintf(stderr,"%s: unrecognized option " LUA_QS "\n",progname,message);
+ else
+ fprintf(stderr,"%s: %s\n",progname,message);
+ fprintf(stderr,
+ "usage: %s [options] [filenames].\n"
+ "Available options are:\n"
+ " - process stdin\n"
+ " -l list\n"
+ " -o name output to file " LUA_QL("name") " (default is \"%s\")\n"
+ " -p parse only\n"
+ " -s strip debug information\n"
+ " -v show version information\n"
+ " -cci bits cross-compile with given integer size\n"
+ " -ccn type bits cross-compile with given lua_Number type and size\n"
+ " -cce endian cross-compile with given endianness ('big' or 'little')\n"
+ " -- stop handling options\n",
+ progname,Output);
+ exit(EXIT_FAILURE);
+}
+
+#define IS(s) (strcmp(argv[i],s)==0)
+
+static int doargs(int argc, char* argv[])
+{
+ int i;
+ int version=0;
+ if (argv[0]!=NULL && *argv[0]!=0) progname=argv[0];
+ for (i=1; itop+(i))->l.p)
+
+static const Proto* combine(lua_State* L, int n)
+{
+ if (n==1)
+ return toproto(L,-1);
+ else
+ {
+ int i,pc;
+ Proto* f=luaF_newproto(L);
+ setptvalue2s(L,L->top,f); incr_top(L);
+ f->source=luaS_newliteral(L,"=(" PROGNAME ")");
+ f->maxstacksize=1;
+ pc=2*n+1;
+ f->code=luaM_newvector(L,pc,Instruction);
+ f->sizecode=pc;
+ f->p=luaM_newvector(L,n,Proto*);
+ f->sizep=n;
+ pc=0;
+ for (i=0; ip[i]=toproto(L,i-n-1);
+ f->code[pc++]=CREATE_ABx(OP_CLOSURE,0,i);
+ f->code[pc++]=CREATE_ABC(OP_CALL,0,1,1);
+ }
+ f->code[pc++]=CREATE_ABC(OP_RETURN,0,1,0);
+ return f;
+ }
+}
+
+static int writer(lua_State* L, const void* p, size_t size, void* u)
+{
+ UNUSED(L);
+ return (fwrite(p,size,1,(FILE*)u)!=1) && (size!=0);
+}
+
+struct Smain {
+ int argc;
+ char** argv;
+};
+
+static int pmain(lua_State* L)
+{
+ struct Smain* s = (struct Smain*)lua_touserdata(L, 1);
+ int argc=s->argc;
+ char** argv=s->argv;
+ const Proto* f;
+ int i;
+ if (!lua_checkstack(L,argc)) fatal("too many input files");
+ for (i=0; i1);
+ if (dumping)
+ {
+ FILE* D= (output==NULL) ? stdout : fopen(output,"wb");
+ if (D==NULL) cannot("open");
+ lua_lock(L);
+ int result=luaU_dump_crosscompile(L,f,writer,D,stripping,target);
+ lua_unlock(L);
+ if (result==LUA_ERR_CC_INTOVERFLOW) fatal("value too big or small for target integer type");
+ if (result==LUA_ERR_CC_NOTINTEGER) fatal("target lua_Number is integral but fractional value found");
+ if (ferror(D)) cannot("write");
+ if (fclose(D)) cannot("close");
+ }
+ return 0;
+}
+
+int main(int argc, char* argv[])
+{
+ lua_State* L;
+ struct Smain s;
+
+ int test=1;
+ target.little_endian=*(char*)&test;
+ target.sizeof_int=sizeof(int);
+ target.sizeof_strsize_t=sizeof(strsize_t);
+ target.sizeof_lua_Number=sizeof(lua_Number);
+ target.lua_Number_integral=(((lua_Number)0.5)==0);
+ target.is_arm_fpa=0;
+
+ int i=doargs(argc,argv);
+ argc-=i; argv+=i;
+ if (argc<=0) usage("no input files given");
+ L=lua_open();
+ if (L==NULL) fatal("not enough memory for state");
+ s.argc=argc;
+ s.argv=argv;
+ if (lua_cpcall(L,pmain,&s)!=0) fatal(lua_tostring(L,-1));
+ lua_close(L);
+ return EXIT_SUCCESS;
+}
diff --git a/app/lua/not_ported/print.c b/app/lua/not_ported/print.c
new file mode 100644
index 00000000..dccce975
--- /dev/null
+++ b/app/lua/not_ported/print.c
@@ -0,0 +1,227 @@
+/*
+** $Id: print.c,v 1.55a 2006/05/31 13:30:05 lhf Exp $
+** print bytecodes
+** See Copyright Notice in lua.h
+*/
+
+#include "c_ctype.h"
+#include "c_stdio.h"
+
+#define luac_c
+#define LUA_CORE
+
+#include "ldebug.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lundump.h"
+
+#define PrintFunction luaU_print
+
+#define Sizeof(x) ((int)sizeof(x))
+#define VOID(p) ((const void*)(p))
+
+static void PrintString(const TString* ts)
+{
+ const char* s=getstr(ts);
+ size_t i,n=ts->tsv.len;
+ putchar('"');
+ for (i=0; ik[i];
+ switch (ttype(o))
+ {
+ case LUA_TNIL:
+ printf("nil");
+ break;
+ case LUA_TBOOLEAN:
+ printf(bvalue(o) ? "true" : "false");
+ break;
+ case LUA_TNUMBER:
+ printf(LUA_NUMBER_FMT,nvalue(o));
+ break;
+ case LUA_TSTRING:
+ PrintString(rawtsvalue(o));
+ break;
+ default: /* cannot happen */
+ printf("? type=%d",ttype(o));
+ break;
+ }
+}
+
+static void PrintCode(const Proto* f)
+{
+ const Instruction* code=f->code;
+ int pc,n=f->sizecode;
+ for (pc=0; pc0) printf("[%d]\t",line); else printf("[-]\t");
+ printf("%-9s\t",luaP_opnames[o]);
+ switch (getOpMode(o))
+ {
+ case iABC:
+ printf("%d",a);
+ if (getBMode(o)!=OpArgN) printf(" %d",ISK(b) ? (-1-INDEXK(b)) : b);
+ if (getCMode(o)!=OpArgN) printf(" %d",ISK(c) ? (-1-INDEXK(c)) : c);
+ break;
+ case iABx:
+ if (getBMode(o)==OpArgK) printf("%d %d",a,-1-bx); else printf("%d %d",a,bx);
+ break;
+ case iAsBx:
+ if (o==OP_JMP) printf("%d",sbx); else printf("%d %d",a,sbx);
+ break;
+ }
+ switch (o)
+ {
+ case OP_LOADK:
+ printf("\t; "); PrintConstant(f,bx);
+ break;
+ case OP_GETUPVAL:
+ case OP_SETUPVAL:
+ printf("\t; %s", (f->sizeupvalues>0) ? getstr(f->upvalues[b]) : "-");
+ break;
+ case OP_GETGLOBAL:
+ case OP_SETGLOBAL:
+ printf("\t; %s",svalue(&f->k[bx]));
+ break;
+ case OP_GETTABLE:
+ case OP_SELF:
+ if (ISK(c)) { printf("\t; "); PrintConstant(f,INDEXK(c)); }
+ break;
+ case OP_SETTABLE:
+ case OP_ADD:
+ case OP_SUB:
+ case OP_MUL:
+ case OP_DIV:
+ case OP_POW:
+ case OP_EQ:
+ case OP_LT:
+ case OP_LE:
+ if (ISK(b) || ISK(c))
+ {
+ printf("\t; ");
+ if (ISK(b)) PrintConstant(f,INDEXK(b)); else printf("-");
+ printf(" ");
+ if (ISK(c)) PrintConstant(f,INDEXK(c)); else printf("-");
+ }
+ break;
+ case OP_JMP:
+ case OP_FORLOOP:
+ case OP_FORPREP:
+ printf("\t; to %d",sbx+pc+2);
+ break;
+ case OP_CLOSURE:
+ printf("\t; %p",VOID(f->p[bx]));
+ break;
+ case OP_SETLIST:
+ if (c==0) printf("\t; %d",(int)code[++pc]);
+ else printf("\t; %d",c);
+ break;
+ default:
+ break;
+ }
+ printf("\n");
+ }
+}
+
+#define SS(x) (x==1)?"":"s"
+#define S(x) x,SS(x)
+
+static void PrintHeader(const Proto* f)
+{
+ const char* s=getstr(f->source);
+ if (*s=='@' || *s=='=')
+ s++;
+ else if (*s==LUA_SIGNATURE[0])
+ s="(bstring)";
+ else
+ s="(string)";
+ printf("\n%s <%s:%d,%d> (%d instruction%s, %d bytes at %p)\n",
+ (f->linedefined==0)?"main":"function",s,
+ f->linedefined,f->lastlinedefined,
+ S(f->sizecode),f->sizecode*Sizeof(Instruction),VOID(f));
+ printf("%d%s param%s, %d slot%s, %d upvalue%s, ",
+ f->numparams,f->is_vararg?"+":"",SS(f->numparams),
+ S(f->maxstacksize),S(f->nups));
+ printf("%d local%s, %d constant%s, %d function%s\n",
+ S(f->sizelocvars),S(f->sizek),S(f->sizep));
+}
+
+static void PrintConstants(const Proto* f)
+{
+ int i,n=f->sizek;
+ printf("constants (%d) for %p:\n",n,VOID(f));
+ for (i=0; isizelocvars;
+ printf("locals (%d) for %p:\n",n,VOID(f));
+ for (i=0; ilocvars[i].varname),f->locvars[i].startpc+1,f->locvars[i].endpc+1);
+ }
+}
+
+static void PrintUpvalues(const Proto* f)
+{
+ int i,n=f->sizeupvalues;
+ printf("upvalues (%d) for %p:\n",n,VOID(f));
+ if (f->upvalues==NULL) return;
+ for (i=0; iupvalues[i]));
+ }
+}
+
+void PrintFunction(const Proto* f, int full)
+{
+ int i,n=f->sizep;
+ PrintHeader(f);
+ PrintCode(f);
+ if (full)
+ {
+ PrintConstants(f);
+ PrintLocals(f);
+ PrintUpvalues(f);
+ }
+ for (i=0; ip[i],full);
+}
diff --git a/app/lua/not_used/ldblib.c b/app/lua/not_used/ldblib.c
new file mode 100644
index 00000000..e2bad1c1
--- /dev/null
+++ b/app/lua/not_used/ldblib.c
@@ -0,0 +1,406 @@
+/*
+** $Id: ldblib.c,v 1.104.1.4 2009/08/04 18:50:18 roberto Exp $
+** Interface from Lua to its debug API
+** See Copyright Notice in lua.h
+*/
+
+
+#include "c_stdio.h"
+#include "c_stdlib.h"
+#include "c_string.h"
+
+#define ldblib_c
+#define LUA_LIB
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+#include "lrotable.h"
+
+
+
+static int ICACHE_FLASH_ATTR db_getregistry (lua_State *L) {
+ lua_pushvalue(L, LUA_REGISTRYINDEX);
+ return 1;
+}
+
+
+static int ICACHE_FLASH_ATTR db_getmetatable (lua_State *L) {
+ luaL_checkany(L, 1);
+ if (!lua_getmetatable(L, 1)) {
+ lua_pushnil(L); /* no metatable */
+ }
+ return 1;
+}
+
+
+static int ICACHE_FLASH_ATTR db_setmetatable (lua_State *L) {
+ int t = lua_type(L, 2);
+ luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2,
+ "nil or table expected");
+ lua_settop(L, 2);
+ lua_pushboolean(L, lua_setmetatable(L, 1));
+ return 1;
+}
+
+
+static int ICACHE_FLASH_ATTR db_getfenv (lua_State *L) {
+ luaL_checkany(L, 1);
+ lua_getfenv(L, 1);
+ return 1;
+}
+
+
+static int ICACHE_FLASH_ATTR db_setfenv (lua_State *L) {
+ luaL_checktype(L, 2, LUA_TTABLE);
+ lua_settop(L, 2);
+ if (lua_setfenv(L, 1) == 0)
+ luaL_error(L, LUA_QL("setfenv")
+ " cannot change environment of given object");
+ return 1;
+}
+
+
+static void ICACHE_FLASH_ATTR settabss (lua_State *L, const char *i, const char *v) {
+ lua_pushstring(L, v);
+ lua_setfield(L, -2, i);
+}
+
+
+static void ICACHE_FLASH_ATTR settabsi (lua_State *L, const char *i, int v) {
+ lua_pushinteger(L, v);
+ lua_setfield(L, -2, i);
+}
+
+
+static lua_State *ICACHE_FLASH_ATTR getthread (lua_State *L, int *arg) {
+ if (lua_isthread(L, 1)) {
+ *arg = 1;
+ return lua_tothread(L, 1);
+ }
+ else {
+ *arg = 0;
+ return L;
+ }
+}
+
+
+static void ICACHE_FLASH_ATTR treatstackoption (lua_State *L, lua_State *L1, const char *fname) {
+ if (L == L1) {
+ lua_pushvalue(L, -2);
+ lua_remove(L, -3);
+ }
+ else
+ lua_xmove(L1, L, 1);
+ lua_setfield(L, -2, fname);
+}
+
+
+static int ICACHE_FLASH_ATTR db_getinfo (lua_State *L) {
+ lua_Debug ar;
+ int arg;
+ lua_State *L1 = getthread(L, &arg);
+ const char *options = luaL_optstring(L, arg+2, "flnSu");
+ if (lua_isnumber(L, arg+1)) {
+ if (!lua_getstack(L1, (int)lua_tointeger(L, arg+1), &ar)) {
+ lua_pushnil(L); /* level out of range */
+ return 1;
+ }
+ }
+ else if (lua_isfunction(L, arg+1) || lua_islightfunction(L, arg+1)) {
+ lua_pushfstring(L, ">%s", options);
+ options = lua_tostring(L, -1);
+ lua_pushvalue(L, arg+1);
+ lua_xmove(L, L1, 1);
+ }
+ else
+ return luaL_argerror(L, arg+1, "function or level expected");
+ if (!lua_getinfo(L1, options, &ar))
+ return luaL_argerror(L, arg+2, "invalid option");
+ lua_createtable(L, 0, 2);
+ if (c_strchr(options, 'S')) {
+ settabss(L, "source", ar.source);
+ settabss(L, "short_src", ar.short_src);
+ settabsi(L, "linedefined", ar.linedefined);
+ settabsi(L, "lastlinedefined", ar.lastlinedefined);
+ settabss(L, "what", ar.what);
+ }
+ if (c_strchr(options, 'l'))
+ settabsi(L, "currentline", ar.currentline);
+ if (c_strchr(options, 'u'))
+ settabsi(L, "nups", ar.nups);
+ if (c_strchr(options, 'n')) {
+ settabss(L, "name", ar.name);
+ settabss(L, "namewhat", ar.namewhat);
+ }
+ if (c_strchr(options, 'L'))
+ treatstackoption(L, L1, "activelines");
+ if (c_strchr(options, 'f'))
+ treatstackoption(L, L1, "func");
+ return 1; /* return table */
+}
+
+
+static int ICACHE_FLASH_ATTR db_getlocal (lua_State *L) {
+ int arg;
+ lua_State *L1 = getthread(L, &arg);
+ lua_Debug ar;
+ const char *name;
+ if (!lua_getstack(L1, luaL_checkint(L, arg+1), &ar)) /* out of range? */
+ return luaL_argerror(L, arg+1, "level out of range");
+ name = lua_getlocal(L1, &ar, luaL_checkint(L, arg+2));
+ if (name) {
+ lua_xmove(L1, L, 1);
+ lua_pushstring(L, name);
+ lua_pushvalue(L, -2);
+ return 2;
+ }
+ else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+
+static int ICACHE_FLASH_ATTR db_setlocal (lua_State *L) {
+ int arg;
+ lua_State *L1 = getthread(L, &arg);
+ lua_Debug ar;
+ if (!lua_getstack(L1, luaL_checkint(L, arg+1), &ar)) /* out of range? */
+ return luaL_argerror(L, arg+1, "level out of range");
+ luaL_checkany(L, arg+3);
+ lua_settop(L, arg+3);
+ lua_xmove(L, L1, 1);
+ lua_pushstring(L, lua_setlocal(L1, &ar, luaL_checkint(L, arg+2)));
+ return 1;
+}
+
+
+static int ICACHE_FLASH_ATTR auxupvalue (lua_State *L, int get) {
+ const char *name;
+ int n = luaL_checkint(L, 2);
+ luaL_checktype(L, 1, LUA_TFUNCTION);
+ if (lua_iscfunction(L, 1)) return 0; /* cannot touch C upvalues from Lua */
+ name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n);
+ if (name == NULL) return 0;
+ lua_pushstring(L, name);
+ lua_insert(L, -(get+1));
+ return get + 1;
+}
+
+
+static int ICACHE_FLASH_ATTR db_getupvalue (lua_State *L) {
+ return auxupvalue(L, 1);
+}
+
+
+static int ICACHE_FLASH_ATTR db_setupvalue (lua_State *L) {
+ luaL_checkany(L, 3);
+ return auxupvalue(L, 0);
+}
+
+
+
+static const char KEY_HOOK = 'h';
+
+
+static void ICACHE_FLASH_ATTR hookf (lua_State *L, lua_Debug *ar) {
+ static const char *const hooknames[] =
+ {"call", "return", "line", "count", "tail return"};
+ lua_pushlightuserdata(L, (void *)&KEY_HOOK);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ lua_pushlightuserdata(L, L);
+ lua_rawget(L, -2);
+ if (lua_isfunction(L, -1)) {
+ lua_pushstring(L, hooknames[(int)ar->event]);
+ if (ar->currentline >= 0)
+ lua_pushinteger(L, ar->currentline);
+ else lua_pushnil(L);
+ lua_assert(lua_getinfo(L, "lS", ar));
+ lua_call(L, 2, 0);
+ }
+}
+
+
+static int ICACHE_FLASH_ATTR makemask (const char *smask, int count) {
+ int mask = 0;
+ if (c_strchr(smask, 'c')) mask |= LUA_MASKCALL;
+ if (c_strchr(smask, 'r')) mask |= LUA_MASKRET;
+ if (c_strchr(smask, 'l')) mask |= LUA_MASKLINE;
+ if (count > 0) mask |= LUA_MASKCOUNT;
+ return mask;
+}
+
+
+static char *ICACHE_FLASH_ATTR unmakemask (int mask, char *smask) {
+ int i = 0;
+ if (mask & LUA_MASKCALL) smask[i++] = 'c';
+ if (mask & LUA_MASKRET) smask[i++] = 'r';
+ if (mask & LUA_MASKLINE) smask[i++] = 'l';
+ smask[i] = '\0';
+ return smask;
+}
+
+
+static void ICACHE_FLASH_ATTR gethooktable (lua_State *L) {
+ lua_pushlightuserdata(L, (void *)&KEY_HOOK);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ if (!lua_istable(L, -1)) {
+ lua_pop(L, 1);
+ lua_createtable(L, 0, 1);
+ lua_pushlightuserdata(L, (void *)&KEY_HOOK);
+ lua_pushvalue(L, -2);
+ lua_rawset(L, LUA_REGISTRYINDEX);
+ }
+}
+
+
+static int ICACHE_FLASH_ATTR db_sethook (lua_State *L) {
+ int arg, mask, count;
+ lua_Hook func;
+ lua_State *L1 = getthread(L, &arg);
+ if (lua_isnoneornil(L, arg+1)) {
+ lua_settop(L, arg+1);
+ func = NULL; mask = 0; count = 0; /* turn off hooks */
+ }
+ else {
+ const char *smask = luaL_checkstring(L, arg+2);
+ luaL_checkanyfunction(L, arg+1);
+ count = luaL_optint(L, arg+3, 0);
+ func = hookf; mask = makemask(smask, count);
+ }
+ gethooktable(L);
+ lua_pushlightuserdata(L, L1);
+ lua_pushvalue(L, arg+1);
+ lua_rawset(L, -3); /* set new hook */
+ lua_pop(L, 1); /* remove hook table */
+ lua_sethook(L1, func, mask, count); /* set hooks */
+ return 0;
+}
+
+
+static int ICACHE_FLASH_ATTR db_gethook (lua_State *L) {
+ int arg;
+ lua_State *L1 = getthread(L, &arg);
+ char buff[5];
+ int mask = lua_gethookmask(L1);
+ lua_Hook hook = lua_gethook(L1);
+ if (hook != NULL && hook != hookf) /* external hook? */
+ lua_pushliteral(L, "external hook");
+ else {
+ gethooktable(L);
+ lua_pushlightuserdata(L, L1);
+ lua_rawget(L, -2); /* get hook */
+ lua_remove(L, -2); /* remove hook table */
+ }
+ lua_pushstring(L, unmakemask(mask, buff));
+ lua_pushinteger(L, lua_gethookcount(L1));
+ return 3;
+}
+
+
+static int ICACHE_FLASH_ATTR db_debug (lua_State *L) {
+ for (;;) {
+ char buffer[LUA_MAXINPUT];
+#if defined(LUA_USE_STDIO)
+ c_fputs("lua_debug> ", c_stderr);
+ if (c_fgets(buffer, sizeof(buffer), c_stdin) == 0 ||
+#else
+// luai_writestringerror("%s", "lua_debug>");
+ if (lua_readline(L, buffer, "lua_debug>") == 0 ||
+#endif
+ c_strcmp(buffer, "cont\n") == 0)
+ return 0;
+ if (luaL_loadbuffer(L, buffer, c_strlen(buffer), "=(debug command)") ||
+ lua_pcall(L, 0, 0, 0)) {
+#if defined(LUA_USE_STDIO)
+ c_fputs(lua_tostring(L, -1), c_stderr);
+ c_fputs("\n", c_stderr);
+#else
+ luai_writestringerror("%s\n", lua_tostring(L, -1));
+#endif
+ }
+ lua_settop(L, 0); /* remove eventual returns */
+ }
+}
+
+
+#define LEVELS1 12 /* size of the first part of the stack */
+#define LEVELS2 10 /* size of the second part of the stack */
+
+static int ICACHE_FLASH_ATTR db_errorfb (lua_State *L) {
+ int level;
+ int firstpart = 1; /* still before eventual `...' */
+ int arg;
+ lua_State *L1 = getthread(L, &arg);
+ lua_Debug ar;
+ if (lua_isnumber(L, arg+2)) {
+ level = (int)lua_tointeger(L, arg+2);
+ lua_pop(L, 1);
+ }
+ else
+ level = (L == L1) ? 1 : 0; /* level 0 may be this own function */
+ if (lua_gettop(L) == arg)
+ lua_pushliteral(L, "");
+ else if (!lua_isstring(L, arg+1)) return 1; /* message is not a string */
+ else lua_pushliteral(L, "\n");
+ lua_pushliteral(L, "stack traceback:");
+ while (lua_getstack(L1, level++, &ar)) {
+ if (level > LEVELS1 && firstpart) {
+ /* no more than `LEVELS2' more levels? */
+ if (!lua_getstack(L1, level+LEVELS2, &ar))
+ level--; /* keep going */
+ else {
+ lua_pushliteral(L, "\n\t..."); /* too many levels */
+ while (lua_getstack(L1, level+LEVELS2, &ar)) /* find last levels */
+ level++;
+ }
+ firstpart = 0;
+ continue;
+ }
+ lua_pushliteral(L, "\n\t");
+ lua_getinfo(L1, "Snl", &ar);
+ lua_pushfstring(L, "%s:", ar.short_src);
+ if (ar.currentline > 0)
+ lua_pushfstring(L, "%d:", ar.currentline);
+ if (*ar.namewhat != '\0') /* is there a name? */
+ lua_pushfstring(L, " in function " LUA_QS, ar.name);
+ else {
+ if (*ar.what == 'm') /* main? */
+ lua_pushfstring(L, " in main chunk");
+ else if (*ar.what == 'C' || *ar.what == 't')
+ lua_pushliteral(L, " ?"); /* C function or tail call */
+ else
+ lua_pushfstring(L, " in function <%s:%d>",
+ ar.short_src, ar.linedefined);
+ }
+ lua_concat(L, lua_gettop(L) - arg);
+ }
+ lua_concat(L, lua_gettop(L) - arg);
+ return 1;
+}
+
+#define MIN_OPT_LEVEL 1
+#include "lrodefs.h"
+const LUA_REG_TYPE dblib[] = {
+ {LSTRKEY("debug"), LFUNCVAL(db_debug)},
+ {LSTRKEY("getfenv"), LFUNCVAL(db_getfenv)},
+ {LSTRKEY("gethook"), LFUNCVAL(db_gethook)},
+ {LSTRKEY("getinfo"), LFUNCVAL(db_getinfo)},
+ {LSTRKEY("getlocal"), LFUNCVAL(db_getlocal)},
+ {LSTRKEY("getregistry"), LFUNCVAL(db_getregistry)},
+ {LSTRKEY("getmetatable"), LFUNCVAL(db_getmetatable)},
+ {LSTRKEY("getupvalue"), LFUNCVAL(db_getupvalue)},
+ {LSTRKEY("setfenv"), LFUNCVAL(db_setfenv)},
+ {LSTRKEY("sethook"), LFUNCVAL(db_sethook)},
+ {LSTRKEY("setlocal"), LFUNCVAL(db_setlocal)},
+ {LSTRKEY("setmetatable"), LFUNCVAL(db_setmetatable)},
+ {LSTRKEY("setupvalue"), LFUNCVAL(db_setupvalue)},
+ {LSTRKEY("traceback"), LFUNCVAL(db_errorfb)},
+ {LNILKEY, LNILVAL}
+};
+
+LUALIB_API int ICACHE_FLASH_ATTR luaopen_debug (lua_State *L) {
+ LREGISTER(L, LUA_DBLIBNAME, dblib);
+}
diff --git a/app/lwip/Makefile b/app/lwip/Makefile
new file mode 100644
index 00000000..37d6e747
--- /dev/null
+++ b/app/lwip/Makefile
@@ -0,0 +1,50 @@
+
+#############################################################
+# Required variables for each makefile
+# Discard this section from all parent makefiles
+# Expected variables (with automatic defaults):
+# CSRCS (all "C" files in the dir)
+# SUBDIRS (all subdirs with a Makefile)
+# GEN_LIBS - list of libs to be generated ()
+# GEN_IMAGES - list of images to be generated ()
+# COMPONENTS_xxx - a list of libs/objs in the form
+# subdir/lib to be extracted and rolled up into
+# a generated lib/image xxx.a ()
+#
+ifndef PDIR
+UP_EXTRACT_DIR = ..
+GEN_LIBS = liblwip.a
+COMPONENTS_liblwip = api/liblwipapi.a \
+ app/liblwipapp.a \
+ core/liblwipcore.a \
+ core/ipv4/liblwipipv4.a \
+ netif/liblwipnetif.a
+endif
+
+
+#############################################################
+# Configuration i.e. compile options etc.
+# Target specific stuff (defines etc.) goes in here!
+# Generally values applying to a tree are captured in the
+# makefile at its root level - these are then overridden
+# for a subtree within the makefile rooted therein
+#
+#DEFINES +=
+
+#############################################################
+# Recursion Magic - Don't touch this!!
+#
+# Each subtree potentially has an include directory
+# corresponding to the common APIs applicable to modules
+# rooted at that subtree. Accordingly, the INCLUDE PATH
+# of a module can only contain the include directories up
+# its parent path, and not its siblings
+#
+# Required for each makefile to inherit from the parent
+#
+
+INCLUDES := $(INCLUDES) -I $(PDIR)include
+INCLUDES += -I ./
+PDIR := ../$(PDIR)
+sinclude $(PDIR)Makefile
+
diff --git a/app/lwip/api/Makefile b/app/lwip/api/Makefile
new file mode 100644
index 00000000..69b027d2
--- /dev/null
+++ b/app/lwip/api/Makefile
@@ -0,0 +1,46 @@
+
+#############################################################
+# Required variables for each makefile
+# Discard this section from all parent makefiles
+# Expected variables (with automatic defaults):
+# CSRCS (all "C" files in the dir)
+# SUBDIRS (all subdirs with a Makefile)
+# GEN_LIBS - list of libs to be generated ()
+# GEN_IMAGES - list of images to be generated ()
+# COMPONENTS_xxx - a list of libs/objs in the form
+# subdir/lib to be extracted and rolled up into
+# a generated lib/image xxx.a ()
+#
+ifndef PDIR
+
+GEN_LIBS = liblwipapi.a
+
+endif
+
+
+#############################################################
+# Configuration i.e. compile options etc.
+# Target specific stuff (defines etc.) goes in here!
+# Generally values applying to a tree are captured in the
+# makefile at its root level - these are then overridden
+# for a subtree within the makefile rooted therein
+#
+#DEFINES +=
+
+#############################################################
+# Recursion Magic - Don't touch this!!
+#
+# Each subtree potentially has an include directory
+# corresponding to the common APIs applicable to modules
+# rooted at that subtree. Accordingly, the INCLUDE PATH
+# of a module can only contain the include directories up
+# its parent path, and not its siblings
+#
+# Required for each makefile to inherit from the parent
+#
+
+INCLUDES := $(INCLUDES) -I $(PDIR)include
+INCLUDES += -I ./
+PDIR := ../$(PDIR)
+sinclude $(PDIR)Makefile
+
diff --git a/app/lwip/api/api_lib.c b/app/lwip/api/api_lib.c
new file mode 100644
index 00000000..158325b0
--- /dev/null
+++ b/app/lwip/api/api_lib.c
@@ -0,0 +1,740 @@
+/**
+ * @file
+ * Sequential API External module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ *
+ */
+
+/* This is the part of the API that is linked with
+ the application */
+
+#include "lwip/opt.h"
+
+#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/api.h"
+#include "lwip/tcpip.h"
+#include "lwip/memp.h"
+
+#include "lwip/ip.h"
+#include "lwip/raw.h"
+#include "lwip/udp.h"
+#include "lwip/tcp.h"
+
+#include
+
+/**
+ * Create a new netconn (of a specific type) that has a callback function.
+ * The corresponding pcb is also created.
+ *
+ * @param t the type of 'connection' to create (@see enum netconn_type)
+ * @param proto the IP protocol for RAW IP pcbs
+ * @param callback a function to call on status changes (RX available, TX'ed)
+ * @return a newly allocated struct netconn or
+ * NULL on memory error
+ */
+struct netconn*
+netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, netconn_callback callback)
+{
+ struct netconn *conn;
+ struct api_msg msg;
+
+ conn = netconn_alloc(t, callback);
+ if (conn != NULL) {
+ msg.function = do_newconn;
+ msg.msg.msg.n.proto = proto;
+ msg.msg.conn = conn;
+ if (TCPIP_APIMSG(&msg) != ERR_OK) {
+ LWIP_ASSERT("freeing conn without freeing pcb", conn->pcb.tcp == NULL);
+ LWIP_ASSERT("conn has no op_completed", sys_sem_valid(&conn->op_completed));
+ LWIP_ASSERT("conn has no recvmbox", sys_mbox_valid(&conn->recvmbox));
+#if LWIP_TCP
+ LWIP_ASSERT("conn->acceptmbox shouldn't exist", !sys_mbox_valid(&conn->acceptmbox));
+#endif /* LWIP_TCP */
+ sys_sem_free(&conn->op_completed);
+ sys_mbox_free(&conn->recvmbox);
+ memp_free(MEMP_NETCONN, conn);
+ return NULL;
+ }
+ }
+ return conn;
+}
+
+/**
+ * Close a netconn 'connection' and free its resources.
+ * UDP and RAW connection are completely closed, TCP pcbs might still be in a waitstate
+ * after this returns.
+ *
+ * @param conn the netconn to delete
+ * @return ERR_OK if the connection was deleted
+ */
+err_t
+netconn_delete(struct netconn *conn)
+{
+ struct api_msg msg;
+
+ /* No ASSERT here because possible to get a (conn == NULL) if we got an accept error */
+ if (conn == NULL) {
+ return ERR_OK;
+ }
+
+ msg.function = do_delconn;
+ msg.msg.conn = conn;
+ tcpip_apimsg(&msg);
+
+ netconn_free(conn);
+
+ /* don't care for return value of do_delconn since it only calls void functions */
+
+ return ERR_OK;
+}
+
+/**
+ * Get the local or remote IP address and port of a netconn.
+ * For RAW netconns, this returns the protocol instead of a port!
+ *
+ * @param conn the netconn to query
+ * @param addr a pointer to which to save the IP address
+ * @param port a pointer to which to save the port (or protocol for RAW)
+ * @param local 1 to get the local IP address, 0 to get the remote one
+ * @return ERR_CONN for invalid connections
+ * ERR_OK if the information was retrieved
+ */
+err_t
+netconn_getaddr(struct netconn *conn, ip_addr_t *addr, u16_t *port, u8_t local)
+{
+ struct api_msg msg;
+ err_t err;
+
+ LWIP_ERROR("netconn_getaddr: invalid conn", (conn != NULL), return ERR_ARG;);
+ LWIP_ERROR("netconn_getaddr: invalid addr", (addr != NULL), return ERR_ARG;);
+ LWIP_ERROR("netconn_getaddr: invalid port", (port != NULL), return ERR_ARG;);
+
+ msg.function = do_getaddr;
+ msg.msg.conn = conn;
+ msg.msg.msg.ad.ipaddr = addr;
+ msg.msg.msg.ad.port = port;
+ msg.msg.msg.ad.local = local;
+ err = TCPIP_APIMSG(&msg);
+
+ NETCONN_SET_SAFE_ERR(conn, err);
+ return err;
+}
+
+/**
+ * Bind a netconn to a specific local IP address and port.
+ * Binding one netconn twice might not always be checked correctly!
+ *
+ * @param conn the netconn to bind
+ * @param addr the local IP address to bind the netconn to (use IP_ADDR_ANY
+ * to bind to all addresses)
+ * @param port the local port to bind the netconn to (not used for RAW)
+ * @return ERR_OK if bound, any other err_t on failure
+ */
+err_t
+netconn_bind(struct netconn *conn, ip_addr_t *addr, u16_t port)
+{
+ struct api_msg msg;
+ err_t err;
+
+ LWIP_ERROR("netconn_bind: invalid conn", (conn != NULL), return ERR_ARG;);
+
+ msg.function = do_bind;
+ msg.msg.conn = conn;
+ msg.msg.msg.bc.ipaddr = addr;
+ msg.msg.msg.bc.port = port;
+ err = TCPIP_APIMSG(&msg);
+
+ NETCONN_SET_SAFE_ERR(conn, err);
+ return err;
+}
+
+/**
+ * Connect a netconn to a specific remote IP address and port.
+ *
+ * @param conn the netconn to connect
+ * @param addr the remote IP address to connect to
+ * @param port the remote port to connect to (no used for RAW)
+ * @return ERR_OK if connected, return value of tcp_/udp_/raw_connect otherwise
+ */
+err_t
+netconn_connect(struct netconn *conn, ip_addr_t *addr, u16_t port)
+{
+ struct api_msg msg;
+ err_t err;
+
+ LWIP_ERROR("netconn_connect: invalid conn", (conn != NULL), return ERR_ARG;);
+
+ msg.function = do_connect;
+ msg.msg.conn = conn;
+ msg.msg.msg.bc.ipaddr = addr;
+ msg.msg.msg.bc.port = port;
+ /* This is the only function which need to not block tcpip_thread */
+ err = tcpip_apimsg(&msg);
+
+ NETCONN_SET_SAFE_ERR(conn, err);
+ return err;
+}
+
+/**
+ * Disconnect a netconn from its current peer (only valid for UDP netconns).
+ *
+ * @param conn the netconn to disconnect
+ * @return TODO: return value is not set here...
+ */
+err_t
+netconn_disconnect(struct netconn *conn)
+{
+ struct api_msg msg;
+ err_t err;
+
+ LWIP_ERROR("netconn_disconnect: invalid conn", (conn != NULL), return ERR_ARG;);
+
+ msg.function = do_disconnect;
+ msg.msg.conn = conn;
+ err = TCPIP_APIMSG(&msg);
+
+ NETCONN_SET_SAFE_ERR(conn, err);
+ return err;
+}
+
+/**
+ * Set a TCP netconn into listen mode
+ *
+ * @param conn the tcp netconn to set to listen mode
+ * @param backlog the listen backlog, only used if TCP_LISTEN_BACKLOG==1
+ * @return ERR_OK if the netconn was set to listen (UDP and RAW netconns
+ * don't return any error (yet?))
+ */
+err_t
+netconn_listen_with_backlog(struct netconn *conn, u8_t backlog)
+{
+#if LWIP_TCP
+ struct api_msg msg;
+ err_t err;
+
+ /* This does no harm. If TCP_LISTEN_BACKLOG is off, backlog is unused. */
+ LWIP_UNUSED_ARG(backlog);
+
+ LWIP_ERROR("netconn_listen: invalid conn", (conn != NULL), return ERR_ARG;);
+
+ msg.function = do_listen;
+ msg.msg.conn = conn;
+#if TCP_LISTEN_BACKLOG
+ msg.msg.msg.lb.backlog = backlog;
+#endif /* TCP_LISTEN_BACKLOG */
+ err = TCPIP_APIMSG(&msg);
+
+ NETCONN_SET_SAFE_ERR(conn, err);
+ return err;
+#else /* LWIP_TCP */
+ LWIP_UNUSED_ARG(conn);
+ LWIP_UNUSED_ARG(backlog);
+ return ERR_ARG;
+#endif /* LWIP_TCP */
+}
+
+/**
+ * Accept a new connection on a TCP listening netconn.
+ *
+ * @param conn the TCP listen netconn
+ * @param new_conn pointer where the new connection is stored
+ * @return ERR_OK if a new connection has been received or an error
+ * code otherwise
+ */
+err_t
+netconn_accept(struct netconn *conn, struct netconn **new_conn)
+{
+#if LWIP_TCP
+ struct netconn *newconn;
+ err_t err;
+#if TCP_LISTEN_BACKLOG
+ struct api_msg msg;
+#endif /* TCP_LISTEN_BACKLOG */
+
+ LWIP_ERROR("netconn_accept: invalid pointer", (new_conn != NULL), return ERR_ARG;);
+ *new_conn = NULL;
+ LWIP_ERROR("netconn_accept: invalid conn", (conn != NULL), return ERR_ARG;);
+ LWIP_ERROR("netconn_accept: invalid acceptmbox", sys_mbox_valid(&conn->acceptmbox), return ERR_ARG;);
+
+ err = conn->last_err;
+ if (ERR_IS_FATAL(err)) {
+ /* don't recv on fatal errors: this might block the application task
+ waiting on acceptmbox forever! */
+ return err;
+ }
+
+#if LWIP_SO_RCVTIMEO
+ if (sys_arch_mbox_fetch(&conn->acceptmbox, (void **)&newconn, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {
+ NETCONN_SET_SAFE_ERR(conn, ERR_TIMEOUT);
+ return ERR_TIMEOUT;
+ }
+#else
+ sys_arch_mbox_fetch(&conn->acceptmbox, (void **)&newconn, 0);
+#endif /* LWIP_SO_RCVTIMEO*/
+ /* Register event with callback */
+ API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0);
+
+ if (newconn == NULL) {
+ /* connection has been closed */
+ NETCONN_SET_SAFE_ERR(conn, ERR_CLSD);
+ return ERR_CLSD;
+ }
+#if TCP_LISTEN_BACKLOG
+ /* Let the stack know that we have accepted the connection. */
+ msg.function = do_recv;
+ msg.msg.conn = conn;
+ /* don't care for the return value of do_recv */
+ TCPIP_APIMSG(&msg);
+#endif /* TCP_LISTEN_BACKLOG */
+
+ *new_conn = newconn;
+ /* don't set conn->last_err: it's only ERR_OK, anyway */
+ return ERR_OK;
+#else /* LWIP_TCP */
+ LWIP_UNUSED_ARG(conn);
+ LWIP_UNUSED_ARG(new_conn);
+ return ERR_ARG;
+#endif /* LWIP_TCP */
+}
+
+/**
+ * Receive data: actual implementation that doesn't care whether pbuf or netbuf
+ * is received
+ *
+ * @param conn the netconn from which to receive data
+ * @param new_buf pointer where a new pbuf/netbuf is stored when received data
+ * @return ERR_OK if data has been received, an error code otherwise (timeout,
+ * memory error or another error)
+ */
+static err_t
+netconn_recv_data(struct netconn *conn, void **new_buf)
+{
+ void *buf = NULL;
+ u16_t len;
+ err_t err;
+#if LWIP_TCP
+ struct api_msg msg;
+#endif /* LWIP_TCP */
+
+ LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;);
+ *new_buf = NULL;
+ LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL), return ERR_ARG;);
+ LWIP_ERROR("netconn_accept: invalid recvmbox", sys_mbox_valid(&conn->recvmbox), return ERR_CONN;);
+
+ err = conn->last_err;
+ if (ERR_IS_FATAL(err)) {
+ /* don't recv on fatal errors: this might block the application task
+ waiting on recvmbox forever! */
+ /* @todo: this does not allow us to fetch data that has been put into recvmbox
+ before the fatal error occurred - is that a problem? */
+ return err;
+ }
+
+#if LWIP_SO_RCVTIMEO
+ if (sys_arch_mbox_fetch(&conn->recvmbox, &buf, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {
+ NETCONN_SET_SAFE_ERR(conn, ERR_TIMEOUT);
+ return ERR_TIMEOUT;
+ }
+#else
+ sys_arch_mbox_fetch(&conn->recvmbox, &buf, 0);
+#endif /* LWIP_SO_RCVTIMEO*/
+
+#if LWIP_TCP
+ if (conn->type == NETCONN_TCP) {
+ if (!netconn_get_noautorecved(conn) || (buf == NULL)) {
+ /* Let the stack know that we have taken the data. */
+ /* TODO: Speedup: Don't block and wait for the answer here
+ (to prevent multiple thread-switches). */
+ msg.function = do_recv;
+ msg.msg.conn = conn;
+ if (buf != NULL) {
+ msg.msg.msg.r.len = ((struct pbuf *)buf)->tot_len;
+ } else {
+ msg.msg.msg.r.len = 1;
+ }
+ /* don't care for the return value of do_recv */
+ TCPIP_APIMSG(&msg);
+ }
+
+ /* If we are closed, we indicate that we no longer wish to use the socket */
+ if (buf == NULL) {
+ API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0);
+ /* Avoid to lose any previous error code */
+ NETCONN_SET_SAFE_ERR(conn, ERR_CLSD);
+ return ERR_CLSD;
+ }
+ len = ((struct pbuf *)buf)->tot_len;
+ }
+#endif /* LWIP_TCP */
+#if LWIP_TCP && (LWIP_UDP || LWIP_RAW)
+ else
+#endif /* LWIP_TCP && (LWIP_UDP || LWIP_RAW) */
+#if (LWIP_UDP || LWIP_RAW)
+ {
+ LWIP_ASSERT("buf != NULL", buf != NULL);
+ len = netbuf_len((struct netbuf *)buf);
+ }
+#endif /* (LWIP_UDP || LWIP_RAW) */
+
+#if LWIP_SO_RCVBUF
+ SYS_ARCH_DEC(conn->recv_avail, len);
+#endif /* LWIP_SO_RCVBUF */
+ /* Register event with callback */
+ API_EVENT(conn, NETCONN_EVT_RCVMINUS, len);
+
+ LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_recv_data: received %p, len=%"U16_F"\n", buf, len));
+
+ *new_buf = buf;
+ /* don't set conn->last_err: it's only ERR_OK, anyway */
+ return ERR_OK;
+}
+
+/**
+ * Receive data (in form of a pbuf) from a TCP netconn
+ *
+ * @param conn the netconn from which to receive data
+ * @param new_buf pointer where a new pbuf is stored when received data
+ * @return ERR_OK if data has been received, an error code otherwise (timeout,
+ * memory error or another error)
+ * ERR_ARG if conn is not a TCP netconn
+ */
+err_t
+netconn_recv_tcp_pbuf(struct netconn *conn, struct pbuf **new_buf)
+{
+ LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL) &&
+ netconn_type(conn) == NETCONN_TCP, return ERR_ARG;);
+
+ return netconn_recv_data(conn, (void **)new_buf);
+}
+
+/**
+ * Receive data (in form of a netbuf containing a packet buffer) from a netconn
+ *
+ * @param conn the netconn from which to receive data
+ * @param new_buf pointer where a new netbuf is stored when received data
+ * @return ERR_OK if data has been received, an error code otherwise (timeout,
+ * memory error or another error)
+ */
+err_t
+netconn_recv(struct netconn *conn, struct netbuf **new_buf)
+{
+#if LWIP_TCP
+ struct netbuf *buf = NULL;
+ err_t err;
+#endif /* LWIP_TCP */
+
+ LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;);
+ *new_buf = NULL;
+ LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL), return ERR_ARG;);
+ LWIP_ERROR("netconn_accept: invalid recvmbox", sys_mbox_valid(&conn->recvmbox), return ERR_CONN;);
+
+#if LWIP_TCP
+ if (conn->type == NETCONN_TCP) {
+ struct pbuf *p = NULL;
+ /* This is not a listening netconn, since recvmbox is set */
+
+ buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
+ if (buf == NULL) {
+ NETCONN_SET_SAFE_ERR(conn, ERR_MEM);
+ return ERR_MEM;
+ }
+
+ err = netconn_recv_data(conn, (void **)&p);
+ if (err != ERR_OK) {
+ memp_free(MEMP_NETBUF, buf);
+ return err;
+ }
+ LWIP_ASSERT("p != NULL", p != NULL);
+
+ buf->p = p;
+ buf->ptr = p;
+ buf->port = 0;
+ ip_addr_set_any(&buf->addr);
+ *new_buf = buf;
+ /* don't set conn->last_err: it's only ERR_OK, anyway */
+ return ERR_OK;
+ } else
+#endif /* LWIP_TCP */
+ {
+#if (LWIP_UDP || LWIP_RAW)
+ return netconn_recv_data(conn, (void **)new_buf);
+#endif /* (LWIP_UDP || LWIP_RAW) */
+ }
+}
+
+/**
+ * TCP: update the receive window: by calling this, the application
+ * tells the stack that it has processed data and is able to accept
+ * new data.
+ * ATTENTION: use with care, this is mainly used for sockets!
+ * Can only be used when calling netconn_set_noautorecved(conn, 1) before.
+ *
+ * @param conn the netconn for which to update the receive window
+ * @param length amount of data processed (ATTENTION: this must be accurate!)
+ */
+void
+netconn_recved(struct netconn *conn, u32_t length)
+{
+#if LWIP_TCP
+ if ((conn != NULL) && (conn->type == NETCONN_TCP) &&
+ (netconn_get_noautorecved(conn))) {
+ struct api_msg msg;
+ /* Let the stack know that we have taken the data. */
+ /* TODO: Speedup: Don't block and wait for the answer here
+ (to prevent multiple thread-switches). */
+ msg.function = do_recv;
+ msg.msg.conn = conn;
+ msg.msg.msg.r.len = length;
+ /* don't care for the return value of do_recv */
+ TCPIP_APIMSG(&msg);
+ }
+#else /* LWIP_TCP */
+ LWIP_UNUSED_ARG(conn);
+ LWIP_UNUSED_ARG(length);
+#endif /* LWIP_TCP */
+}
+
+/**
+ * Send data (in form of a netbuf) to a specific remote IP address and port.
+ * Only to be used for UDP and RAW netconns (not TCP).
+ *
+ * @param conn the netconn over which to send data
+ * @param buf a netbuf containing the data to send
+ * @param addr the remote IP address to which to send the data
+ * @param port the remote port to which to send the data
+ * @return ERR_OK if data was sent, any other err_t on error
+ */
+err_t
+netconn_sendto(struct netconn *conn, struct netbuf *buf, ip_addr_t *addr, u16_t port)
+{
+ if (buf != NULL) {
+ ip_addr_set(&buf->addr, addr);
+ buf->port = port;
+ return netconn_send(conn, buf);
+ }
+ return ERR_VAL;
+}
+
+/**
+ * Send data over a UDP or RAW netconn (that is already connected).
+ *
+ * @param conn the UDP or RAW netconn over which to send data
+ * @param buf a netbuf containing the data to send
+ * @return ERR_OK if data was sent, any other err_t on error
+ */
+err_t
+netconn_send(struct netconn *conn, struct netbuf *buf)
+{
+ struct api_msg msg;
+ err_t err;
+
+ LWIP_ERROR("netconn_send: invalid conn", (conn != NULL), return ERR_ARG;);
+
+ LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_send: sending %"U16_F" bytes\n", buf->p->tot_len));
+ msg.function = do_send;
+ msg.msg.conn = conn;
+ msg.msg.msg.b = buf;
+ err = TCPIP_APIMSG(&msg);
+
+ NETCONN_SET_SAFE_ERR(conn, err);
+ return err;
+}
+
+/**
+ * Send data over a TCP netconn.
+ *
+ * @param conn the TCP netconn over which to send data
+ * @param dataptr pointer to the application buffer that contains the data to send
+ * @param size size of the application data to send
+ * @param apiflags combination of following flags :
+ * - NETCONN_COPY: data will be copied into memory belonging to the stack
+ * - NETCONN_MORE: for TCP connection, PSH flag will be set on last segment sent
+ * - NETCONN_DONTBLOCK: only write the data if all dat can be written at once
+ * @return ERR_OK if data was sent, any other err_t on error
+ */
+err_t
+netconn_write(struct netconn *conn, const void *dataptr, size_t size, u8_t apiflags)
+{
+ struct api_msg msg;
+ err_t err;
+
+ LWIP_ERROR("netconn_write: invalid conn", (conn != NULL), return ERR_ARG;);
+ LWIP_ERROR("netconn_write: invalid conn->type", (conn->type == NETCONN_TCP), return ERR_VAL;);
+ if (size == 0) {
+ return ERR_OK;
+ }
+
+ /* @todo: for non-blocking write, check if 'size' would ever fit into
+ snd_queue or snd_buf */
+ msg.function = do_write;
+ msg.msg.conn = conn;
+ msg.msg.msg.w.dataptr = dataptr;
+ msg.msg.msg.w.apiflags = apiflags;
+ msg.msg.msg.w.len = size;
+ /* For locking the core: this _can_ be delayed on low memory/low send buffer,
+ but if it is, this is done inside api_msg.c:do_write(), so we can use the
+ non-blocking version here. */
+ err = TCPIP_APIMSG(&msg);
+
+ NETCONN_SET_SAFE_ERR(conn, err);
+ return err;
+}
+
+/**
+ * Close ot shutdown a TCP netconn (doesn't delete it).
+ *
+ * @param conn the TCP netconn to close or shutdown
+ * @param how fully close or only shutdown one side?
+ * @return ERR_OK if the netconn was closed, any other err_t on error
+ */
+static err_t
+netconn_close_shutdown(struct netconn *conn, u8_t how)
+{
+ struct api_msg msg;
+ err_t err;
+
+ LWIP_ERROR("netconn_close: invalid conn", (conn != NULL), return ERR_ARG;);
+
+ msg.function = do_close;
+ msg.msg.conn = conn;
+ /* shutting down both ends is the same as closing */
+ msg.msg.msg.sd.shut = how;
+ /* because of the LWIP_TCPIP_CORE_LOCKING implementation of do_close,
+ don't use TCPIP_APIMSG here */
+ err = tcpip_apimsg(&msg);
+
+ NETCONN_SET_SAFE_ERR(conn, err);
+ return err;
+}
+
+/**
+ * Close a TCP netconn (doesn't delete it).
+ *
+ * @param conn the TCP netconn to close
+ * @return ERR_OK if the netconn was closed, any other err_t on error
+ */
+err_t
+netconn_close(struct netconn *conn)
+{
+ /* shutting down both ends is the same as closing */
+ return netconn_close_shutdown(conn, NETCONN_SHUT_RDWR);
+}
+
+/**
+ * Shut down one or both sides of a TCP netconn (doesn't delete it).
+ *
+ * @param conn the TCP netconn to shut down
+ * @return ERR_OK if the netconn was closed, any other err_t on error
+ */
+err_t
+netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx)
+{
+ return netconn_close_shutdown(conn, (shut_rx ? NETCONN_SHUT_RD : 0) | (shut_tx ? NETCONN_SHUT_WR : 0));
+}
+
+#if LWIP_IGMP
+/**
+ * Join multicast groups for UDP netconns.
+ *
+ * @param conn the UDP netconn for which to change multicast addresses
+ * @param multiaddr IP address of the multicast group to join or leave
+ * @param netif_addr the IP address of the network interface on which to send
+ * the igmp message
+ * @param join_or_leave flag whether to send a join- or leave-message
+ * @return ERR_OK if the action was taken, any err_t on error
+ */
+err_t
+netconn_join_leave_group(struct netconn *conn,
+ ip_addr_t *multiaddr,
+ ip_addr_t *netif_addr,
+ enum netconn_igmp join_or_leave)
+{
+ struct api_msg msg;
+ err_t err;
+
+ LWIP_ERROR("netconn_join_leave_group: invalid conn", (conn != NULL), return ERR_ARG;);
+
+ msg.function = do_join_leave_group;
+ msg.msg.conn = conn;
+ msg.msg.msg.jl.multiaddr = multiaddr;
+ msg.msg.msg.jl.netif_addr = netif_addr;
+ msg.msg.msg.jl.join_or_leave = join_or_leave;
+ err = TCPIP_APIMSG(&msg);
+
+ NETCONN_SET_SAFE_ERR(conn, err);
+ return err;
+}
+#endif /* LWIP_IGMP */
+
+#if LWIP_DNS
+/**
+ * Execute a DNS query, only one IP address is returned
+ *
+ * @param name a string representation of the DNS host name to query
+ * @param addr a preallocated ip_addr_t where to store the resolved IP address
+ * @return ERR_OK: resolving succeeded
+ * ERR_MEM: memory error, try again later
+ * ERR_ARG: dns client not initialized or invalid hostname
+ * ERR_VAL: dns server response was invalid
+ */
+err_t
+netconn_gethostbyname(const char *name, ip_addr_t *addr)
+{
+ struct dns_api_msg msg;
+ err_t err;
+ sys_sem_t sem;
+
+ LWIP_ERROR("netconn_gethostbyname: invalid name", (name != NULL), return ERR_ARG;);
+ LWIP_ERROR("netconn_gethostbyname: invalid addr", (addr != NULL), return ERR_ARG;);
+
+ err = sys_sem_new(&sem, 0);
+ if (err != ERR_OK) {
+ return err;
+ }
+
+ msg.name = name;
+ msg.addr = addr;
+ msg.err = &err;
+ msg.sem = &sem;
+
+ tcpip_callback(do_gethostbyname, &msg);
+ sys_sem_wait(&sem);
+ sys_sem_free(&sem);
+
+ return err;
+}
+#endif /* LWIP_DNS*/
+
+#endif /* LWIP_NETCONN */
diff --git a/app/lwip/api/api_msg.c b/app/lwip/api/api_msg.c
new file mode 100644
index 00000000..dcb07e9c
--- /dev/null
+++ b/app/lwip/api/api_msg.c
@@ -0,0 +1,1540 @@
+/**
+ * @file
+ * Sequential API Internal module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/api_msg.h"
+
+#include "lwip/ip.h"
+#include "lwip/udp.h"
+#include "lwip/tcp.h"
+#include "lwip/raw.h"
+
+#include "lwip/memp.h"
+#include "lwip/tcpip.h"
+#include "lwip/igmp.h"
+#include "lwip/dns.h"
+
+#include
+
+#define SET_NONBLOCKING_CONNECT(conn, val) do { if(val) { \
+ (conn)->flags |= NETCONN_FLAG_IN_NONBLOCKING_CONNECT; \
+} else { \
+ (conn)->flags &= ~ NETCONN_FLAG_IN_NONBLOCKING_CONNECT; }} while(0)
+#define IN_NONBLOCKING_CONNECT(conn) (((conn)->flags & NETCONN_FLAG_IN_NONBLOCKING_CONNECT) != 0)
+
+/* forward declarations */
+#if LWIP_TCP
+static err_t do_writemore(struct netconn *conn);
+static void do_close_internal(struct netconn *conn);
+#endif
+
+#if LWIP_RAW
+/**
+ * Receive callback function for RAW netconns.
+ * Doesn't 'eat' the packet, only references it and sends it to
+ * conn->recvmbox
+ *
+ * @see raw.h (struct raw_pcb.recv) for parameters and return value
+ */
+static u8_t
+recv_raw(void *arg, struct raw_pcb *pcb, struct pbuf *p,
+ ip_addr_t *addr)
+{
+ struct pbuf *q;
+ struct netbuf *buf;
+ struct netconn *conn;
+
+ LWIP_UNUSED_ARG(addr);
+ conn = (struct netconn *)arg;
+
+ if ((conn != NULL) && sys_mbox_valid(&conn->recvmbox)) {
+#if LWIP_SO_RCVBUF
+ int recv_avail;
+ SYS_ARCH_GET(conn->recv_avail, recv_avail);
+ if ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize) {
+ return 0;
+ }
+#endif /* LWIP_SO_RCVBUF */
+ /* copy the whole packet into new pbufs */
+ q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
+ if(q != NULL) {
+ if (pbuf_copy(q, p) != ERR_OK) {
+ pbuf_free(q);
+ q = NULL;
+ }
+ }
+
+ if (q != NULL) {
+ u16_t len;
+ buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
+ if (buf == NULL) {
+ pbuf_free(q);
+ return 0;
+ }
+
+ buf->p = q;
+ buf->ptr = q;
+ ip_addr_copy(buf->addr, *ip_current_src_addr());
+ buf->port = pcb->protocol;
+
+ len = q->tot_len;
+ if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) {
+ netbuf_delete(buf);
+ return 0;
+ } else {
+#if LWIP_SO_RCVBUF
+ SYS_ARCH_INC(conn->recv_avail, len);
+#endif /* LWIP_SO_RCVBUF */
+ /* Register event with callback */
+ API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
+ }
+ }
+ }
+
+ return 0; /* do not eat the packet */
+}
+#endif /* LWIP_RAW*/
+
+#if LWIP_UDP
+/**
+ * Receive callback function for UDP netconns.
+ * Posts the packet to conn->recvmbox or deletes it on memory error.
+ *
+ * @see udp.h (struct udp_pcb.recv) for parameters
+ */
+static void
+recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p,
+ ip_addr_t *addr, u16_t port)
+{
+ struct netbuf *buf;
+ struct netconn *conn;
+ u16_t len;
+#if LWIP_SO_RCVBUF
+ int recv_avail;
+#endif /* LWIP_SO_RCVBUF */
+
+ LWIP_UNUSED_ARG(pcb); /* only used for asserts... */
+ LWIP_ASSERT("recv_udp must have a pcb argument", pcb != NULL);
+ LWIP_ASSERT("recv_udp must have an argument", arg != NULL);
+ conn = (struct netconn *)arg;
+ LWIP_ASSERT("recv_udp: recv for wrong pcb!", conn->pcb.udp == pcb);
+
+#if LWIP_SO_RCVBUF
+ SYS_ARCH_GET(conn->recv_avail, recv_avail);
+ if ((conn == NULL) || !sys_mbox_valid(&conn->recvmbox) ||
+ ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize)) {
+#else /* LWIP_SO_RCVBUF */
+ if ((conn == NULL) || !sys_mbox_valid(&conn->recvmbox)) {
+#endif /* LWIP_SO_RCVBUF */
+ pbuf_free(p);
+ return;
+ }
+
+ buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
+ if (buf == NULL) {
+ pbuf_free(p);
+ return;
+ } else {
+ buf->p = p;
+ buf->ptr = p;
+ ip_addr_set(&buf->addr, addr);
+ buf->port = port;
+#if LWIP_NETBUF_RECVINFO
+ {
+ const struct ip_hdr* iphdr = ip_current_header();
+ /* get the UDP header - always in the first pbuf, ensured by udp_input */
+ const struct udp_hdr* udphdr = (void*)(((char*)iphdr) + IPH_LEN(iphdr));
+#if LWIP_CHECKSUM_ON_COPY
+ buf->flags = NETBUF_FLAG_DESTADDR;
+#endif /* LWIP_CHECKSUM_ON_COPY */
+ ip_addr_set(&buf->toaddr, ip_current_dest_addr());
+ buf->toport_chksum = udphdr->dest;
+ }
+#endif /* LWIP_NETBUF_RECVINFO */
+ }
+
+ len = p->tot_len;
+ if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) {
+ netbuf_delete(buf);
+ return;
+ } else {
+#if LWIP_SO_RCVBUF
+ SYS_ARCH_INC(conn->recv_avail, len);
+#endif /* LWIP_SO_RCVBUF */
+ /* Register event with callback */
+ API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
+ }
+}
+#endif /* LWIP_UDP */
+
+#if LWIP_TCP
+/**
+ * Receive callback function for TCP netconns.
+ * Posts the packet to conn->recvmbox, but doesn't delete it on errors.
+ *
+ * @see tcp.h (struct tcp_pcb.recv) for parameters and return value
+ */
+static err_t
+recv_tcp(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
+{
+ struct netconn *conn;
+ u16_t len;
+
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_ASSERT("recv_tcp must have a pcb argument", pcb != NULL);
+ LWIP_ASSERT("recv_tcp must have an argument", arg != NULL);
+ conn = (struct netconn *)arg;
+ LWIP_ASSERT("recv_tcp: recv for wrong pcb!", conn->pcb.tcp == pcb);
+
+ if (conn == NULL) {
+ return ERR_VAL;
+ }
+ if (!sys_mbox_valid(&conn->recvmbox)) {
+ /* recvmbox already deleted */
+ if (p != NULL) {
+ tcp_recved(pcb, p->tot_len);
+ pbuf_free(p);
+ }
+ return ERR_OK;
+ }
+ /* Unlike for UDP or RAW pcbs, don't check for available space
+ using recv_avail since that could break the connection
+ (data is already ACKed) */
+
+ /* don't overwrite fatal errors! */
+ NETCONN_SET_SAFE_ERR(conn, err);
+
+ if (p != NULL) {
+ len = p->tot_len;
+ } else {
+ len = 0;
+ }
+
+ if (sys_mbox_trypost(&conn->recvmbox, p) != ERR_OK) {
+ /* don't deallocate p: it is presented to us later again from tcp_fasttmr! */
+ return ERR_MEM;
+ } else {
+#if LWIP_SO_RCVBUF
+ SYS_ARCH_INC(conn->recv_avail, len);
+#endif /* LWIP_SO_RCVBUF */
+ /* Register event with callback */
+ API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
+ }
+
+ return ERR_OK;
+}
+
+/**
+ * Poll callback function for TCP netconns.
+ * Wakes up an application thread that waits for a connection to close
+ * or data to be sent. The application thread then takes the
+ * appropriate action to go on.
+ *
+ * Signals the conn->sem.
+ * netconn_close waits for conn->sem if closing failed.
+ *
+ * @see tcp.h (struct tcp_pcb.poll) for parameters and return value
+ */
+static err_t
+poll_tcp(void *arg, struct tcp_pcb *pcb)
+{
+ struct netconn *conn = (struct netconn *)arg;
+
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_ASSERT("conn != NULL", (conn != NULL));
+
+ if (conn->state == NETCONN_WRITE) {
+ do_writemore(conn);
+ } else if (conn->state == NETCONN_CLOSE) {
+ do_close_internal(conn);
+ }
+ /* @todo: implement connect timeout here? */
+
+ /* Did a nonblocking write fail before? Then check available write-space. */
+ if (conn->flags & NETCONN_FLAG_CHECK_WRITESPACE) {
+ /* If the queued byte- or pbuf-count drops below the configured low-water limit,
+ let select mark this pcb as writable again. */
+ if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) &&
+ (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) {
+ conn->flags &= ~NETCONN_FLAG_CHECK_WRITESPACE;
+ API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
+ }
+ }
+
+ return ERR_OK;
+}
+
+/**
+ * Sent callback function for TCP netconns.
+ * Signals the conn->sem and calls API_EVENT.
+ * netconn_write waits for conn->sem if send buffer is low.
+ *
+ * @see tcp.h (struct tcp_pcb.sent) for parameters and return value
+ */
+static err_t
+sent_tcp(void *arg, struct tcp_pcb *pcb, u16_t len)
+{
+ struct netconn *conn = (struct netconn *)arg;
+
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_ASSERT("conn != NULL", (conn != NULL));
+
+ if (conn->state == NETCONN_WRITE) {
+ do_writemore(conn);
+ } else if (conn->state == NETCONN_CLOSE) {
+ do_close_internal(conn);
+ }
+
+ if (conn) {
+ /* If the queued byte- or pbuf-count drops below the configured low-water limit,
+ let select mark this pcb as writable again. */
+ if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) &&
+ (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) {
+ conn->flags &= ~NETCONN_FLAG_CHECK_WRITESPACE;
+ API_EVENT(conn, NETCONN_EVT_SENDPLUS, len);
+ }
+ }
+
+ return ERR_OK;
+}
+
+/**
+ * Error callback function for TCP netconns.
+ * Signals conn->sem, posts to all conn mboxes and calls API_EVENT.
+ * The application thread has then to decide what to do.
+ *
+ * @see tcp.h (struct tcp_pcb.err) for parameters
+ */
+static void
+err_tcp(void *arg, err_t err)
+{
+ struct netconn *conn;
+ enum netconn_state old_state;
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ conn = (struct netconn *)arg;
+ LWIP_ASSERT("conn != NULL", (conn != NULL));
+
+ conn->pcb.tcp = NULL;
+
+ /* no check since this is always fatal! */
+ SYS_ARCH_PROTECT(lev);
+ conn->last_err = err;
+ SYS_ARCH_UNPROTECT(lev);
+
+ /* reset conn->state now before waking up other threads */
+ old_state = conn->state;
+ conn->state = NETCONN_NONE;
+
+ /* Notify the user layer about a connection error. Used to signal
+ select. */
+ API_EVENT(conn, NETCONN_EVT_ERROR, 0);
+ /* Try to release selects pending on 'read' or 'write', too.
+ They will get an error if they actually try to read or write. */
+ API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
+ API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
+
+ /* pass NULL-message to recvmbox to wake up pending recv */
+ if (sys_mbox_valid(&conn->recvmbox)) {
+ /* use trypost to prevent deadlock */
+ sys_mbox_trypost(&conn->recvmbox, NULL);
+ }
+ /* pass NULL-message to acceptmbox to wake up pending accept */
+ if (sys_mbox_valid(&conn->acceptmbox)) {
+ /* use trypost to preven deadlock */
+ sys_mbox_trypost(&conn->acceptmbox, NULL);
+ }
+
+ if ((old_state == NETCONN_WRITE) || (old_state == NETCONN_CLOSE) ||
+ (old_state == NETCONN_CONNECT)) {
+ /* calling do_writemore/do_close_internal is not necessary
+ since the pcb has already been deleted! */
+ int was_nonblocking_connect = IN_NONBLOCKING_CONNECT(conn);
+ SET_NONBLOCKING_CONNECT(conn, 0);
+
+ if (!was_nonblocking_connect) {
+ /* set error return code */
+ LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
+ conn->current_msg->err = err;
+ conn->current_msg = NULL;
+ /* wake up the waiting task */
+ sys_sem_signal(&conn->op_completed);
+ }
+ } else {
+ LWIP_ASSERT("conn->current_msg == NULL", conn->current_msg == NULL);
+ }
+}
+
+/**
+ * Setup a tcp_pcb with the correct callback function pointers
+ * and their arguments.
+ *
+ * @param conn the TCP netconn to setup
+ */
+static void
+setup_tcp(struct netconn *conn)
+{
+ struct tcp_pcb *pcb;
+
+ pcb = conn->pcb.tcp;
+ tcp_arg(pcb, conn);
+ tcp_recv(pcb, recv_tcp);
+ tcp_sent(pcb, sent_tcp);
+ tcp_poll(pcb, poll_tcp, 4);
+ tcp_err(pcb, err_tcp);
+}
+
+/**
+ * Accept callback function for TCP netconns.
+ * Allocates a new netconn and posts that to conn->acceptmbox.
+ *
+ * @see tcp.h (struct tcp_pcb_listen.accept) for parameters and return value
+ */
+static err_t
+accept_function(void *arg, struct tcp_pcb *newpcb, err_t err)
+{
+ struct netconn *newconn;
+ struct netconn *conn = (struct netconn *)arg;
+
+ LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: newpcb->tate: %s\n", tcp_debug_state_str(newpcb->state)));
+
+ if (!sys_mbox_valid(&conn->acceptmbox)) {
+ LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: acceptmbox already deleted\n"));
+ return ERR_VAL;
+ }
+
+ /* We have to set the callback here even though
+ * the new socket is unknown. conn->socket is marked as -1. */
+ newconn = netconn_alloc(conn->type, conn->callback);
+ if (newconn == NULL) {
+ return ERR_MEM;
+ }
+ newconn->pcb.tcp = newpcb;
+ setup_tcp(newconn);
+ /* no protection: when creating the pcb, the netconn is not yet known
+ to the application thread */
+ newconn->last_err = err;
+
+ if (sys_mbox_trypost(&conn->acceptmbox, newconn) != ERR_OK) {
+ /* When returning != ERR_OK, the pcb is aborted in tcp_process(),
+ so do nothing here! */
+ newconn->pcb.tcp = NULL;
+ /* no need to drain since we know the recvmbox is empty. */
+ sys_mbox_free(&newconn->recvmbox);
+ sys_mbox_set_invalid(&newconn->recvmbox);
+ netconn_free(newconn);
+ return ERR_MEM;
+ } else {
+ /* Register event with callback */
+ API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
+ }
+
+ return ERR_OK;
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Create a new pcb of a specific type.
+ * Called from do_newconn().
+ *
+ * @param msg the api_msg_msg describing the connection type
+ * @return msg->conn->err, but the return value is currently ignored
+ */
+static void
+pcb_new(struct api_msg_msg *msg)
+{
+ LWIP_ASSERT("pcb_new: pcb already allocated", msg->conn->pcb.tcp == NULL);
+
+ /* Allocate a PCB for this connection */
+ switch(NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+ case NETCONN_RAW:
+ msg->conn->pcb.raw = raw_new(msg->msg.n.proto);
+ if(msg->conn->pcb.raw == NULL) {
+ msg->err = ERR_MEM;
+ break;
+ }
+ raw_recv(msg->conn->pcb.raw, recv_raw, msg->conn);
+ break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+ case NETCONN_UDP:
+ msg->conn->pcb.udp = udp_new();
+ if(msg->conn->pcb.udp == NULL) {
+ msg->err = ERR_MEM;
+ break;
+ }
+#if LWIP_UDPLITE
+ if (msg->conn->type==NETCONN_UDPLITE) {
+ udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_UDPLITE);
+ }
+#endif /* LWIP_UDPLITE */
+ if (msg->conn->type==NETCONN_UDPNOCHKSUM) {
+ udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_NOCHKSUM);
+ }
+ udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn);
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case NETCONN_TCP:
+ msg->conn->pcb.tcp = tcp_new();
+ if(msg->conn->pcb.tcp == NULL) {
+ msg->err = ERR_MEM;
+ break;
+ }
+ setup_tcp(msg->conn);
+ break;
+#endif /* LWIP_TCP */
+ default:
+ /* Unsupported netconn type, e.g. protocol disabled */
+ msg->err = ERR_VAL;
+ break;
+ }
+}
+
+/**
+ * Create a new pcb of a specific type inside a netconn.
+ * Called from netconn_new_with_proto_and_callback.
+ *
+ * @param msg the api_msg_msg describing the connection type
+ */
+void
+do_newconn(struct api_msg_msg *msg)
+{
+ msg->err = ERR_OK;
+ if(msg->conn->pcb.tcp == NULL) {
+ pcb_new(msg);
+ }
+ /* Else? This "new" connection already has a PCB allocated. */
+ /* Is this an error condition? Should it be deleted? */
+ /* We currently just are happy and return. */
+
+ TCPIP_APIMSG_ACK(msg);
+}
+
+/**
+ * Create a new netconn (of a specific type) that has a callback function.
+ * The corresponding pcb is NOT created!
+ *
+ * @param t the type of 'connection' to create (@see enum netconn_type)
+ * @param proto the IP protocol for RAW IP pcbs
+ * @param callback a function to call on status changes (RX available, TX'ed)
+ * @return a newly allocated struct netconn or
+ * NULL on memory error
+ */
+struct netconn*
+netconn_alloc(enum netconn_type t, netconn_callback callback)
+{
+ struct netconn *conn;
+ int size;
+
+ conn = (struct netconn *)memp_malloc(MEMP_NETCONN);
+ if (conn == NULL) {
+ return NULL;
+ }
+
+ conn->last_err = ERR_OK;
+ conn->type = t;
+ conn->pcb.tcp = NULL;
+
+#if (DEFAULT_RAW_RECVMBOX_SIZE == DEFAULT_UDP_RECVMBOX_SIZE) && \
+ (DEFAULT_RAW_RECVMBOX_SIZE == DEFAULT_TCP_RECVMBOX_SIZE)
+ size = DEFAULT_RAW_RECVMBOX_SIZE;
+#else
+ switch(NETCONNTYPE_GROUP(t)) {
+#if LWIP_RAW
+ case NETCONN_RAW:
+ size = DEFAULT_RAW_RECVMBOX_SIZE;
+ break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+ case NETCONN_UDP:
+ size = DEFAULT_UDP_RECVMBOX_SIZE;
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case NETCONN_TCP:
+ size = DEFAULT_TCP_RECVMBOX_SIZE;
+ break;
+#endif /* LWIP_TCP */
+ default:
+ LWIP_ASSERT("netconn_alloc: undefined netconn_type", 0);
+ break;
+ }
+#endif
+
+ if (sys_sem_new(&conn->op_completed, 0) != ERR_OK) {
+ memp_free(MEMP_NETCONN, conn);
+ return NULL;
+ }
+ if (sys_mbox_new(&conn->recvmbox, size) != ERR_OK) {
+ sys_sem_free(&conn->op_completed);
+ memp_free(MEMP_NETCONN, conn);
+ return NULL;
+ }
+
+#if LWIP_TCP
+ sys_mbox_set_invalid(&conn->acceptmbox);
+#endif
+ conn->state = NETCONN_NONE;
+#if LWIP_SOCKET
+ /* initialize socket to -1 since 0 is a valid socket */
+ conn->socket = -1;
+#endif /* LWIP_SOCKET */
+ conn->callback = callback;
+#if LWIP_TCP
+ conn->current_msg = NULL;
+ conn->write_offset = 0;
+#endif /* LWIP_TCP */
+#if LWIP_SO_RCVTIMEO
+ conn->recv_timeout = 0;
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVBUF
+ conn->recv_bufsize = RECV_BUFSIZE_DEFAULT;
+ conn->recv_avail = 0;
+#endif /* LWIP_SO_RCVBUF */
+ conn->flags = 0;
+ return conn;
+}
+
+/**
+ * Delete a netconn and all its resources.
+ * The pcb is NOT freed (since we might not be in the right thread context do this).
+ *
+ * @param conn the netconn to free
+ */
+void
+netconn_free(struct netconn *conn)
+{
+ LWIP_ASSERT("PCB must be deallocated outside this function", conn->pcb.tcp == NULL);
+ LWIP_ASSERT("recvmbox must be deallocated before calling this function",
+ !sys_mbox_valid(&conn->recvmbox));
+#if LWIP_TCP
+ LWIP_ASSERT("acceptmbox must be deallocated before calling this function",
+ !sys_mbox_valid(&conn->acceptmbox));
+#endif /* LWIP_TCP */
+
+ sys_sem_free(&conn->op_completed);
+ sys_sem_set_invalid(&conn->op_completed);
+
+ memp_free(MEMP_NETCONN, conn);
+}
+
+/**
+ * Delete rcvmbox and acceptmbox of a netconn and free the left-over data in
+ * these mboxes
+ *
+ * @param conn the netconn to free
+ * @bytes_drained bytes drained from recvmbox
+ * @accepts_drained pending connections drained from acceptmbox
+ */
+static void
+netconn_drain(struct netconn *conn)
+{
+ void *mem;
+#if LWIP_TCP
+ struct pbuf *p;
+#endif /* LWIP_TCP */
+
+ /* This runs in tcpip_thread, so we don't need to lock against rx packets */
+
+ /* Delete and drain the recvmbox. */
+ if (sys_mbox_valid(&conn->recvmbox)) {
+ while (sys_mbox_tryfetch(&conn->recvmbox, &mem) != SYS_MBOX_EMPTY) {
+#if LWIP_TCP
+ if (conn->type == NETCONN_TCP) {
+ if(mem != NULL) {
+ p = (struct pbuf*)mem;
+ /* pcb might be set to NULL already by err_tcp() */
+ if (conn->pcb.tcp != NULL) {
+ tcp_recved(conn->pcb.tcp, p->tot_len);
+ }
+ pbuf_free(p);
+ }
+ } else
+#endif /* LWIP_TCP */
+ {
+ netbuf_delete((struct netbuf *)mem);
+ }
+ }
+ sys_mbox_free(&conn->recvmbox);
+ sys_mbox_set_invalid(&conn->recvmbox);
+ }
+
+ /* Delete and drain the acceptmbox. */
+#if LWIP_TCP
+ if (sys_mbox_valid(&conn->acceptmbox)) {
+ while (sys_mbox_tryfetch(&conn->acceptmbox, &mem) != SYS_MBOX_EMPTY) {
+ struct netconn *newconn = (struct netconn *)mem;
+ /* Only tcp pcbs have an acceptmbox, so no need to check conn->type */
+ /* pcb might be set to NULL already by err_tcp() */
+ if (conn->pcb.tcp != NULL) {
+ tcp_accepted(conn->pcb.tcp);
+ }
+ /* drain recvmbox */
+ netconn_drain(newconn);
+ if (newconn->pcb.tcp != NULL) {
+ tcp_abort(newconn->pcb.tcp);
+ newconn->pcb.tcp = NULL;
+ }
+ netconn_free(newconn);
+ }
+ sys_mbox_free(&conn->acceptmbox);
+ sys_mbox_set_invalid(&conn->acceptmbox);
+ }
+#endif /* LWIP_TCP */
+}
+
+#if LWIP_TCP
+/**
+ * Internal helper function to close a TCP netconn: since this sometimes
+ * doesn't work at the first attempt, this function is called from multiple
+ * places.
+ *
+ * @param conn the TCP netconn to close
+ */
+static void
+do_close_internal(struct netconn *conn)
+{
+ err_t err;
+ u8_t shut, shut_rx, shut_tx, close;
+
+ LWIP_ASSERT("invalid conn", (conn != NULL));
+ LWIP_ASSERT("this is for tcp netconns only", (conn->type == NETCONN_TCP));
+ LWIP_ASSERT("conn must be in state NETCONN_CLOSE", (conn->state == NETCONN_CLOSE));
+ LWIP_ASSERT("pcb already closed", (conn->pcb.tcp != NULL));
+ LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
+
+ shut = conn->current_msg->msg.sd.shut;
+ shut_rx = shut & NETCONN_SHUT_RD;
+ shut_tx = shut & NETCONN_SHUT_WR;
+ /* shutting down both ends is the same as closing */
+ close = shut == NETCONN_SHUT_RDWR;
+
+ /* Set back some callback pointers */
+ if (close) {
+ tcp_arg(conn->pcb.tcp, NULL);
+ }
+ if (conn->pcb.tcp->state == LISTEN) {
+ tcp_accept(conn->pcb.tcp, NULL);
+ } else {
+ /* some callbacks have to be reset if tcp_close is not successful */
+ if (shut_rx) {
+ tcp_recv(conn->pcb.tcp, NULL);
+ tcp_accept(conn->pcb.tcp, NULL);
+ }
+ if (shut_tx) {
+ tcp_sent(conn->pcb.tcp, NULL);
+ }
+ if (close) {
+ tcp_poll(conn->pcb.tcp, NULL, 4);
+ tcp_err(conn->pcb.tcp, NULL);
+ }
+ }
+ /* Try to close the connection */
+ if (shut == NETCONN_SHUT_RDWR) {
+ err = tcp_close(conn->pcb.tcp);
+ } else {
+ err = tcp_shutdown(conn->pcb.tcp, shut & NETCONN_SHUT_RD, shut & NETCONN_SHUT_WR);
+ }
+ if (err == ERR_OK) {
+ /* Closing succeeded */
+ conn->current_msg->err = ERR_OK;
+ conn->current_msg = NULL;
+ conn->state = NETCONN_NONE;
+ /* Set back some callback pointers as conn is going away */
+ conn->pcb.tcp = NULL;
+ /* Trigger select() in socket layer. Make sure everybody notices activity
+ on the connection, error first! */
+ if (close) {
+ API_EVENT(conn, NETCONN_EVT_ERROR, 0);
+ }
+ if (shut_rx) {
+ API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
+ }
+ if (shut_tx) {
+ API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
+ }
+ /* wake up the application task */
+ sys_sem_signal(&conn->op_completed);
+ } else {
+ /* Closing failed, restore some of the callbacks */
+ /* Closing of listen pcb will never fail! */
+ LWIP_ASSERT("Closing a listen pcb may not fail!", (conn->pcb.tcp->state != LISTEN));
+ tcp_sent(conn->pcb.tcp, sent_tcp);
+ tcp_poll(conn->pcb.tcp, poll_tcp, 4);
+ tcp_err(conn->pcb.tcp, err_tcp);
+ tcp_arg(conn->pcb.tcp, conn);
+ /* don't restore recv callback: we don't want to receive any more data */
+ }
+ /* If closing didn't succeed, we get called again either
+ from poll_tcp or from sent_tcp */
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Delete the pcb inside a netconn.
+ * Called from netconn_delete.
+ *
+ * @param msg the api_msg_msg pointing to the connection
+ */
+void
+do_delconn(struct api_msg_msg *msg)
+{
+ /* @todo TCP: abort running write/connect? */
+ if ((msg->conn->state != NETCONN_NONE) &&
+ (msg->conn->state != NETCONN_LISTEN) &&
+ (msg->conn->state != NETCONN_CONNECT)) {
+ /* this only happens for TCP netconns */
+ LWIP_ASSERT("msg->conn->type == NETCONN_TCP", msg->conn->type == NETCONN_TCP);
+ msg->err = ERR_INPROGRESS;
+ } else {
+ LWIP_ASSERT("blocking connect in progress",
+ (msg->conn->state != NETCONN_CONNECT) || IN_NONBLOCKING_CONNECT(msg->conn));
+ /* Drain and delete mboxes */
+ netconn_drain(msg->conn);
+
+ if (msg->conn->pcb.tcp != NULL) {
+
+ switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+ case NETCONN_RAW:
+ raw_remove(msg->conn->pcb.raw);
+ break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+ case NETCONN_UDP:
+ msg->conn->pcb.udp->recv_arg = NULL;
+ udp_remove(msg->conn->pcb.udp);
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case NETCONN_TCP:
+ LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL &&
+ msg->conn->write_offset == 0);
+ msg->conn->state = NETCONN_CLOSE;
+ msg->msg.sd.shut = NETCONN_SHUT_RDWR;
+ msg->conn->current_msg = msg;
+ do_close_internal(msg->conn);
+ /* API_EVENT is called inside do_close_internal, before releasing
+ the application thread, so we can return at this point! */
+ return;
+#endif /* LWIP_TCP */
+ default:
+ break;
+ }
+ msg->conn->pcb.tcp = NULL;
+ }
+ /* tcp netconns don't come here! */
+
+ /* @todo: this lets select make the socket readable and writable,
+ which is wrong! errfd instead? */
+ API_EVENT(msg->conn, NETCONN_EVT_RCVPLUS, 0);
+ API_EVENT(msg->conn, NETCONN_EVT_SENDPLUS, 0);
+ }
+ if (sys_sem_valid(&msg->conn->op_completed)) {
+ sys_sem_signal(&msg->conn->op_completed);
+ }
+}
+
+/**
+ * Bind a pcb contained in a netconn
+ * Called from netconn_bind.
+ *
+ * @param msg the api_msg_msg pointing to the connection and containing
+ * the IP address and port to bind to
+ */
+void
+do_bind(struct api_msg_msg *msg)
+{
+ if (ERR_IS_FATAL(msg->conn->last_err)) {
+ msg->err = msg->conn->last_err;
+ } else {
+ msg->err = ERR_VAL;
+ if (msg->conn->pcb.tcp != NULL) {
+ switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+ case NETCONN_RAW:
+ msg->err = raw_bind(msg->conn->pcb.raw, msg->msg.bc.ipaddr);
+ break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+ case NETCONN_UDP:
+ msg->err = udp_bind(msg->conn->pcb.udp, msg->msg.bc.ipaddr, msg->msg.bc.port);
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case NETCONN_TCP:
+ msg->err = tcp_bind(msg->conn->pcb.tcp, msg->msg.bc.ipaddr, msg->msg.bc.port);
+ break;
+#endif /* LWIP_TCP */
+ default:
+ break;
+ }
+ }
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+
+#if LWIP_TCP
+/**
+ * TCP callback function if a connection (opened by tcp_connect/do_connect) has
+ * been established (or reset by the remote host).
+ *
+ * @see tcp.h (struct tcp_pcb.connected) for parameters and return values
+ */
+static err_t
+do_connected(void *arg, struct tcp_pcb *pcb, err_t err)
+{
+ struct netconn *conn;
+ int was_blocking;
+
+ LWIP_UNUSED_ARG(pcb);
+
+ conn = (struct netconn *)arg;
+
+ if (conn == NULL) {
+ return ERR_VAL;
+ }
+
+ LWIP_ASSERT("conn->state == NETCONN_CONNECT", conn->state == NETCONN_CONNECT);
+ LWIP_ASSERT("(conn->current_msg != NULL) || conn->in_non_blocking_connect",
+ (conn->current_msg != NULL) || IN_NONBLOCKING_CONNECT(conn));
+
+ if (conn->current_msg != NULL) {
+ conn->current_msg->err = err;
+ }
+ if ((conn->type == NETCONN_TCP) && (err == ERR_OK)) {
+ setup_tcp(conn);
+ }
+ was_blocking = !IN_NONBLOCKING_CONNECT(conn);
+ SET_NONBLOCKING_CONNECT(conn, 0);
+ conn->current_msg = NULL;
+ conn->state = NETCONN_NONE;
+ if (!was_blocking) {
+ SYS_ARCH_DECL_PROTECT(lev);
+ SYS_ARCH_PROTECT(lev);
+ if (conn->last_err == ERR_INPROGRESS) {
+ conn->last_err = ERR_OK;
+ }
+ SYS_ARCH_UNPROTECT(lev);
+ }
+ API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
+
+ if (was_blocking) {
+ sys_sem_signal(&conn->op_completed);
+ }
+ return ERR_OK;
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Connect a pcb contained inside a netconn
+ * Called from netconn_connect.
+ *
+ * @param msg the api_msg_msg pointing to the connection and containing
+ * the IP address and port to connect to
+ */
+void
+do_connect(struct api_msg_msg *msg)
+{
+ if (msg->conn->pcb.tcp == NULL) {
+ /* This may happen when calling netconn_connect() a second time */
+ msg->err = ERR_CLSD;
+ } else {
+ switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+ case NETCONN_RAW:
+ msg->err = raw_connect(msg->conn->pcb.raw, msg->msg.bc.ipaddr);
+ break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+ case NETCONN_UDP:
+ msg->err = udp_connect(msg->conn->pcb.udp, msg->msg.bc.ipaddr, msg->msg.bc.port);
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case NETCONN_TCP:
+ /* Prevent connect while doing any other action. */
+ if (msg->conn->state != NETCONN_NONE) {
+ msg->err = ERR_ISCONN;
+ } else {
+ setup_tcp(msg->conn);
+ msg->err = tcp_connect(msg->conn->pcb.tcp, msg->msg.bc.ipaddr,
+ msg->msg.bc.port, do_connected);
+ if (msg->err == ERR_OK) {
+ u8_t non_blocking = netconn_is_nonblocking(msg->conn);
+ msg->conn->state = NETCONN_CONNECT;
+ SET_NONBLOCKING_CONNECT(msg->conn, non_blocking);
+ if (non_blocking) {
+ msg->err = ERR_INPROGRESS;
+ } else {
+ msg->conn->current_msg = msg;
+ /* sys_sem_signal() is called from do_connected (or err_tcp()),
+ * when the connection is established! */
+ return;
+ }
+ }
+ }
+ break;
+#endif /* LWIP_TCP */
+ default:
+ LWIP_ERROR("Invalid netconn type", 0, do{ msg->err = ERR_VAL; }while(0));
+ break;
+ }
+ }
+ sys_sem_signal(&msg->conn->op_completed);
+}
+
+/**
+ * Connect a pcb contained inside a netconn
+ * Only used for UDP netconns.
+ * Called from netconn_disconnect.
+ *
+ * @param msg the api_msg_msg pointing to the connection to disconnect
+ */
+void
+do_disconnect(struct api_msg_msg *msg)
+{
+#if LWIP_UDP
+ if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
+ udp_disconnect(msg->conn->pcb.udp);
+ msg->err = ERR_OK;
+ } else
+#endif /* LWIP_UDP */
+ {
+ msg->err = ERR_VAL;
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+
+#if LWIP_TCP
+/**
+ * Set a TCP pcb contained in a netconn into listen mode
+ * Called from netconn_listen.
+ *
+ * @param msg the api_msg_msg pointing to the connection
+ */
+void
+do_listen(struct api_msg_msg *msg)
+{
+ if (ERR_IS_FATAL(msg->conn->last_err)) {
+ msg->err = msg->conn->last_err;
+ } else {
+ msg->err = ERR_CONN;
+ if (msg->conn->pcb.tcp != NULL) {
+ if (msg->conn->type == NETCONN_TCP) {
+ if (msg->conn->state == NETCONN_NONE) {
+#if TCP_LISTEN_BACKLOG
+ struct tcp_pcb* lpcb = tcp_listen_with_backlog(msg->conn->pcb.tcp, msg->msg.lb.backlog);
+#else /* TCP_LISTEN_BACKLOG */
+ struct tcp_pcb* lpcb = tcp_listen(msg->conn->pcb.tcp);
+#endif /* TCP_LISTEN_BACKLOG */
+ if (lpcb == NULL) {
+ /* in this case, the old pcb is still allocated */
+ msg->err = ERR_MEM;
+ } else {
+ /* delete the recvmbox and allocate the acceptmbox */
+ if (sys_mbox_valid(&msg->conn->recvmbox)) {
+ /** @todo: should we drain the recvmbox here? */
+ sys_mbox_free(&msg->conn->recvmbox);
+ sys_mbox_set_invalid(&msg->conn->recvmbox);
+ }
+ msg->err = ERR_OK;
+ if (!sys_mbox_valid(&msg->conn->acceptmbox)) {
+ msg->err = sys_mbox_new(&msg->conn->acceptmbox, DEFAULT_ACCEPTMBOX_SIZE);
+ }
+ if (msg->err == ERR_OK) {
+ msg->conn->state = NETCONN_LISTEN;
+ msg->conn->pcb.tcp = lpcb;
+ tcp_arg(msg->conn->pcb.tcp, msg->conn);
+ tcp_accept(msg->conn->pcb.tcp, accept_function);
+ } else {
+ /* since the old pcb is already deallocated, free lpcb now */
+ tcp_close(lpcb);
+ msg->conn->pcb.tcp = NULL;
+ }
+ }
+ }
+ }
+ }
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Send some data on a RAW or UDP pcb contained in a netconn
+ * Called from netconn_send
+ *
+ * @param msg the api_msg_msg pointing to the connection
+ */
+void
+do_send(struct api_msg_msg *msg)
+{
+ if (ERR_IS_FATAL(msg->conn->last_err)) {
+ msg->err = msg->conn->last_err;
+ } else {
+ msg->err = ERR_CONN;
+ if (msg->conn->pcb.tcp != NULL) {
+ switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+ case NETCONN_RAW:
+ if (ip_addr_isany(&msg->msg.b->addr)) {
+ msg->err = raw_send(msg->conn->pcb.raw, msg->msg.b->p);
+ } else {
+ msg->err = raw_sendto(msg->conn->pcb.raw, msg->msg.b->p, &msg->msg.b->addr);
+ }
+ break;
+#endif
+#if LWIP_UDP
+ case NETCONN_UDP:
+#if LWIP_CHECKSUM_ON_COPY
+ if (ip_addr_isany(&msg->msg.b->addr)) {
+ msg->err = udp_send_chksum(msg->conn->pcb.udp, msg->msg.b->p,
+ msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum);
+ } else {
+ msg->err = udp_sendto_chksum(msg->conn->pcb.udp, msg->msg.b->p,
+ &msg->msg.b->addr, msg->msg.b->port,
+ msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum);
+ }
+#else /* LWIP_CHECKSUM_ON_COPY */
+ if (ip_addr_isany(&msg->msg.b->addr)) {
+ msg->err = udp_send(msg->conn->pcb.udp, msg->msg.b->p);
+ } else {
+ msg->err = udp_sendto(msg->conn->pcb.udp, msg->msg.b->p, &msg->msg.b->addr, msg->msg.b->port);
+ }
+#endif /* LWIP_CHECKSUM_ON_COPY */
+ break;
+#endif /* LWIP_UDP */
+ default:
+ break;
+ }
+ }
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+
+#if LWIP_TCP
+/**
+ * Indicate data has been received from a TCP pcb contained in a netconn
+ * Called from netconn_recv
+ *
+ * @param msg the api_msg_msg pointing to the connection
+ */
+void
+do_recv(struct api_msg_msg *msg)
+{
+ msg->err = ERR_OK;
+ if (msg->conn->pcb.tcp != NULL) {
+ if (msg->conn->type == NETCONN_TCP) {
+#if TCP_LISTEN_BACKLOG
+ if (msg->conn->pcb.tcp->state == LISTEN) {
+ tcp_accepted(msg->conn->pcb.tcp);
+ } else
+#endif /* TCP_LISTEN_BACKLOG */
+ {
+ u32_t remaining = msg->msg.r.len;
+ do {
+ u16_t recved = (remaining > 0xffff) ? 0xffff : (u16_t)remaining;
+ tcp_recved(msg->conn->pcb.tcp, recved);
+ remaining -= recved;
+ }while(remaining != 0);
+ }
+ }
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+
+/**
+ * See if more data needs to be written from a previous call to netconn_write.
+ * Called initially from do_write. If the first call can't send all data
+ * (because of low memory or empty send-buffer), this function is called again
+ * from sent_tcp() or poll_tcp() to send more data. If all data is sent, the
+ * blocking application thread (waiting in netconn_write) is released.
+ *
+ * @param conn netconn (that is currently in state NETCONN_WRITE) to process
+ * @return ERR_OK
+ * ERR_MEM if LWIP_TCPIP_CORE_LOCKING=1 and sending hasn't yet finished
+ */
+static err_t
+do_writemore(struct netconn *conn)
+{
+ err_t err = ERR_OK;
+ void *dataptr;
+ u16_t len, available;
+ u8_t write_finished = 0;
+ size_t diff;
+ u8_t dontblock = netconn_is_nonblocking(conn) ||
+ (conn->current_msg->msg.w.apiflags & NETCONN_DONTBLOCK);
+ u8_t apiflags = conn->current_msg->msg.w.apiflags;
+
+ LWIP_ASSERT("conn != NULL", conn != NULL);
+ LWIP_ASSERT("conn->state == NETCONN_WRITE", (conn->state == NETCONN_WRITE));
+ LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
+ LWIP_ASSERT("conn->pcb.tcp != NULL", conn->pcb.tcp != NULL);
+ LWIP_ASSERT("conn->write_offset < conn->current_msg->msg.w.len",
+ conn->write_offset < conn->current_msg->msg.w.len);
+
+ dataptr = (u8_t*)conn->current_msg->msg.w.dataptr + conn->write_offset;
+ diff = conn->current_msg->msg.w.len - conn->write_offset;
+ if (diff > 0xffffUL) { /* max_u16_t */
+ len = 0xffff;
+#if LWIP_TCPIP_CORE_LOCKING
+ conn->flags |= NETCONN_FLAG_WRITE_DELAYED;
+#endif
+ apiflags |= TCP_WRITE_FLAG_MORE;
+ } else {
+ len = (u16_t)diff;
+ }
+ available = tcp_sndbuf(conn->pcb.tcp);
+ if (available < len) {
+ /* don't try to write more than sendbuf */
+ len = available;
+#if LWIP_TCPIP_CORE_LOCKING
+ conn->flags |= NETCONN_FLAG_WRITE_DELAYED;
+#endif
+ apiflags |= TCP_WRITE_FLAG_MORE;
+ }
+ if (dontblock && (len < conn->current_msg->msg.w.len)) {
+ /* failed to send all data at once -> nonblocking write not possible */
+ err = ERR_MEM;
+ }
+ if (err == ERR_OK) {
+ LWIP_ASSERT("do_writemore: invalid length!", ((conn->write_offset + len) <= conn->current_msg->msg.w.len));
+ err = tcp_write(conn->pcb.tcp, dataptr, len, apiflags);
+ }
+ if (dontblock && (err == ERR_MEM)) {
+ /* nonblocking write failed */
+ write_finished = 1;
+ err = ERR_WOULDBLOCK;
+ /* let poll_tcp check writable space to mark the pcb
+ writable again */
+ conn->flags |= NETCONN_FLAG_CHECK_WRITESPACE;
+ /* let select mark this pcb as non-writable. */
+ API_EVENT(conn, NETCONN_EVT_SENDMINUS, len);
+ } else {
+ /* if OK or memory error, check available space */
+ if (((err == ERR_OK) || (err == ERR_MEM)) &&
+ ((tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT) ||
+ (tcp_sndqueuelen(conn->pcb.tcp) >= TCP_SNDQUEUELOWAT))) {
+ /* The queued byte- or pbuf-count exceeds the configured low-water limit,
+ let select mark this pcb as non-writable. */
+ API_EVENT(conn, NETCONN_EVT_SENDMINUS, len);
+ }
+
+ if (err == ERR_OK) {
+ conn->write_offset += len;
+ if (conn->write_offset == conn->current_msg->msg.w.len) {
+ /* everything was written */
+ write_finished = 1;
+ conn->write_offset = 0;
+ }
+ tcp_output(conn->pcb.tcp);
+ } else if (err == ERR_MEM) {
+ /* If ERR_MEM, we wait for sent_tcp or poll_tcp to be called
+ we do NOT return to the application thread, since ERR_MEM is
+ only a temporary error! */
+
+ /* tcp_write returned ERR_MEM, try tcp_output anyway */
+ tcp_output(conn->pcb.tcp);
+
+ #if LWIP_TCPIP_CORE_LOCKING
+ conn->flags |= NETCONN_FLAG_WRITE_DELAYED;
+ #endif
+ } else {
+ /* On errors != ERR_MEM, we don't try writing any more but return
+ the error to the application thread. */
+ write_finished = 1;
+ }
+ }
+
+ if (write_finished) {
+ /* everything was written: set back connection state
+ and back to application task */
+ conn->current_msg->err = err;
+ conn->current_msg = NULL;
+ conn->state = NETCONN_NONE;
+#if LWIP_TCPIP_CORE_LOCKING
+ if ((conn->flags & NETCONN_FLAG_WRITE_DELAYED) != 0)
+#endif
+ {
+ sys_sem_signal(&conn->op_completed);
+ }
+ }
+#if LWIP_TCPIP_CORE_LOCKING
+ else
+ return ERR_MEM;
+#endif
+ return ERR_OK;
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Send some data on a TCP pcb contained in a netconn
+ * Called from netconn_write
+ *
+ * @param msg the api_msg_msg pointing to the connection
+ */
+void
+do_write(struct api_msg_msg *msg)
+{
+ if (ERR_IS_FATAL(msg->conn->last_err)) {
+ msg->err = msg->conn->last_err;
+ } else {
+ if (msg->conn->type == NETCONN_TCP) {
+#if LWIP_TCP
+ if (msg->conn->state != NETCONN_NONE) {
+ /* netconn is connecting, closing or in blocking write */
+ msg->err = ERR_INPROGRESS;
+ } else if (msg->conn->pcb.tcp != NULL) {
+ msg->conn->state = NETCONN_WRITE;
+ /* set all the variables used by do_writemore */
+ LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL &&
+ msg->conn->write_offset == 0);
+ LWIP_ASSERT("msg->msg.w.len != 0", msg->msg.w.len != 0);
+ msg->conn->current_msg = msg;
+ msg->conn->write_offset = 0;
+#if LWIP_TCPIP_CORE_LOCKING
+ msg->conn->flags &= ~NETCONN_FLAG_WRITE_DELAYED;
+ if (do_writemore(msg->conn) != ERR_OK) {
+ LWIP_ASSERT("state!", msg->conn->state == NETCONN_WRITE);
+ UNLOCK_TCPIP_CORE();
+ sys_arch_sem_wait(&msg->conn->op_completed, 0);
+ LOCK_TCPIP_CORE();
+ LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE);
+ }
+#else /* LWIP_TCPIP_CORE_LOCKING */
+ do_writemore(msg->conn);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+ /* for both cases: if do_writemore was called, don't ACK the APIMSG
+ since do_writemore ACKs it! */
+ return;
+ } else {
+ msg->err = ERR_CONN;
+ }
+#else /* LWIP_TCP */
+ msg->err = ERR_VAL;
+#endif /* LWIP_TCP */
+#if (LWIP_UDP || LWIP_RAW)
+ } else {
+ msg->err = ERR_VAL;
+#endif /* (LWIP_UDP || LWIP_RAW) */
+ }
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+
+/**
+ * Return a connection's local or remote address
+ * Called from netconn_getaddr
+ *
+ * @param msg the api_msg_msg pointing to the connection
+ */
+void
+do_getaddr(struct api_msg_msg *msg)
+{
+ if (msg->conn->pcb.ip != NULL) {
+ *(msg->msg.ad.ipaddr) = (msg->msg.ad.local ? msg->conn->pcb.ip->local_ip :
+ msg->conn->pcb.ip->remote_ip);
+
+ msg->err = ERR_OK;
+ switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+ case NETCONN_RAW:
+ if (msg->msg.ad.local) {
+ *(msg->msg.ad.port) = msg->conn->pcb.raw->protocol;
+ } else {
+ /* return an error as connecting is only a helper for upper layers */
+ msg->err = ERR_CONN;
+ }
+ break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+ case NETCONN_UDP:
+ if (msg->msg.ad.local) {
+ *(msg->msg.ad.port) = msg->conn->pcb.udp->local_port;
+ } else {
+ if ((msg->conn->pcb.udp->flags & UDP_FLAGS_CONNECTED) == 0) {
+ msg->err = ERR_CONN;
+ } else {
+ *(msg->msg.ad.port) = msg->conn->pcb.udp->remote_port;
+ }
+ }
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case NETCONN_TCP:
+ *(msg->msg.ad.port) = (msg->msg.ad.local?msg->conn->pcb.tcp->local_port:msg->conn->pcb.tcp->remote_port);
+ break;
+#endif /* LWIP_TCP */
+ default:
+ LWIP_ASSERT("invalid netconn_type", 0);
+ break;
+ }
+ } else {
+ msg->err = ERR_CONN;
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+
+/**
+ * Close a TCP pcb contained in a netconn
+ * Called from netconn_close
+ *
+ * @param msg the api_msg_msg pointing to the connection
+ */
+void
+do_close(struct api_msg_msg *msg)
+{
+#if LWIP_TCP
+ /* @todo: abort running write/connect? */
+ if ((msg->conn->state != NETCONN_NONE) && (msg->conn->state != NETCONN_LISTEN)) {
+ /* this only happens for TCP netconns */
+ LWIP_ASSERT("msg->conn->type == NETCONN_TCP", msg->conn->type == NETCONN_TCP);
+ msg->err = ERR_INPROGRESS;
+ } else if ((msg->conn->pcb.tcp != NULL) && (msg->conn->type == NETCONN_TCP)) {
+ if ((msg->msg.sd.shut != NETCONN_SHUT_RDWR) && (msg->conn->state == NETCONN_LISTEN)) {
+ /* LISTEN doesn't support half shutdown */
+ msg->err = ERR_CONN;
+ } else {
+ if (msg->msg.sd.shut & NETCONN_SHUT_RD) {
+ /* Drain and delete mboxes */
+ netconn_drain(msg->conn);
+ }
+ LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL &&
+ msg->conn->write_offset == 0);
+ msg->conn->state = NETCONN_CLOSE;
+ msg->conn->current_msg = msg;
+ do_close_internal(msg->conn);
+ /* for tcp netconns, do_close_internal ACKs the message */
+ return;
+ }
+ } else
+#endif /* LWIP_TCP */
+ {
+ msg->err = ERR_VAL;
+ }
+ sys_sem_signal(&msg->conn->op_completed);
+}
+
+#if LWIP_IGMP
+/**
+ * Join multicast groups for UDP netconns.
+ * Called from netconn_join_leave_group
+ *
+ * @param msg the api_msg_msg pointing to the connection
+ */
+void
+do_join_leave_group(struct api_msg_msg *msg)
+{
+ if (ERR_IS_FATAL(msg->conn->last_err)) {
+ msg->err = msg->conn->last_err;
+ } else {
+ if (msg->conn->pcb.tcp != NULL) {
+ if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
+#if LWIP_UDP
+ if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
+ msg->err = igmp_joingroup(msg->msg.jl.netif_addr, msg->msg.jl.multiaddr);
+ } else {
+ msg->err = igmp_leavegroup(msg->msg.jl.netif_addr, msg->msg.jl.multiaddr);
+ }
+#endif /* LWIP_UDP */
+#if (LWIP_TCP || LWIP_RAW)
+ } else {
+ msg->err = ERR_VAL;
+#endif /* (LWIP_TCP || LWIP_RAW) */
+ }
+ } else {
+ msg->err = ERR_CONN;
+ }
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+#endif /* LWIP_IGMP */
+
+#if LWIP_DNS
+/**
+ * Callback function that is called when DNS name is resolved
+ * (or on timeout). A waiting application thread is waked up by
+ * signaling the semaphore.
+ */
+static void
+do_dns_found(const char *name, ip_addr_t *ipaddr, void *arg)
+{
+ struct dns_api_msg *msg = (struct dns_api_msg*)arg;
+
+ LWIP_ASSERT("DNS response for wrong host name", strcmp(msg->name, name) == 0);
+ LWIP_UNUSED_ARG(name);
+
+ if (ipaddr == NULL) {
+ /* timeout or memory error */
+ *msg->err = ERR_VAL;
+ } else {
+ /* address was resolved */
+ *msg->err = ERR_OK;
+ *msg->addr = *ipaddr;
+ }
+ /* wake up the application task waiting in netconn_gethostbyname */
+ sys_sem_signal(msg->sem);
+}
+
+/**
+ * Execute a DNS query
+ * Called from netconn_gethostbyname
+ *
+ * @param arg the dns_api_msg pointing to the query
+ */
+void
+do_gethostbyname(void *arg)
+{
+ struct dns_api_msg *msg = (struct dns_api_msg*)arg;
+
+ *msg->err = dns_gethostbyname(msg->name, msg->addr, do_dns_found, msg);
+ if (*msg->err != ERR_INPROGRESS) {
+ /* on error or immediate success, wake up the application
+ * task waiting in netconn_gethostbyname */
+ sys_sem_signal(msg->sem);
+ }
+}
+#endif /* LWIP_DNS */
+
+#endif /* LWIP_NETCONN */
diff --git a/app/lwip/api/err.c b/app/lwip/api/err.c
new file mode 100644
index 00000000..b0a4eb3e
--- /dev/null
+++ b/app/lwip/api/err.c
@@ -0,0 +1,75 @@
+/**
+ * @file
+ * Error Management module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ *
+ */
+
+#include "lwip/err.h"
+
+#ifdef LWIP_DEBUG
+
+static const char *err_strerr[] = {
+ "Ok.", /* ERR_OK 0 */
+ "Out of memory error.", /* ERR_MEM -1 */
+ "Buffer error.", /* ERR_BUF -2 */
+ "Timeout.", /* ERR_TIMEOUT -3 */
+ "Routing problem.", /* ERR_RTE -4 */
+ "Operation in progress.", /* ERR_INPROGRESS -5 */
+ "Illegal value.", /* ERR_VAL -6 */
+ "Operation would block.", /* ERR_WOULDBLOCK -7 */
+ "Connection aborted.", /* ERR_ABRT -8 */
+ "Connection reset.", /* ERR_RST -9 */
+ "Connection closed.", /* ERR_CLSD -10 */
+ "Not connected.", /* ERR_CONN -11 */
+ "Illegal argument.", /* ERR_ARG -12 */
+ "Address in use.", /* ERR_USE -13 */
+ "Low-level netif error.", /* ERR_IF -14 */
+ "Already connected.", /* ERR_ISCONN -15 */
+};
+
+/**
+ * Convert an lwip internal error to a string representation.
+ *
+ * @param err an lwip internal err_t
+ * @return a string representation for err
+ */
+const char *
+lwip_strerr(err_t err)
+{
+ return err_strerr[-err];
+
+}
+
+#endif /* LWIP_DEBUG */
diff --git a/app/lwip/api/netbuf.c b/app/lwip/api/netbuf.c
new file mode 100644
index 00000000..886f66b0
--- /dev/null
+++ b/app/lwip/api/netbuf.c
@@ -0,0 +1,245 @@
+/**
+ * @file
+ * Network buffer management
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/netbuf.h"
+#include "lwip/memp.h"
+
+#include
+
+/**
+ * Create (allocate) and initialize a new netbuf.
+ * The netbuf doesn't yet contain a packet buffer!
+ * һµnetbufռ䣬κݿռ
+ * @return a pointer to a new netbuf
+ * NULL on lack of memory
+ */
+struct
+netbuf *netbuf_new(void)
+{
+ struct netbuf *buf;
+
+ buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
+ if (buf != NULL) {
+ buf->p = NULL;
+ buf->ptr = NULL;
+ ip_addr_set_any(&buf->addr);
+ buf->port = 0;
+#if LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY
+#if LWIP_CHECKSUM_ON_COPY
+ buf->flags = 0;
+#endif /* LWIP_CHECKSUM_ON_COPY */
+ buf->toport_chksum = 0;
+#if LWIP_NETBUF_RECVINFO
+ ip_addr_set_any(&buf->toaddr);
+#endif /* LWIP_NETBUF_RECVINFO */
+#endif /* LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY */
+ return buf;
+ } else {
+ return NULL;
+ }
+}
+
+/**
+ * Deallocate a netbuf allocated by netbuf_new().
+ * ͷһnetbufռ
+ * @param buf pointer to a netbuf allocated by netbuf_new()
+ */
+void
+netbuf_delete(struct netbuf *buf)
+{
+ if (buf != NULL) {
+ if (buf->p != NULL) {
+ pbuf_free(buf->p);
+ buf->p = buf->ptr = NULL;
+ }
+ memp_free(MEMP_NETBUF, buf);
+ }
+}
+
+/**
+ * Allocate memory for a packet buffer for a given netbuf.
+ *ΪnetbufṹsizeСݿռ
+ * @param buf the netbuf for which to allocate a packet buffer
+ * @param size the size of the packet buffer to allocate
+ * @return pointer to the allocated memory
+ * NULL if no memory could be allocated
+ */
+void *
+netbuf_alloc(struct netbuf *buf, u16_t size)
+{
+ LWIP_ERROR("netbuf_alloc: invalid buf", (buf != NULL), return NULL;);
+
+ /* Deallocate any previously allocated memory. */
+ if (buf->p != NULL) {
+ pbuf_free(buf->p);
+ }
+ buf->p = pbuf_alloc(PBUF_TRANSPORT, size, PBUF_RAM);
+ if (buf->p == NULL) {
+ return NULL;
+ }
+ LWIP_ASSERT("check that first pbuf can hold size",
+ (buf->p->len >= size));
+ buf->ptr = buf->p;
+ return buf->p->payload;
+}
+
+/**
+ * Free the packet buffer included in a netbuf
+ *ͷnetbufṹָpbuf
+ * @param buf pointer to the netbuf which contains the packet buffer to free
+ */
+void
+netbuf_free(struct netbuf *buf)
+{
+ LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return;);
+ if (buf->p != NULL) {
+ pbuf_free(buf->p);
+ }
+ buf->p = buf->ptr = NULL;
+}
+
+/**
+ * Let a netbuf reference existing (non-volatile) data.
+ *
+ * @param buf netbuf which should reference the data
+ * @param dataptr pointer to the data to reference
+ * @param size size of the data
+ * @return ERR_OK if data is referenced
+ * ERR_MEM if data couldn't be referenced due to lack of memory
+ */
+err_t
+netbuf_ref(struct netbuf *buf, const void *dataptr, u16_t size)
+{
+ LWIP_ERROR("netbuf_ref: invalid buf", (buf != NULL), return ERR_ARG;);
+ if (buf->p != NULL) {
+ pbuf_free(buf->p);
+ }
+ buf->p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF);
+ if (buf->p == NULL) {
+ buf->ptr = NULL;
+ return ERR_MEM;
+ }
+ buf->p->payload = (void*)dataptr;
+ buf->p->len = buf->p->tot_len = size;
+ buf->ptr = buf->p;
+ return ERR_OK;
+}
+
+/**
+ * Chain one netbuf to another (@see pbuf_chain)
+ *
+ * @param head the first netbuf
+ * @param tail netbuf to chain after head, freed by this function, may not be reference after returning
+ */
+void
+netbuf_chain(struct netbuf *head, struct netbuf *tail)
+{
+ LWIP_ERROR("netbuf_ref: invalid head", (head != NULL), return;);
+ LWIP_ERROR("netbuf_chain: invalid tail", (tail != NULL), return;);
+ pbuf_cat(head->p, tail->p);
+ head->ptr = head->p;
+ memp_free(MEMP_NETBUF, tail);
+}
+
+/**
+ * Get the data pointer and length of the data inside a netbuf.
+ *
+ * @param buf netbuf to get the data from
+ * @param dataptr pointer to a void pointer where to store the data pointer
+ * @param len pointer to an u16_t where the length of the data is stored
+ * @return ERR_OK if the information was retreived,
+ * ERR_BUF on error.
+ */
+err_t
+netbuf_data(struct netbuf *buf, void **dataptr, u16_t *len)
+{
+ LWIP_ERROR("netbuf_data: invalid buf", (buf != NULL), return ERR_ARG;);
+ LWIP_ERROR("netbuf_data: invalid dataptr", (dataptr != NULL), return ERR_ARG;);
+ LWIP_ERROR("netbuf_data: invalid len", (len != NULL), return ERR_ARG;);
+
+ if (buf->ptr == NULL) {
+ return ERR_BUF;
+ }
+ *dataptr = buf->ptr->payload;
+ *len = buf->ptr->len;
+ return ERR_OK;
+}
+
+/**
+ * Move the current data pointer of a packet buffer contained in a netbuf
+ * to the next part.
+ * The packet buffer itself is not modified.
+ *
+ * @param buf the netbuf to modify
+ * @return -1 if there is no next part
+ * 1 if moved to the next part but now there is no next part
+ * 0 if moved to the next part and there are still more parts
+ */
+s8_t
+netbuf_next(struct netbuf *buf)
+{
+ LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return -1;);
+ if (buf->ptr->next == NULL) {
+ return -1;
+ }
+ buf->ptr = buf->ptr->next;
+ if (buf->ptr->next == NULL) {
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * Move the current data pointer of a packet buffer contained in a netbuf
+ * to the beginning of the packet.
+ * The packet buffer itself is not modified.
+ *
+ * @param buf the netbuf to modify
+ */
+void
+netbuf_first(struct netbuf *buf)
+{
+ LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return;);
+ buf->ptr = buf->p;
+}
+
+#endif /* LWIP_NETCONN */
diff --git a/app/lwip/api/netdb.c b/app/lwip/api/netdb.c
new file mode 100644
index 00000000..a7e4e06b
--- /dev/null
+++ b/app/lwip/api/netdb.c
@@ -0,0 +1,352 @@
+/**
+ * @file
+ * API functions for name resolving
+ *
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ *
+ */
+
+#include "lwip/netdb.h"
+
+#if LWIP_DNS && LWIP_SOCKET
+
+#include "lwip/err.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/ip_addr.h"
+#include "lwip/api.h"
+#include "lwip/dns.h"
+
+#include
+#include
+
+/** helper struct for gethostbyname_r to access the char* buffer */
+struct gethostbyname_r_helper {
+ ip_addr_t *addrs;
+ ip_addr_t addr;
+ char *aliases;
+};
+
+/** h_errno is exported in netdb.h for access by applications. */
+#if LWIP_DNS_API_DECLARE_H_ERRNO
+int h_errno;
+#endif /* LWIP_DNS_API_DECLARE_H_ERRNO */
+
+/** define "hostent" variables storage: 0 if we use a static (but unprotected)
+ * set of variables for lwip_gethostbyname, 1 if we use a local storage */
+#ifndef LWIP_DNS_API_HOSTENT_STORAGE
+#define LWIP_DNS_API_HOSTENT_STORAGE 0
+#endif
+
+/** define "hostent" variables storage */
+#if LWIP_DNS_API_HOSTENT_STORAGE
+#define HOSTENT_STORAGE
+#else
+#define HOSTENT_STORAGE static
+#endif /* LWIP_DNS_API_STATIC_HOSTENT */
+
+/**
+ * Returns an entry containing addresses of address family AF_INET
+ * for the host with name name.
+ * Due to dns_gethostbyname limitations, only one address is returned.
+ *
+ * @param name the hostname to resolve
+ * @return an entry containing addresses of address family AF_INET
+ * for the host with name name
+ */
+struct hostent*
+lwip_gethostbyname(const char *name)
+{
+ err_t err;
+ ip_addr_t addr;
+
+ /* buffer variables for lwip_gethostbyname() */
+ HOSTENT_STORAGE struct hostent s_hostent;
+ HOSTENT_STORAGE char *s_aliases;
+ HOSTENT_STORAGE ip_addr_t s_hostent_addr;
+ HOSTENT_STORAGE ip_addr_t *s_phostent_addr[2];
+
+ /* query host IP address */
+ err = netconn_gethostbyname(name, &addr);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err));
+ h_errno = HOST_NOT_FOUND;
+ return NULL;
+ }
+
+ /* fill hostent */
+ s_hostent_addr = addr;
+ s_phostent_addr[0] = &s_hostent_addr;
+ s_phostent_addr[1] = NULL;
+ s_hostent.h_name = (char*)name;
+ s_hostent.h_aliases = &s_aliases;
+ s_hostent.h_addrtype = AF_INET;
+ s_hostent.h_length = sizeof(ip_addr_t);
+ s_hostent.h_addr_list = (char**)&s_phostent_addr;
+
+#if DNS_DEBUG
+ /* dump hostent */
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_name == %s\n", s_hostent.h_name));
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases == %p\n", s_hostent.h_aliases));
+ if (s_hostent.h_aliases != NULL) {
+ u8_t idx;
+ for ( idx=0; s_hostent.h_aliases[idx]; idx++) {
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases[%i]-> == %p\n", idx, s_hostent.h_aliases[idx]));
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases[%i]-> == %s\n", idx, s_hostent.h_aliases[idx]));
+ }
+ }
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addrtype == %d\n", s_hostent.h_addrtype));
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_length == %d\n", s_hostent.h_length));
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list == %p\n", s_hostent.h_addr_list));
+ if (s_hostent.h_addr_list != NULL) {
+ u8_t idx;
+ for ( idx=0; s_hostent.h_addr_list[idx]; idx++) {
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i] == %p\n", idx, s_hostent.h_addr_list[idx]));
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i]-> == %s\n", idx, ip_ntoa((ip_addr_t*)s_hostent.h_addr_list[idx])));
+ }
+ }
+#endif /* DNS_DEBUG */
+
+#if LWIP_DNS_API_HOSTENT_STORAGE
+ /* this function should return the "per-thread" hostent after copy from s_hostent */
+ return sys_thread_hostent(&s_hostent);
+#else
+ return &s_hostent;
+#endif /* LWIP_DNS_API_HOSTENT_STORAGE */
+}
+
+/**
+ * Thread-safe variant of lwip_gethostbyname: instead of using a static
+ * buffer, this function takes buffer and errno pointers as arguments
+ * and uses these for the result.
+ *
+ * @param name the hostname to resolve
+ * @param ret pre-allocated struct where to store the result
+ * @param buf pre-allocated buffer where to store additional data
+ * @param buflen the size of buf
+ * @param result pointer to a hostent pointer that is set to ret on success
+ * and set to zero on error
+ * @param h_errnop pointer to an int where to store errors (instead of modifying
+ * the global h_errno)
+ * @return 0 on success, non-zero on error, additional error information
+ * is stored in *h_errnop instead of h_errno to be thread-safe
+ */
+int
+lwip_gethostbyname_r(const char *name, struct hostent *ret, char *buf,
+ size_t buflen, struct hostent **result, int *h_errnop)
+{
+ err_t err;
+ struct gethostbyname_r_helper *h;
+ char *hostname;
+ size_t namelen;
+ int lh_errno;
+
+ if (h_errnop == NULL) {
+ /* ensure h_errnop is never NULL */
+ h_errnop = &lh_errno;
+ }
+
+ if (result == NULL) {
+ /* not all arguments given */
+ *h_errnop = EINVAL;
+ return -1;
+ }
+ /* first thing to do: set *result to nothing */
+ *result = NULL;
+ if ((name == NULL) || (ret == NULL) || (buf == 0)) {
+ /* not all arguments given */
+ *h_errnop = EINVAL;
+ return -1;
+ }
+
+ namelen = strlen(name);
+ if (buflen < (sizeof(struct gethostbyname_r_helper) + namelen + 1 + (MEM_ALIGNMENT - 1))) {
+ /* buf can't hold the data needed + a copy of name */
+ *h_errnop = ERANGE;
+ return -1;
+ }
+
+ h = (struct gethostbyname_r_helper*)LWIP_MEM_ALIGN(buf);
+ hostname = ((char*)h) + sizeof(struct gethostbyname_r_helper);
+
+ /* query host IP address */
+ err = netconn_gethostbyname(name, &(h->addr));
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err));
+ *h_errnop = ENSRNOTFOUND;
+ return -1;
+ }
+
+ /* copy the hostname into buf */
+ MEMCPY(hostname, name, namelen);
+ hostname[namelen] = 0;
+
+ /* fill hostent */
+ h->addrs = &(h->addr);
+ h->aliases = NULL;
+ ret->h_name = (char*)hostname;
+ ret->h_aliases = &(h->aliases);
+ ret->h_addrtype = AF_INET;
+ ret->h_length = sizeof(ip_addr_t);
+ ret->h_addr_list = (char**)&(h->addrs);
+
+ /* set result != NULL */
+ *result = ret;
+
+ /* return success */
+ return 0;
+}
+
+/**
+ * Frees one or more addrinfo structures returned by getaddrinfo(), along with
+ * any additional storage associated with those structures. If the ai_next field
+ * of the structure is not null, the entire list of structures is freed.
+ *
+ * @param ai struct addrinfo to free
+ */
+void
+lwip_freeaddrinfo(struct addrinfo *ai)
+{
+ struct addrinfo *next;
+
+ while (ai != NULL) {
+ next = ai->ai_next;
+ memp_free(MEMP_NETDB, ai);
+ ai = next;
+ }
+}
+
+/**
+ * Translates the name of a service location (for example, a host name) and/or
+ * a service name and returns a set of socket addresses and associated
+ * information to be used in creating a socket with which to address the
+ * specified service.
+ * Memory for the result is allocated internally and must be freed by calling
+ * lwip_freeaddrinfo()!
+ *
+ * Due to a limitation in dns_gethostbyname, only the first address of a
+ * host is returned.
+ * Also, service names are not supported (only port numbers)!
+ *
+ * @param nodename descriptive name or address string of the host
+ * (may be NULL -> local address)
+ * @param servname port number as string of NULL
+ * @param hints structure containing input values that set socktype and protocol
+ * @param res pointer to a pointer where to store the result (set to NULL on failure)
+ * @return 0 on success, non-zero on failure
+ */
+int
+lwip_getaddrinfo(const char *nodename, const char *servname,
+ const struct addrinfo *hints, struct addrinfo **res)
+{
+ err_t err;
+ ip_addr_t addr;
+ struct addrinfo *ai;
+ struct sockaddr_in *sa = NULL;
+ int port_nr = 0;
+ size_t total_size;
+ size_t namelen = 0;
+
+ if (res == NULL) {
+ return EAI_FAIL;
+ }
+ *res = NULL;
+ if ((nodename == NULL) && (servname == NULL)) {
+ return EAI_NONAME;
+ }
+
+ if (servname != NULL) {
+ /* service name specified: convert to port number
+ * @todo?: currently, only ASCII integers (port numbers) are supported! */
+ port_nr = atoi(servname);
+ if ((port_nr <= 0) || (port_nr > 0xffff)) {
+ return EAI_SERVICE;
+ }
+ }
+
+ if (nodename != NULL) {
+ /* service location specified, try to resolve */
+ err = netconn_gethostbyname(nodename, &addr);
+ if (err != ERR_OK) {
+ return EAI_FAIL;
+ }
+ } else {
+ /* service location specified, use loopback address */
+ ip_addr_set_loopback(&addr);
+ }
+
+ total_size = sizeof(struct addrinfo) + sizeof(struct sockaddr_in);
+ if (nodename != NULL) {
+ namelen = strlen(nodename);
+ LWIP_ASSERT("namelen is too long", (namelen + 1) <= (mem_size_t)-1);
+ total_size += namelen + 1;
+ }
+ /* If this fails, please report to lwip-devel! :-) */
+ LWIP_ASSERT("total_size <= NETDB_ELEM_SIZE: please report this!",
+ total_size <= NETDB_ELEM_SIZE);
+ ai = (struct addrinfo *)memp_malloc(MEMP_NETDB);
+ if (ai == NULL) {
+ goto memerr;
+ }
+ memset(ai, 0, total_size);
+ sa = (struct sockaddr_in*)((u8_t*)ai + sizeof(struct addrinfo));
+ /* set up sockaddr */
+ inet_addr_from_ipaddr(&sa->sin_addr, &addr);
+ sa->sin_family = AF_INET;
+ sa->sin_len = sizeof(struct sockaddr_in);
+ sa->sin_port = htons((u16_t)port_nr);
+
+ /* set up addrinfo */
+ ai->ai_family = AF_INET;
+ if (hints != NULL) {
+ /* copy socktype & protocol from hints if specified */
+ ai->ai_socktype = hints->ai_socktype;
+ ai->ai_protocol = hints->ai_protocol;
+ }
+ if (nodename != NULL) {
+ /* copy nodename to canonname if specified */
+ ai->ai_canonname = ((char*)ai + sizeof(struct addrinfo) + sizeof(struct sockaddr_in));
+ MEMCPY(ai->ai_canonname, nodename, namelen);
+ ai->ai_canonname[namelen] = 0;
+ }
+ ai->ai_addrlen = sizeof(struct sockaddr_in);
+ ai->ai_addr = (struct sockaddr*)sa;
+
+ *res = ai;
+
+ return 0;
+memerr:
+ if (ai != NULL) {
+ memp_free(MEMP_NETDB, ai);
+ }
+ return EAI_MEMORY;
+}
+
+#endif /* LWIP_DNS && LWIP_SOCKET */
diff --git a/app/lwip/api/netifapi.c b/app/lwip/api/netifapi.c
new file mode 100644
index 00000000..43e47203
--- /dev/null
+++ b/app/lwip/api/netifapi.c
@@ -0,0 +1,160 @@
+/**
+ * @file
+ * Network Interface Sequential API module
+ *
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_NETIF_API /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/netifapi.h"
+#include "lwip/tcpip.h"
+
+/**
+ * Call netif_add() inside the tcpip_thread context.
+ */
+void
+do_netifapi_netif_add(struct netifapi_msg_msg *msg)
+{
+ if (!netif_add( msg->netif,
+ msg->msg.add.ipaddr,
+ msg->msg.add.netmask,
+ msg->msg.add.gw,
+ msg->msg.add.state,
+ msg->msg.add.init,
+ msg->msg.add.input)) {
+ msg->err = ERR_IF;
+ } else {
+ msg->err = ERR_OK;
+ }
+ TCPIP_NETIFAPI_ACK(msg);
+}
+
+/**
+ * Call netif_set_addr() inside the tcpip_thread context.
+ */
+void
+do_netifapi_netif_set_addr(struct netifapi_msg_msg *msg)
+{
+ netif_set_addr( msg->netif,
+ msg->msg.add.ipaddr,
+ msg->msg.add.netmask,
+ msg->msg.add.gw);
+ msg->err = ERR_OK;
+ TCPIP_NETIFAPI_ACK(msg);
+}
+
+/**
+ * Call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) inside the
+ * tcpip_thread context.
+ */
+void
+do_netifapi_netif_common(struct netifapi_msg_msg *msg)
+{
+ if (msg->msg.common.errtfunc != NULL) {
+ msg->err = msg->msg.common.errtfunc(msg->netif);
+ } else {
+ msg->err = ERR_OK;
+ msg->msg.common.voidfunc(msg->netif);
+ }
+ TCPIP_NETIFAPI_ACK(msg);
+}
+
+/**
+ * Call netif_add() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ *
+ * @note for params @see netif_add()
+ */
+err_t
+netifapi_netif_add(struct netif *netif,
+ ip_addr_t *ipaddr,
+ ip_addr_t *netmask,
+ ip_addr_t *gw,
+ void *state,
+ netif_init_fn init,
+ netif_input_fn input)
+{
+ struct netifapi_msg msg;
+ msg.function = do_netifapi_netif_add;
+ msg.msg.netif = netif;
+ msg.msg.msg.add.ipaddr = ipaddr;
+ msg.msg.msg.add.netmask = netmask;
+ msg.msg.msg.add.gw = gw;
+ msg.msg.msg.add.state = state;
+ msg.msg.msg.add.init = init;
+ msg.msg.msg.add.input = input;
+ TCPIP_NETIFAPI(&msg);
+ return msg.msg.err;
+}
+
+/**
+ * Call netif_set_addr() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ *
+ * @note for params @see netif_set_addr()
+ */
+err_t
+netifapi_netif_set_addr(struct netif *netif,
+ ip_addr_t *ipaddr,
+ ip_addr_t *netmask,
+ ip_addr_t *gw)
+{
+ struct netifapi_msg msg;
+ msg.function = do_netifapi_netif_set_addr;
+ msg.msg.netif = netif;
+ msg.msg.msg.add.ipaddr = ipaddr;
+ msg.msg.msg.add.netmask = netmask;
+ msg.msg.msg.add.gw = gw;
+ TCPIP_NETIFAPI(&msg);
+ return msg.msg.err;
+}
+
+/**
+ * call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) in a thread-safe
+ * way by running that function inside the tcpip_thread context.
+ *
+ * @note use only for functions where there is only "netif" parameter.
+ */
+err_t
+netifapi_netif_common(struct netif *netif, netifapi_void_fn voidfunc,
+ netifapi_errt_fn errtfunc)
+{
+ struct netifapi_msg msg;
+ msg.function = do_netifapi_netif_common;
+ msg.msg.netif = netif;
+ msg.msg.msg.common.voidfunc = voidfunc;
+ msg.msg.msg.common.errtfunc = errtfunc;
+ TCPIP_NETIFAPI(&msg);
+ return msg.msg.err;
+}
+
+#endif /* LWIP_NETIF_API */
diff --git a/app/lwip/api/sockets.c b/app/lwip/api/sockets.c
new file mode 100644
index 00000000..f3afd630
--- /dev/null
+++ b/app/lwip/api/sockets.c
@@ -0,0 +1,2343 @@
+/**
+ * @file
+ * Sockets BSD-Like API module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ *
+ * Improved by Marc Boucher and David Haas
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/sockets.h"
+#include "lwip/api.h"
+#include "lwip/sys.h"
+#include "lwip/igmp.h"
+#include "lwip/inet.h"
+#include "lwip/tcp.h"
+#include "lwip/raw.h"
+#include "lwip/udp.h"
+#include "lwip/tcpip.h"
+#include "lwip/pbuf.h"
+#if LWIP_CHECKSUM_ON_COPY
+#include "lwip/inet_chksum.h"
+#endif
+
+#include
+
+#define NUM_SOCKETS MEMP_NUM_NETCONN
+
+/** Contains all internal pointers and states used for a socket */
+struct lwip_sock {
+ /** sockets currently are built on netconns, each socket has one netconn */
+ struct netconn *conn;
+ /** data that was left from the previous read */
+ void *lastdata;
+ /** offset in the data that was left from the previous read */
+ u16_t lastoffset;
+ /** number of times data was received, set by event_callback(),
+ tested by the receive and select functions */
+ s16_t rcvevent;
+ /** number of times data was ACKed (free send buffer), set by event_callback(),
+ tested by select */
+ u16_t sendevent;
+ /** error happened for this socket, set by event_callback(), tested by select */
+ u16_t errevent;
+ /** last error that occurred on this socket */
+ int err;
+ /** counter of how many threads are waiting for this socket using select */
+ int select_waiting;
+};
+
+/** Description for a task waiting in select */
+struct lwip_select_cb {
+ /** Pointer to the next waiting task */
+ struct lwip_select_cb *next;
+ /** Pointer to the previous waiting task */
+ struct lwip_select_cb *prev;
+ /** readset passed to select */
+ fd_set *readset;
+ /** writeset passed to select */
+ fd_set *writeset;
+ /** unimplemented: exceptset passed to select */
+ fd_set *exceptset;
+ /** don't signal the same semaphore twice: set to 1 when signalled */
+ int sem_signalled;
+ /** semaphore to wake up a task waiting for select */
+ sys_sem_t sem;
+};
+
+/** This struct is used to pass data to the set/getsockopt_internal
+ * functions running in tcpip_thread context (only a void* is allowed) */
+struct lwip_setgetsockopt_data {
+ /** socket struct for which to change options */
+ struct lwip_sock *sock;
+#ifdef LWIP_DEBUG
+ /** socket index for which to change options */
+ int s;
+#endif /* LWIP_DEBUG */
+ /** level of the option to process */
+ int level;
+ /** name of the option to process */
+ int optname;
+ /** set: value to set the option to
+ * get: value of the option is stored here */
+ void *optval;
+ /** size of *optval */
+ socklen_t *optlen;
+ /** if an error occures, it is temporarily stored here */
+ err_t err;
+};
+
+/** The global array of available sockets */
+static struct lwip_sock sockets[NUM_SOCKETS];
+/** The global list of tasks waiting for select */
+static struct lwip_select_cb *select_cb_list;
+/** This counter is increased from lwip_select when the list is chagned
+ and checked in event_callback to see if it has changed. */
+static volatile int select_cb_ctr;
+
+/** Table to quickly map an lwIP error (err_t) to a socket error
+ * by using -err as an index */
+static const int err_to_errno_table[] = {
+ 0, /* ERR_OK 0 No error, everything OK. */
+ ENOMEM, /* ERR_MEM -1 Out of memory error. */
+ ENOBUFS, /* ERR_BUF -2 Buffer error. */
+ EWOULDBLOCK, /* ERR_TIMEOUT -3 Timeout */
+ EHOSTUNREACH, /* ERR_RTE -4 Routing problem. */
+ EINPROGRESS, /* ERR_INPROGRESS -5 Operation in progress */
+ EINVAL, /* ERR_VAL -6 Illegal value. */
+ EWOULDBLOCK, /* ERR_WOULDBLOCK -7 Operation would block. */
+ ECONNABORTED, /* ERR_ABRT -8 Connection aborted. */
+ ECONNRESET, /* ERR_RST -9 Connection reset. */
+ ESHUTDOWN, /* ERR_CLSD -10 Connection closed. */
+ ENOTCONN, /* ERR_CONN -11 Not connected. */
+ EIO, /* ERR_ARG -12 Illegal argument. */
+ EADDRINUSE, /* ERR_USE -13 Address in use. */
+ -1, /* ERR_IF -14 Low-level netif error */
+ -1, /* ERR_ISCONN -15 Already connected. */
+};
+
+#define ERR_TO_ERRNO_TABLE_SIZE \
+ (sizeof(err_to_errno_table)/sizeof(err_to_errno_table[0]))
+
+#define err_to_errno(err) \
+ ((unsigned)(-(err)) < ERR_TO_ERRNO_TABLE_SIZE ? \
+ err_to_errno_table[-(err)] : EIO)
+
+#ifdef ERRNO
+#ifndef set_errno
+#define set_errno(err) errno = (err)
+#endif
+#else /* ERRNO */
+#define set_errno(err)
+#endif /* ERRNO */
+
+#define sock_set_errno(sk, e) do { \
+ sk->err = (e); \
+ set_errno(sk->err); \
+} while (0)
+
+/* Forward delcaration of some functions */
+static void event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len);
+static void lwip_getsockopt_internal(void *arg);
+static void lwip_setsockopt_internal(void *arg);
+
+/**
+ * Initialize this module. This function has to be called before any other
+ * functions in this module!
+ */
+void
+lwip_socket_init(void)
+{
+}
+
+/**
+ * Map a externally used socket index to the internal socket representation.
+ *
+ * @param s externally used socket index
+ * @return struct lwip_sock for the socket or NULL if not found
+ */
+static struct lwip_sock *
+get_socket(int s)
+{
+ struct lwip_sock *sock;
+
+ if ((s < 0) || (s >= NUM_SOCKETS)) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): invalid\n", s));
+ set_errno(EBADF);
+ return NULL;
+ }
+
+ sock = &sockets[s];
+
+ if (!sock->conn) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): not active\n", s));
+ set_errno(EBADF);
+ return NULL;
+ }
+
+ return sock;
+}
+
+/**
+ * Same as get_socket but doesn't set errno
+ *
+ * @param s externally used socket index
+ * @return struct lwip_sock for the socket or NULL if not found
+ */
+static struct lwip_sock *
+tryget_socket(int s)
+{
+ if ((s < 0) || (s >= NUM_SOCKETS)) {
+ return NULL;
+ }
+ if (!sockets[s].conn) {
+ return NULL;
+ }
+ return &sockets[s];
+}
+
+/**
+ * Allocate a new socket for a given netconn.
+ *
+ * @param newconn the netconn for which to allocate a socket
+ * @param accepted 1 if socket has been created by accept(),
+ * 0 if socket has been created by socket()
+ * @return the index of the new socket; -1 on error
+ */
+static int
+alloc_socket(struct netconn *newconn, int accepted)
+{
+ int i;
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ /* allocate a new socket identifier */
+ for (i = 0; i < NUM_SOCKETS; ++i) {
+ /* Protect socket array */
+ SYS_ARCH_PROTECT(lev);
+ if (!sockets[i].conn) {
+ sockets[i].conn = newconn;
+ /* The socket is not yet known to anyone, so no need to protect
+ after having marked it as used. */
+ SYS_ARCH_UNPROTECT(lev);
+ sockets[i].lastdata = NULL;
+ sockets[i].lastoffset = 0;
+ sockets[i].rcvevent = 0;
+ /* TCP sendbuf is empty, but the socket is not yet writable until connected
+ * (unless it has been created by accept()). */
+ sockets[i].sendevent = (newconn->type == NETCONN_TCP ? (accepted != 0) : 1);
+ sockets[i].errevent = 0;
+ sockets[i].err = 0;
+ sockets[i].select_waiting = 0;
+ return i;
+ }
+ SYS_ARCH_UNPROTECT(lev);
+ }
+ return -1;
+}
+
+/** Free a socket. The socket's netconn must have been
+ * delete before!
+ *
+ * @param sock the socket to free
+ * @param is_tcp != 0 for TCP sockets, used to free lastdata
+ */
+static void
+free_socket(struct lwip_sock *sock, int is_tcp)
+{
+ void *lastdata;
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ lastdata = sock->lastdata;
+ sock->lastdata = NULL;
+ sock->lastoffset = 0;
+ sock->err = 0;
+
+ /* Protect socket array */
+ SYS_ARCH_PROTECT(lev);
+ sock->conn = NULL;
+ SYS_ARCH_UNPROTECT(lev);
+ /* don't use 'sock' after this line, as another task might have allocated it */
+
+ if (lastdata != NULL) {
+ if (is_tcp) {
+ pbuf_free((struct pbuf *)lastdata);
+ } else {
+ netbuf_delete((struct netbuf *)lastdata);
+ }
+ }
+}
+
+/* Below this, the well-known socket functions are implemented.
+ * Use google.com or opengroup.org to get a good description :-)
+ *
+ * Exceptions are documented!
+ */
+
+int
+lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen)
+{
+ struct lwip_sock *sock, *nsock;
+ struct netconn *newconn;
+ ip_addr_t naddr;
+ u16_t port;
+ int newsock;
+ struct sockaddr_in sin;
+ err_t err;
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d)...\n", s));
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ if (netconn_is_nonblocking(sock->conn) && (sock->rcvevent <= 0)) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): returning EWOULDBLOCK\n", s));
+ sock_set_errno(sock, EWOULDBLOCK);
+ return -1;
+ }
+
+ /* wait for a new connection */
+ err = netconn_accept(sock->conn, &newconn);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_acept failed, err=%d\n", s, err));
+ sock_set_errno(sock, err_to_errno(err));
+ return -1;
+ }
+ LWIP_ASSERT("newconn != NULL", newconn != NULL);
+ /* Prevent automatic window updates, we do this on our own! */
+ netconn_set_noautorecved(newconn, 1);
+
+ /* get the IP address and port of the remote host */
+ err = netconn_peer(newconn, &naddr, &port);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_peer failed, err=%d\n", s, err));
+ netconn_delete(newconn);
+ sock_set_errno(sock, err_to_errno(err));
+ return -1;
+ }
+
+ /* Note that POSIX only requires us to check addr is non-NULL. addrlen must
+ * not be NULL if addr is valid.
+ */
+ if (NULL != addr) {
+ LWIP_ASSERT("addr valid but addrlen NULL", addrlen != NULL);
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_len = sizeof(sin);
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(port);
+ inet_addr_from_ipaddr(&sin.sin_addr, &naddr);
+
+ if (*addrlen > sizeof(sin))
+ *addrlen = sizeof(sin);
+
+ MEMCPY(addr, &sin, *addrlen);
+ }
+
+ newsock = alloc_socket(newconn, 1);
+ if (newsock == -1) {
+ netconn_delete(newconn);
+ sock_set_errno(sock, ENFILE);
+ return -1;
+ }
+ LWIP_ASSERT("invalid socket index", (newsock >= 0) && (newsock < NUM_SOCKETS));
+ LWIP_ASSERT("newconn->callback == event_callback", newconn->callback == event_callback);
+ nsock = &sockets[newsock];
+
+ /* See event_callback: If data comes in right away after an accept, even
+ * though the server task might not have created a new socket yet.
+ * In that case, newconn->socket is counted down (newconn->socket--),
+ * so nsock->rcvevent is >= 1 here!
+ */
+ SYS_ARCH_PROTECT(lev);
+ nsock->rcvevent += (s16_t)(-1 - newconn->socket);
+ newconn->socket = newsock;
+ SYS_ARCH_UNPROTECT(lev);
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) returning new sock=%d addr=", s, newsock));
+ ip_addr_debug_print(SOCKETS_DEBUG, &naddr);
+ LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", port));
+
+ sock_set_errno(sock, 0);
+ return newsock;
+}
+
+int
+lwip_bind(int s, const struct sockaddr *name, socklen_t namelen)
+{
+ struct lwip_sock *sock;
+ ip_addr_t local_addr;
+ u16_t local_port;
+ err_t err;
+ const struct sockaddr_in *name_in;
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ /* check size, familiy and alignment of 'name' */
+ LWIP_ERROR("lwip_bind: invalid address", ((namelen == sizeof(struct sockaddr_in)) &&
+ ((name->sa_family) == AF_INET) && ((((mem_ptr_t)name) % 4) == 0)),
+ sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
+ name_in = (const struct sockaddr_in *)(void*)name;
+
+ inet_addr_to_ipaddr(&local_addr, &name_in->sin_addr);
+ local_port = name_in->sin_port;
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d, addr=", s));
+ ip_addr_debug_print(SOCKETS_DEBUG, &local_addr);
+ LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", ntohs(local_port)));
+
+ err = netconn_bind(sock->conn, &local_addr, ntohs(local_port));
+
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) failed, err=%d\n", s, err));
+ sock_set_errno(sock, err_to_errno(err));
+ return -1;
+ }
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) succeeded\n", s));
+ sock_set_errno(sock, 0);
+ return 0;
+}
+
+int
+lwip_close(int s)
+{
+ struct lwip_sock *sock;
+ int is_tcp = 0;
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_close(%d)\n", s));
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ if(sock->conn != NULL) {
+ is_tcp = netconn_type(sock->conn) == NETCONN_TCP;
+ } else {
+ LWIP_ASSERT("sock->lastdata == NULL", sock->lastdata == NULL);
+ }
+
+ netconn_delete(sock->conn);
+
+ free_socket(sock, is_tcp);
+ set_errno(0);
+ return 0;
+}
+
+int
+lwip_connect(int s, const struct sockaddr *name, socklen_t namelen)
+{
+ struct lwip_sock *sock;
+ err_t err;
+ const struct sockaddr_in *name_in;
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ /* check size, familiy and alignment of 'name' */
+ LWIP_ERROR("lwip_connect: invalid address", ((namelen == sizeof(struct sockaddr_in)) &&
+ ((name->sa_family) == AF_INET) && ((((mem_ptr_t)name) % 4) == 0)),
+ sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
+ name_in = (const struct sockaddr_in *)(void*)name;
+
+ if (name_in->sin_family == AF_UNSPEC) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, AF_UNSPEC)\n", s));
+ err = netconn_disconnect(sock->conn);
+ } else {
+ ip_addr_t remote_addr;
+ u16_t remote_port;
+
+ inet_addr_to_ipaddr(&remote_addr, &name_in->sin_addr);
+ remote_port = name_in->sin_port;
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, addr=", s));
+ ip_addr_debug_print(SOCKETS_DEBUG, &remote_addr);
+ LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", ntohs(remote_port)));
+
+ err = netconn_connect(sock->conn, &remote_addr, ntohs(remote_port));
+ }
+
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) failed, err=%d\n", s, err));
+ sock_set_errno(sock, err_to_errno(err));
+ return -1;
+ }
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) succeeded\n", s));
+ sock_set_errno(sock, 0);
+ return 0;
+}
+
+/**
+ * Set a socket into listen mode.
+ * The socket may not have been used for another connection previously.
+ *
+ * @param s the socket to set to listening mode
+ * @param backlog (ATTENTION: needs TCP_LISTEN_BACKLOG=1)
+ * @return 0 on success, non-zero on failure
+ */
+int
+lwip_listen(int s, int backlog)
+{
+ struct lwip_sock *sock;
+ err_t err;
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d, backlog=%d)\n", s, backlog));
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ /* limit the "backlog" parameter to fit in an u8_t */
+ backlog = LWIP_MIN(LWIP_MAX(backlog, 0), 0xff);
+
+ err = netconn_listen_with_backlog(sock->conn, (u8_t)backlog);
+
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d) failed, err=%d\n", s, err));
+ sock_set_errno(sock, err_to_errno(err));
+ return -1;
+ }
+
+ sock_set_errno(sock, 0);
+ return 0;
+}
+
+int
+lwip_recvfrom(int s, void *mem, size_t len, int flags,
+ struct sockaddr *from, socklen_t *fromlen)
+{
+ struct lwip_sock *sock;
+ void *buf = NULL;
+ struct pbuf *p;
+ u16_t buflen, copylen;
+ int off = 0;
+ ip_addr_t *addr;
+ u16_t port;
+ u8_t done = 0;
+ err_t err;
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d, %p, %"SZT_F", 0x%x, ..)\n", s, mem, len, flags));
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ do {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: top while sock->lastdata=%p\n", sock->lastdata));
+ /* Check if there is data left from the last recv operation. */
+ if (sock->lastdata) {
+ buf = sock->lastdata;
+ } else {
+ /* If this is non-blocking call, then check first */
+ if (((flags & MSG_DONTWAIT) || netconn_is_nonblocking(sock->conn)) &&
+ (sock->rcvevent <= 0)) {
+ if (off > 0) {
+ /* update receive window */
+ netconn_recved(sock->conn, (u32_t)off);
+ /* already received data, return that */
+ sock_set_errno(sock, 0);
+ return off;
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): returning EWOULDBLOCK\n", s));
+ sock_set_errno(sock, EWOULDBLOCK);
+ return -1;
+ }
+
+ /* No data was left from the previous operation, so we try to get
+ some from the network. */
+ if (netconn_type(sock->conn) == NETCONN_TCP) {
+ err = netconn_recv_tcp_pbuf(sock->conn, (struct pbuf **)&buf);
+ } else {
+ err = netconn_recv(sock->conn, (struct netbuf **)&buf);
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: netconn_recv err=%d, netbuf=%p\n",
+ err, buf));
+
+ if (err != ERR_OK) {
+ if (off > 0) {
+ /* update receive window */
+ netconn_recved(sock->conn, (u32_t)off);
+ /* already received data, return that */
+ sock_set_errno(sock, 0);
+ return off;
+ }
+ /* We should really do some error checking here. */
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): buf == NULL, error is \"%s\"!\n",
+ s, lwip_strerr(err)));
+ sock_set_errno(sock, err_to_errno(err));
+ if (err == ERR_CLSD) {
+ return 0;
+ } else {
+ return -1;
+ }
+ }
+ LWIP_ASSERT("buf != NULL", buf != NULL);
+ sock->lastdata = buf;
+ }
+
+ if (netconn_type(sock->conn) == NETCONN_TCP) {
+ p = (struct pbuf *)buf;
+ } else {
+ p = ((struct netbuf *)buf)->p;
+ }
+ buflen = p->tot_len;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: buflen=%"U16_F" len=%"SZT_F" off=%d sock->lastoffset=%"U16_F"\n",
+ buflen, len, off, sock->lastoffset));
+
+ buflen -= sock->lastoffset;
+
+ if (len > buflen) {
+ copylen = buflen;
+ } else {
+ copylen = (u16_t)len;
+ }
+
+ /* copy the contents of the received buffer into
+ the supplied memory pointer mem */
+ pbuf_copy_partial(p, (u8_t*)mem + off, copylen, sock->lastoffset);
+
+ off += copylen;
+
+ if (netconn_type(sock->conn) == NETCONN_TCP) {
+ LWIP_ASSERT("invalid copylen, len would underflow", len >= copylen);
+ len -= copylen;
+ if ( (len <= 0) ||
+ (p->flags & PBUF_FLAG_PUSH) ||
+ (sock->rcvevent <= 0) ||
+ ((flags & MSG_PEEK)!=0)) {
+ done = 1;
+ }
+ } else {
+ done = 1;
+ }
+
+ /* Check to see from where the data was.*/
+ if (done) {
+ ip_addr_t fromaddr;
+ if (from && fromlen) {
+ struct sockaddr_in sin;
+
+ if (netconn_type(sock->conn) == NETCONN_TCP) {
+ addr = &fromaddr;
+ netconn_getaddr(sock->conn, addr, &port, 0);
+ } else {
+ addr = netbuf_fromaddr((struct netbuf *)buf);
+ port = netbuf_fromport((struct netbuf *)buf);
+ }
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_len = sizeof(sin);
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(port);
+ inet_addr_from_ipaddr(&sin.sin_addr, addr);
+
+ if (*fromlen > sizeof(sin)) {
+ *fromlen = sizeof(sin);
+ }
+
+ MEMCPY(from, &sin, *fromlen);
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): addr=", s));
+ ip_addr_debug_print(SOCKETS_DEBUG, addr);
+ LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", port, off));
+ } else {
+#if SOCKETS_DEBUG
+ if (netconn_type(sock->conn) == NETCONN_TCP) {
+ addr = &fromaddr;
+ netconn_getaddr(sock->conn, addr, &port, 0);
+ } else {
+ addr = netbuf_fromaddr((struct netbuf *)buf);
+ port = netbuf_fromport((struct netbuf *)buf);
+ }
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): addr=", s));
+ ip_addr_debug_print(SOCKETS_DEBUG, addr);
+ LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", port, off));
+#endif /* SOCKETS_DEBUG */
+ }
+ }
+
+ /* If we don't peek the incoming message... */
+ if ((flags & MSG_PEEK) == 0) {
+ /* If this is a TCP socket, check if there is data left in the
+ buffer. If so, it should be saved in the sock structure for next
+ time around. */
+ if ((netconn_type(sock->conn) == NETCONN_TCP) && (buflen - copylen > 0)) {
+ sock->lastdata = buf;
+ sock->lastoffset += copylen;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: lastdata now netbuf=%p\n", buf));
+ } else {
+ sock->lastdata = NULL;
+ sock->lastoffset = 0;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: deleting netbuf=%p\n", buf));
+ if (netconn_type(sock->conn) == NETCONN_TCP) {
+ pbuf_free((struct pbuf *)buf);
+ } else {
+ netbuf_delete((struct netbuf *)buf);
+ }
+ }
+ }
+ } while (!done);
+
+ if (off > 0) {
+ /* update receive window */
+ netconn_recved(sock->conn, (u32_t)off);
+ }
+ sock_set_errno(sock, 0);
+ return off;
+}
+
+int
+lwip_read(int s, void *mem, size_t len)
+{
+ return lwip_recvfrom(s, mem, len, 0, NULL, NULL);
+}
+
+int
+lwip_recv(int s, void *mem, size_t len, int flags)
+{
+ return lwip_recvfrom(s, mem, len, flags, NULL, NULL);
+}
+
+int
+lwip_send(int s, const void *data, size_t size, int flags)
+{
+ struct lwip_sock *sock;
+ err_t err;
+ u8_t write_flags;
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d, data=%p, size=%"SZT_F", flags=0x%x)\n",
+ s, data, size, flags));
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ if (sock->conn->type != NETCONN_TCP) {
+#if (LWIP_UDP || LWIP_RAW)
+ return lwip_sendto(s, data, size, flags, NULL, 0);
+#else /* (LWIP_UDP || LWIP_RAW) */
+ sock_set_errno(sock, err_to_errno(ERR_ARG));
+ return -1;
+#endif /* (LWIP_UDP || LWIP_RAW) */
+ }
+
+ if ((flags & MSG_DONTWAIT) || netconn_is_nonblocking(sock->conn)) {
+ if ((size > TCP_SND_BUF) || ((size / TCP_MSS) > TCP_SND_QUEUELEN)) {
+ /* too much data to ever send nonblocking! */
+ sock_set_errno(sock, EMSGSIZE);
+ return -1;
+ }
+ }
+
+ write_flags = NETCONN_COPY |
+ ((flags & MSG_MORE) ? NETCONN_MORE : 0) |
+ ((flags & MSG_DONTWAIT) ? NETCONN_DONTBLOCK : 0);
+ err = netconn_write(sock->conn, data, size, write_flags);
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d) err=%d size=%"SZT_F"\n", s, err, size));
+ sock_set_errno(sock, err_to_errno(err));
+ return (err == ERR_OK ? (int)size : -1);
+}
+
+int
+lwip_sendto(int s, const void *data, size_t size, int flags,
+ const struct sockaddr *to, socklen_t tolen)
+{
+ struct lwip_sock *sock;
+ err_t err;
+ u16_t short_size;
+ const struct sockaddr_in *to_in;
+ u16_t remote_port;
+#if !LWIP_TCPIP_CORE_LOCKING
+ struct netbuf buf;
+#endif
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ if (sock->conn->type == NETCONN_TCP) {
+#if LWIP_TCP
+ return lwip_send(s, data, size, flags);
+#else /* LWIP_TCP */
+ LWIP_UNUSED_ARG(flags);
+ sock_set_errno(sock, err_to_errno(ERR_ARG));
+ return -1;
+#endif /* LWIP_TCP */
+ }
+
+ /* @todo: split into multiple sendto's? */
+ LWIP_ASSERT("lwip_sendto: size must fit in u16_t", size <= 0xffff);
+ short_size = (u16_t)size;
+ LWIP_ERROR("lwip_sendto: invalid address", (((to == NULL) && (tolen == 0)) ||
+ ((tolen == sizeof(struct sockaddr_in)) &&
+ ((to->sa_family) == AF_INET) && ((((mem_ptr_t)to) % 4) == 0))),
+ sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
+ to_in = (const struct sockaddr_in *)(void*)to;
+
+#if LWIP_TCPIP_CORE_LOCKING
+ /* Should only be consider like a sample or a simple way to experiment this option (no check of "to" field...) */
+ {
+ struct pbuf* p;
+ ip_addr_t *remote_addr;
+
+#if LWIP_NETIF_TX_SINGLE_PBUF
+ p = pbuf_alloc(PBUF_TRANSPORT, short_size, PBUF_RAM);
+ if (p != NULL) {
+#if LWIP_CHECKSUM_ON_COPY
+ u16_t chksum = 0;
+ if (sock->conn->type != NETCONN_RAW) {
+ chksum = LWIP_CHKSUM_COPY(p->payload, data, short_size);
+ } else
+#endif /* LWIP_CHECKSUM_ON_COPY */
+ MEMCPY(p->payload, data, size);
+#else /* LWIP_NETIF_TX_SINGLE_PBUF */
+ p = pbuf_alloc(PBUF_TRANSPORT, short_size, PBUF_REF);
+ if (p != NULL) {
+ p->payload = (void*)data;
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+
+ if (to_in != NULL) {
+ inet_addr_to_ipaddr_p(remote_addr, &to_in->sin_addr);
+ remote_port = ntohs(to_in->sin_port);
+ } else {
+ remote_addr = IP_ADDR_ANY;
+ remote_port = 0;
+ }
+
+ LOCK_TCPIP_CORE();
+ if (sock->conn->type == NETCONN_RAW) {
+ err = sock->conn->last_err = raw_sendto(sock->conn->pcb.raw, p, remote_addr);
+ } else {
+#if LWIP_UDP
+#if LWIP_CHECKSUM_ON_COPY && LWIP_NETIF_TX_SINGLE_PBUF
+ err = sock->conn->last_err = udp_sendto_chksum(sock->conn->pcb.udp, p,
+ remote_addr, remote_port, 1, chksum);
+#else /* LWIP_CHECKSUM_ON_COPY && LWIP_NETIF_TX_SINGLE_PBUF */
+ err = sock->conn->last_err = udp_sendto(sock->conn->pcb.udp, p,
+ remote_addr, remote_port);
+#endif /* LWIP_CHECKSUM_ON_COPY && LWIP_NETIF_TX_SINGLE_PBUF */
+#else /* LWIP_UDP */
+ err = ERR_ARG;
+#endif /* LWIP_UDP */
+ }
+ UNLOCK_TCPIP_CORE();
+
+ pbuf_free(p);
+ } else {
+ err = ERR_MEM;
+ }
+ }
+#else /* LWIP_TCPIP_CORE_LOCKING */
+ /* initialize a buffer */
+ buf.p = buf.ptr = NULL;
+#if LWIP_CHECKSUM_ON_COPY
+ buf.flags = 0;
+#endif /* LWIP_CHECKSUM_ON_COPY */
+ if (to) {
+ inet_addr_to_ipaddr(&buf.addr, &to_in->sin_addr);
+ remote_port = ntohs(to_in->sin_port);
+ netbuf_fromport(&buf) = remote_port;
+ } else {
+ remote_port = 0;
+ ip_addr_set_any(&buf.addr);
+ netbuf_fromport(&buf) = 0;
+ }
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_sendto(%d, data=%p, short_size=%d"U16_F", flags=0x%x to=",
+ s, data, short_size, flags));
+ ip_addr_debug_print(SOCKETS_DEBUG, &buf.addr);
+ LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", remote_port));
+
+ /* make the buffer point to the data that should be sent */
+#if LWIP_NETIF_TX_SINGLE_PBUF
+ /* Allocate a new netbuf and copy the data into it. */
+ if (netbuf_alloc(&buf, short_size) == NULL) {
+ err = ERR_MEM;
+ } else {
+#if LWIP_CHECKSUM_ON_COPY
+ if (sock->conn->type != NETCONN_RAW) {
+ u16_t chksum = LWIP_CHKSUM_COPY(buf.p->payload, data, short_size);
+ netbuf_set_chksum(&buf, chksum);
+ err = ERR_OK;
+ } else
+#endif /* LWIP_CHECKSUM_ON_COPY */
+ {
+ err = netbuf_take(&buf, data, short_size);
+ }
+ }
+#else /* LWIP_NETIF_TX_SINGLE_PBUF */
+ err = netbuf_ref(&buf, data, short_size);
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+ if (err == ERR_OK) {
+ /* send the data */
+ err = netconn_send(sock->conn, &buf);
+ }
+
+ /* deallocated the buffer */
+ netbuf_free(&buf);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+ sock_set_errno(sock, err_to_errno(err));
+ return (err == ERR_OK ? short_size : -1);
+}
+
+int
+lwip_socket(int domain, int type, int protocol)
+{
+ struct netconn *conn;
+ int i;
+
+ LWIP_UNUSED_ARG(domain);
+
+ /* create a netconn */
+ switch (type) {
+ case SOCK_RAW:
+ conn = netconn_new_with_proto_and_callback(NETCONN_RAW, (u8_t)protocol, event_callback);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_RAW, %d) = ",
+ domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
+ break;
+ case SOCK_DGRAM:
+ conn = netconn_new_with_callback( (protocol == IPPROTO_UDPLITE) ?
+ NETCONN_UDPLITE : NETCONN_UDP, event_callback);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_DGRAM, %d) = ",
+ domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
+ break;
+ case SOCK_STREAM:
+ conn = netconn_new_with_callback(NETCONN_TCP, event_callback);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_STREAM, %d) = ",
+ domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
+ if (conn != NULL) {
+ /* Prevent automatic window updates, we do this on our own! */
+ netconn_set_noautorecved(conn, 1);
+ }
+ break;
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%d, %d/UNKNOWN, %d) = -1\n",
+ domain, type, protocol));
+ set_errno(EINVAL);
+ return -1;
+ }
+
+ if (!conn) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("-1 / ENOBUFS (could not create netconn)\n"));
+ set_errno(ENOBUFS);
+ return -1;
+ }
+
+ i = alloc_socket(conn, 0);
+
+ if (i == -1) {
+ netconn_delete(conn);
+ set_errno(ENFILE);
+ return -1;
+ }
+ conn->socket = i;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("%d\n", i));
+ set_errno(0);
+ return i;
+}
+
+int
+lwip_write(int s, const void *data, size_t size)
+{
+ return lwip_send(s, data, size, 0);
+}
+
+/**
+ * Go through the readset and writeset lists and see which socket of the sockets
+ * set in the sets has events. On return, readset, writeset and exceptset have
+ * the sockets enabled that had events.
+ *
+ * exceptset is not used for now!!!
+ *
+ * @param maxfdp1 the highest socket index in the sets
+ * @param readset_in: set of sockets to check for read events
+ * @param writeset_in: set of sockets to check for write events
+ * @param exceptset_in: set of sockets to check for error events
+ * @param readset_out: set of sockets that had read events
+ * @param writeset_out: set of sockets that had write events
+ * @param exceptset_out: set os sockets that had error events
+ * @return number of sockets that had events (read/write/exception) (>= 0)
+ */
+static int
+lwip_selscan(int maxfdp1, fd_set *readset_in, fd_set *writeset_in, fd_set *exceptset_in,
+ fd_set *readset_out, fd_set *writeset_out, fd_set *exceptset_out)
+{
+ int i, nready = 0;
+ fd_set lreadset, lwriteset, lexceptset;
+ struct lwip_sock *sock;
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ FD_ZERO(&lreadset);
+ FD_ZERO(&lwriteset);
+ FD_ZERO(&lexceptset);
+
+ /* Go through each socket in each list to count number of sockets which
+ currently match */
+ for(i = 0; i < maxfdp1; i++) {
+ void* lastdata = NULL;
+ s16_t rcvevent = 0;
+ u16_t sendevent = 0;
+ u16_t errevent = 0;
+ /* First get the socket's status (protected)... */
+ SYS_ARCH_PROTECT(lev);
+ sock = tryget_socket(i);
+ if (sock != NULL) {
+ lastdata = sock->lastdata;
+ rcvevent = sock->rcvevent;
+ sendevent = sock->sendevent;
+ errevent = sock->errevent;
+ }
+ SYS_ARCH_UNPROTECT(lev);
+ /* ... then examine it: */
+ /* See if netconn of this socket is ready for read */
+ if (readset_in && FD_ISSET(i, readset_in) && ((lastdata != NULL) || (rcvevent > 0))) {
+ FD_SET(i, &lreadset);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for reading\n", i));
+ nready++;
+ }
+ /* See if netconn of this socket is ready for write */
+ if (writeset_in && FD_ISSET(i, writeset_in) && (sendevent != 0)) {
+ FD_SET(i, &lwriteset);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for writing\n", i));
+ nready++;
+ }
+ /* See if netconn of this socket had an error */
+ if (exceptset_in && FD_ISSET(i, exceptset_in) && (errevent != 0)) {
+ FD_SET(i, &lexceptset);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for exception\n", i));
+ nready++;
+ }
+ }
+ /* copy local sets to the ones provided as arguments */
+ *readset_out = lreadset;
+ *writeset_out = lwriteset;
+ *exceptset_out = lexceptset;
+
+ LWIP_ASSERT("nready >= 0", nready >= 0);
+ return nready;
+}
+
+/**
+ * Processing exceptset is not yet implemented.
+ */
+int
+lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
+ struct timeval *timeout)
+{
+ u32_t waitres = 0;
+ int nready;
+ fd_set lreadset, lwriteset, lexceptset;
+ u32_t msectimeout;
+ struct lwip_select_cb select_cb;
+ err_t err;
+ int i;
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select(%d, %p, %p, %p, tvsec=%"S32_F" tvusec=%"S32_F")\n",
+ maxfdp1, (void *)readset, (void *) writeset, (void *) exceptset,
+ timeout ? (s32_t)timeout->tv_sec : (s32_t)-1,
+ timeout ? (s32_t)timeout->tv_usec : (s32_t)-1));
+
+ /* Go through each socket in each list to count number of sockets which
+ currently match */
+ nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
+
+ /* If we don't have any current events, then suspend if we are supposed to */
+ if (!nready) {
+ if (timeout && timeout->tv_sec == 0 && timeout->tv_usec == 0) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: no timeout, returning 0\n"));
+ /* This is OK as the local fdsets are empty and nready is zero,
+ or we would have returned earlier. */
+ goto return_copy_fdsets;
+ }
+
+ /* None ready: add our semaphore to list:
+ We don't actually need any dynamic memory. Our entry on the
+ list is only valid while we are in this function, so it's ok
+ to use local variables. */
+
+ select_cb.next = NULL;
+ select_cb.prev = NULL;
+ select_cb.readset = readset;
+ select_cb.writeset = writeset;
+ select_cb.exceptset = exceptset;
+ select_cb.sem_signalled = 0;
+ err = sys_sem_new(&select_cb.sem, 0);
+ if (err != ERR_OK) {
+ /* failed to create semaphore */
+ set_errno(ENOMEM);
+ return -1;
+ }
+
+ /* Protect the select_cb_list */
+ SYS_ARCH_PROTECT(lev);
+
+ /* Put this select_cb on top of list */
+ select_cb.next = select_cb_list;
+ if (select_cb_list != NULL) {
+ select_cb_list->prev = &select_cb;
+ }
+ select_cb_list = &select_cb;
+ /* Increasing this counter tells even_callback that the list has changed. */
+ select_cb_ctr++;
+
+ /* Now we can safely unprotect */
+ SYS_ARCH_UNPROTECT(lev);
+
+ /* Increase select_waiting for each socket we are interested in */
+ for(i = 0; i < maxfdp1; i++) {
+ if ((readset && FD_ISSET(i, readset)) ||
+ (writeset && FD_ISSET(i, writeset)) ||
+ (exceptset && FD_ISSET(i, exceptset))) {
+ struct lwip_sock *sock = tryget_socket(i);
+ LWIP_ASSERT("sock != NULL", sock != NULL);
+ SYS_ARCH_PROTECT(lev);
+ sock->select_waiting++;
+ LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0);
+ SYS_ARCH_UNPROTECT(lev);
+ }
+ }
+
+ /* Call lwip_selscan again: there could have been events between
+ the last scan (whithout us on the list) and putting us on the list! */
+ nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
+ if (!nready) {
+ /* Still none ready, just wait to be woken */
+ if (timeout == 0) {
+ /* Wait forever */
+ msectimeout = 0;
+ } else {
+ msectimeout = ((timeout->tv_sec * 1000) + ((timeout->tv_usec + 500)/1000));
+ if (msectimeout == 0) {
+ /* Wait 1ms at least (0 means wait forever) */
+ msectimeout = 1;
+ }
+ }
+
+ waitres = sys_arch_sem_wait(&select_cb.sem, msectimeout);
+ }
+ /* Increase select_waiting for each socket we are interested in */
+ for(i = 0; i < maxfdp1; i++) {
+ if ((readset && FD_ISSET(i, readset)) ||
+ (writeset && FD_ISSET(i, writeset)) ||
+ (exceptset && FD_ISSET(i, exceptset))) {
+ struct lwip_sock *sock = tryget_socket(i);
+ LWIP_ASSERT("sock != NULL", sock != NULL);
+ SYS_ARCH_PROTECT(lev);
+ sock->select_waiting--;
+ LWIP_ASSERT("sock->select_waiting >= 0", sock->select_waiting >= 0);
+ SYS_ARCH_UNPROTECT(lev);
+ }
+ }
+ /* Take us off the list */
+ SYS_ARCH_PROTECT(lev);
+ if (select_cb.next != NULL) {
+ select_cb.next->prev = select_cb.prev;
+ }
+ if (select_cb_list == &select_cb) {
+ LWIP_ASSERT("select_cb.prev == NULL", select_cb.prev == NULL);
+ select_cb_list = select_cb.next;
+ } else {
+ LWIP_ASSERT("select_cb.prev != NULL", select_cb.prev != NULL);
+ select_cb.prev->next = select_cb.next;
+ }
+ /* Increasing this counter tells even_callback that the list has changed. */
+ select_cb_ctr++;
+ SYS_ARCH_UNPROTECT(lev);
+
+ sys_sem_free(&select_cb.sem);
+ if (waitres == SYS_ARCH_TIMEOUT) {
+ /* Timeout */
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: timeout expired\n"));
+ /* This is OK as the local fdsets are empty and nready is zero,
+ or we would have returned earlier. */
+ goto return_copy_fdsets;
+ }
+
+ /* See what's set */
+ nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
+ }
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: nready=%d\n", nready));
+return_copy_fdsets:
+ set_errno(0);
+ if (readset) {
+ *readset = lreadset;
+ }
+ if (writeset) {
+ *writeset = lwriteset;
+ }
+ if (exceptset) {
+ *exceptset = lexceptset;
+ }
+
+
+ return nready;
+}
+
+/**
+ * Callback registered in the netconn layer for each socket-netconn.
+ * Processes recvevent (data available) and wakes up tasks waiting for select.
+ */
+static void
+event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len)
+{
+ int s;
+ struct lwip_sock *sock;
+ struct lwip_select_cb *scb;
+ int last_select_cb_ctr;
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ LWIP_UNUSED_ARG(len);
+
+ /* Get socket */
+ if (conn) {
+ s = conn->socket;
+ if (s < 0) {
+ /* Data comes in right away after an accept, even though
+ * the server task might not have created a new socket yet.
+ * Just count down (or up) if that's the case and we
+ * will use the data later. Note that only receive events
+ * can happen before the new socket is set up. */
+ SYS_ARCH_PROTECT(lev);
+ if (conn->socket < 0) {
+ if (evt == NETCONN_EVT_RCVPLUS) {
+ conn->socket--;
+ }
+ SYS_ARCH_UNPROTECT(lev);
+ return;
+ }
+ s = conn->socket;
+ SYS_ARCH_UNPROTECT(lev);
+ }
+
+ sock = get_socket(s);
+ if (!sock) {
+ return;
+ }
+ } else {
+ return;
+ }
+
+ SYS_ARCH_PROTECT(lev);
+ /* Set event as required */
+ switch (evt) {
+ case NETCONN_EVT_RCVPLUS:
+ sock->rcvevent++;
+ break;
+ case NETCONN_EVT_RCVMINUS:
+ sock->rcvevent--;
+ break;
+ case NETCONN_EVT_SENDPLUS:
+ sock->sendevent = 1;
+ break;
+ case NETCONN_EVT_SENDMINUS:
+ sock->sendevent = 0;
+ break;
+ case NETCONN_EVT_ERROR:
+ sock->errevent = 1;
+ break;
+ default:
+ LWIP_ASSERT("unknown event", 0);
+ break;
+ }
+
+ if (sock->select_waiting == 0) {
+ /* noone is waiting for this socket, no need to check select_cb_list */
+ SYS_ARCH_UNPROTECT(lev);
+ return;
+ }
+
+ /* Now decide if anyone is waiting for this socket */
+ /* NOTE: This code goes through the select_cb_list list multiple times
+ ONLY IF a select was actually waiting. We go through the list the number
+ of waiting select calls + 1. This list is expected to be small. */
+
+ /* At this point, SYS_ARCH is still protected! */
+again:
+ for (scb = select_cb_list; scb != NULL; scb = scb->next) {
+ if (scb->sem_signalled == 0) {
+ /* semaphore not signalled yet */
+ int do_signal = 0;
+ /* Test this select call for our socket */
+ if (sock->rcvevent > 0) {
+ if (scb->readset && FD_ISSET(s, scb->readset)) {
+ do_signal = 1;
+ }
+ }
+ if (sock->sendevent != 0) {
+ if (!do_signal && scb->writeset && FD_ISSET(s, scb->writeset)) {
+ do_signal = 1;
+ }
+ }
+ if (sock->errevent != 0) {
+ if (!do_signal && scb->exceptset && FD_ISSET(s, scb->exceptset)) {
+ do_signal = 1;
+ }
+ }
+ if (do_signal) {
+ scb->sem_signalled = 1;
+ /* Don't call SYS_ARCH_UNPROTECT() before signaling the semaphore, as this might
+ lead to the select thread taking itself off the list, invalidagin the semaphore. */
+ sys_sem_signal(&scb->sem);
+ }
+ }
+ /* unlock interrupts with each step */
+ last_select_cb_ctr = select_cb_ctr;
+ SYS_ARCH_UNPROTECT(lev);
+ /* this makes sure interrupt protection time is short */
+ SYS_ARCH_PROTECT(lev);
+ if (last_select_cb_ctr != select_cb_ctr) {
+ /* someone has changed select_cb_list, restart at the beginning */
+ goto again;
+ }
+ }
+ SYS_ARCH_UNPROTECT(lev);
+}
+
+/**
+ * Unimplemented: Close one end of a full-duplex connection.
+ * Currently, the full connection is closed.
+ */
+int
+lwip_shutdown(int s, int how)
+{
+ struct lwip_sock *sock;
+ err_t err;
+ u8_t shut_rx = 0, shut_tx = 0;
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_shutdown(%d, how=%d)\n", s, how));
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ if (sock->conn != NULL) {
+ if (netconn_type(sock->conn) != NETCONN_TCP) {
+ sock_set_errno(sock, EOPNOTSUPP);
+ return EOPNOTSUPP;
+ }
+ } else {
+ sock_set_errno(sock, ENOTCONN);
+ return ENOTCONN;
+ }
+
+ if (how == SHUT_RD) {
+ shut_rx = 1;
+ } else if (how == SHUT_WR) {
+ shut_tx = 1;
+ } else if(how == SHUT_RDWR) {
+ shut_rx = 1;
+ shut_tx = 1;
+ } else {
+ sock_set_errno(sock, EINVAL);
+ return EINVAL;
+ }
+ err = netconn_shutdown(sock->conn, shut_rx, shut_tx);
+
+ sock_set_errno(sock, err_to_errno(err));
+ return (err == ERR_OK ? 0 : -1);
+}
+
+static int
+lwip_getaddrname(int s, struct sockaddr *name, socklen_t *namelen, u8_t local)
+{
+ struct lwip_sock *sock;
+ struct sockaddr_in sin;
+ ip_addr_t naddr;
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_len = sizeof(sin);
+ sin.sin_family = AF_INET;
+
+ /* get the IP address and port */
+ netconn_getaddr(sock->conn, &naddr, &sin.sin_port, local);
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getaddrname(%d, addr=", s));
+ ip_addr_debug_print(SOCKETS_DEBUG, &naddr);
+ LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", sin.sin_port));
+
+ sin.sin_port = htons(sin.sin_port);
+ inet_addr_from_ipaddr(&sin.sin_addr, &naddr);
+
+ if (*namelen > sizeof(sin)) {
+ *namelen = sizeof(sin);
+ }
+
+ MEMCPY(name, &sin, *namelen);
+ sock_set_errno(sock, 0);
+ return 0;
+}
+
+int
+lwip_getpeername(int s, struct sockaddr *name, socklen_t *namelen)
+{
+ return lwip_getaddrname(s, name, namelen, 0);
+}
+
+int
+lwip_getsockname(int s, struct sockaddr *name, socklen_t *namelen)
+{
+ return lwip_getaddrname(s, name, namelen, 1);
+}
+
+int
+lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen)
+{
+ err_t err = ERR_OK;
+ struct lwip_sock *sock = get_socket(s);
+ struct lwip_setgetsockopt_data data;
+
+ if (!sock) {
+ return -1;
+ }
+
+ if ((NULL == optval) || (NULL == optlen)) {
+ sock_set_errno(sock, EFAULT);
+ return -1;
+ }
+
+ /* Do length and type checks for the various options first, to keep it readable. */
+ switch (level) {
+
+/* Level: SOL_SOCKET */
+ case SOL_SOCKET:
+ switch (optname) {
+
+ case SO_ACCEPTCONN:
+ case SO_BROADCAST:
+ /* UNIMPL case SO_DEBUG: */
+ /* UNIMPL case SO_DONTROUTE: */
+ case SO_ERROR:
+ case SO_KEEPALIVE:
+ /* UNIMPL case SO_CONTIMEO: */
+ /* UNIMPL case SO_SNDTIMEO: */
+#if LWIP_SO_RCVTIMEO
+ case SO_RCVTIMEO:
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVBUF
+ case SO_RCVBUF:
+#endif /* LWIP_SO_RCVBUF */
+ /* UNIMPL case SO_OOBINLINE: */
+ /* UNIMPL case SO_SNDBUF: */
+ /* UNIMPL case SO_RCVLOWAT: */
+ /* UNIMPL case SO_SNDLOWAT: */
+#if SO_REUSE
+ case SO_REUSEADDR:
+ case SO_REUSEPORT:
+#endif /* SO_REUSE */
+ case SO_TYPE:
+ /* UNIMPL case SO_USELOOPBACK: */
+ if (*optlen < sizeof(int)) {
+ err = EINVAL;
+ }
+ break;
+
+ case SO_NO_CHECK:
+ if (*optlen < sizeof(int)) {
+ err = EINVAL;
+ }
+#if LWIP_UDP
+ if ((sock->conn->type != NETCONN_UDP) ||
+ ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0)) {
+ /* this flag is only available for UDP, not for UDP lite */
+ err = EAFNOSUPPORT;
+ }
+#endif /* LWIP_UDP */
+ break;
+
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ } /* switch (optname) */
+ break;
+
+/* Level: IPPROTO_IP */
+ case IPPROTO_IP:
+ switch (optname) {
+ /* UNIMPL case IP_HDRINCL: */
+ /* UNIMPL case IP_RCVDSTADDR: */
+ /* UNIMPL case IP_RCVIF: */
+ case IP_TTL:
+ case IP_TOS:
+ if (*optlen < sizeof(int)) {
+ err = EINVAL;
+ }
+ break;
+#if LWIP_IGMP
+ case IP_MULTICAST_TTL:
+ if (*optlen < sizeof(u8_t)) {
+ err = EINVAL;
+ }
+ break;
+ case IP_MULTICAST_IF:
+ if (*optlen < sizeof(struct in_addr)) {
+ err = EINVAL;
+ }
+ break;
+ case IP_MULTICAST_LOOP:
+ if (*optlen < sizeof(u8_t)) {
+ err = EINVAL;
+ }
+ if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP) {
+ err = EAFNOSUPPORT;
+ }
+ break;
+#endif /* LWIP_IGMP */
+
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ } /* switch (optname) */
+ break;
+
+#if LWIP_TCP
+/* Level: IPPROTO_TCP */
+ case IPPROTO_TCP:
+ if (*optlen < sizeof(int)) {
+ err = EINVAL;
+ break;
+ }
+
+ /* If this is no TCP socket, ignore any options. */
+ if (sock->conn->type != NETCONN_TCP)
+ return 0;
+
+ switch (optname) {
+ case TCP_NODELAY:
+ case TCP_KEEPALIVE:
+#if LWIP_TCP_KEEPALIVE
+ case TCP_KEEPIDLE:
+ case TCP_KEEPINTVL:
+ case TCP_KEEPCNT:
+#endif /* LWIP_TCP_KEEPALIVE */
+ break;
+
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_TCP */
+#if LWIP_UDP && LWIP_UDPLITE
+/* Level: IPPROTO_UDPLITE */
+ case IPPROTO_UDPLITE:
+ if (*optlen < sizeof(int)) {
+ err = EINVAL;
+ break;
+ }
+
+ /* If this is no UDP lite socket, ignore any options. */
+ if (sock->conn->type != NETCONN_UDPLITE) {
+ return 0;
+ }
+
+ switch (optname) {
+ case UDPLITE_SEND_CSCOV:
+ case UDPLITE_RECV_CSCOV:
+ break;
+
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_UDP && LWIP_UDPLITE*/
+/* UNDEFINED LEVEL */
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n",
+ s, level, optname));
+ err = ENOPROTOOPT;
+ } /* switch */
+
+
+ if (err != ERR_OK) {
+ sock_set_errno(sock, err);
+ return -1;
+ }
+
+ /* Now do the actual option processing */
+ data.sock = sock;
+#ifdef LWIP_DEBUG
+ data.s = s;
+#endif /* LWIP_DEBUG */
+ data.level = level;
+ data.optname = optname;
+ data.optval = optval;
+ data.optlen = optlen;
+ data.err = err;
+ tcpip_callback(lwip_getsockopt_internal, &data);
+ sys_arch_sem_wait(&sock->conn->op_completed, 0);
+ /* maybe lwip_getsockopt_internal has changed err */
+ err = data.err;
+
+ sock_set_errno(sock, err);
+ return err ? -1 : 0;
+}
+
+static void
+lwip_getsockopt_internal(void *arg)
+{
+ struct lwip_sock *sock;
+#ifdef LWIP_DEBUG
+ int s;
+#endif /* LWIP_DEBUG */
+ int level, optname;
+ void *optval;
+ struct lwip_setgetsockopt_data *data;
+
+ LWIP_ASSERT("arg != NULL", arg != NULL);
+
+ data = (struct lwip_setgetsockopt_data*)arg;
+ sock = data->sock;
+#ifdef LWIP_DEBUG
+ s = data->s;
+#endif /* LWIP_DEBUG */
+ level = data->level;
+ optname = data->optname;
+ optval = data->optval;
+
+ switch (level) {
+
+/* Level: SOL_SOCKET */
+ case SOL_SOCKET:
+ switch (optname) {
+
+ /* The option flags */
+ case SO_ACCEPTCONN:
+ case SO_BROADCAST:
+ /* UNIMPL case SO_DEBUG: */
+ /* UNIMPL case SO_DONTROUTE: */
+ case SO_KEEPALIVE:
+ /* UNIMPL case SO_OOBINCLUDE: */
+#if SO_REUSE
+ case SO_REUSEADDR:
+ case SO_REUSEPORT:
+#endif /* SO_REUSE */
+ /*case SO_USELOOPBACK: UNIMPL */
+ *(int*)optval = sock->conn->pcb.ip->so_options & optname;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, optname=0x%x, ..) = %s\n",
+ s, optname, (*(int*)optval?"on":"off")));
+ break;
+
+ case SO_TYPE:
+ switch (NETCONNTYPE_GROUP(sock->conn->type)) {
+ case NETCONN_RAW:
+ *(int*)optval = SOCK_RAW;
+ break;
+ case NETCONN_TCP:
+ *(int*)optval = SOCK_STREAM;
+ break;
+ case NETCONN_UDP:
+ *(int*)optval = SOCK_DGRAM;
+ break;
+ default: /* unrecognized socket type */
+ *(int*)optval = sock->conn->type;
+ LWIP_DEBUGF(SOCKETS_DEBUG,
+ ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE): unrecognized socket type %d\n",
+ s, *(int *)optval));
+ } /* switch (sock->conn->type) */
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE) = %d\n",
+ s, *(int *)optval));
+ break;
+
+ case SO_ERROR:
+ /* only overwrite ERR_OK or tempoary errors */
+ if ((sock->err == 0) || (sock->err == EINPROGRESS)) {
+ sock_set_errno(sock, err_to_errno(sock->conn->last_err));
+ }
+ *(int *)optval = sock->err;
+ sock->err = 0;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_ERROR) = %d\n",
+ s, *(int *)optval));
+ break;
+
+#if LWIP_SO_RCVTIMEO
+ case SO_RCVTIMEO:
+ *(int *)optval = netconn_get_recvtimeout(sock->conn);
+ break;
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVBUF
+ case SO_RCVBUF:
+ *(int *)optval = netconn_get_recvbufsize(sock->conn);
+ break;
+#endif /* LWIP_SO_RCVBUF */
+#if LWIP_UDP
+ case SO_NO_CHECK:
+ *(int*)optval = (udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_NOCHKSUM) ? 1 : 0;
+ break;
+#endif /* LWIP_UDP*/
+ default:
+ LWIP_ASSERT("unhandled optname", 0);
+ break;
+ } /* switch (optname) */
+ break;
+
+/* Level: IPPROTO_IP */
+ case IPPROTO_IP:
+ switch (optname) {
+ case IP_TTL:
+ *(int*)optval = sock->conn->pcb.ip->ttl;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TTL) = %d\n",
+ s, *(int *)optval));
+ break;
+ case IP_TOS:
+ *(int*)optval = sock->conn->pcb.ip->tos;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TOS) = %d\n",
+ s, *(int *)optval));
+ break;
+#if LWIP_IGMP
+ case IP_MULTICAST_TTL:
+ *(u8_t*)optval = sock->conn->pcb.ip->ttl;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_TTL) = %d\n",
+ s, *(int *)optval));
+ break;
+ case IP_MULTICAST_IF:
+ inet_addr_from_ipaddr((struct in_addr*)optval, &sock->conn->pcb.udp->multicast_ip);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_IF) = 0x%"X32_F"\n",
+ s, *(u32_t *)optval));
+ break;
+ case IP_MULTICAST_LOOP:
+ if ((sock->conn->pcb.udp->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) {
+ *(u8_t*)optval = 1;
+ } else {
+ *(u8_t*)optval = 0;
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_LOOP) = %d\n",
+ s, *(int *)optval));
+ break;
+#endif /* LWIP_IGMP */
+ default:
+ LWIP_ASSERT("unhandled optname", 0);
+ break;
+ } /* switch (optname) */
+ break;
+
+#if LWIP_TCP
+/* Level: IPPROTO_TCP */
+ case IPPROTO_TCP:
+ switch (optname) {
+ case TCP_NODELAY:
+ *(int*)optval = tcp_nagle_disabled(sock->conn->pcb.tcp);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_NODELAY) = %s\n",
+ s, (*(int*)optval)?"on":"off") );
+ break;
+ case TCP_KEEPALIVE:
+ *(int*)optval = (int)sock->conn->pcb.tcp->keep_idle;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPALIVE) = %d\n",
+ s, *(int *)optval));
+ break;
+
+#if LWIP_TCP_KEEPALIVE
+ case TCP_KEEPIDLE:
+ *(int*)optval = (int)(sock->conn->pcb.tcp->keep_idle/1000);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPIDLE) = %d\n",
+ s, *(int *)optval));
+ break;
+ case TCP_KEEPINTVL:
+ *(int*)optval = (int)(sock->conn->pcb.tcp->keep_intvl/1000);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPINTVL) = %d\n",
+ s, *(int *)optval));
+ break;
+ case TCP_KEEPCNT:
+ *(int*)optval = (int)sock->conn->pcb.tcp->keep_cnt;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPCNT) = %d\n",
+ s, *(int *)optval));
+ break;
+#endif /* LWIP_TCP_KEEPALIVE */
+ default:
+ LWIP_ASSERT("unhandled optname", 0);
+ break;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_TCP */
+#if LWIP_UDP && LWIP_UDPLITE
+ /* Level: IPPROTO_UDPLITE */
+ case IPPROTO_UDPLITE:
+ switch (optname) {
+ case UDPLITE_SEND_CSCOV:
+ *(int*)optval = sock->conn->pcb.udp->chksum_len_tx;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) = %d\n",
+ s, (*(int*)optval)) );
+ break;
+ case UDPLITE_RECV_CSCOV:
+ *(int*)optval = sock->conn->pcb.udp->chksum_len_rx;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) = %d\n",
+ s, (*(int*)optval)) );
+ break;
+ default:
+ LWIP_ASSERT("unhandled optname", 0);
+ break;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_UDP */
+ default:
+ LWIP_ASSERT("unhandled level", 0);
+ break;
+ } /* switch (level) */
+ sys_sem_signal(&sock->conn->op_completed);
+}
+
+int
+lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen)
+{
+ struct lwip_sock *sock = get_socket(s);
+ err_t err = ERR_OK;
+ struct lwip_setgetsockopt_data data;
+
+ if (!sock) {
+ return -1;
+ }
+
+ if (NULL == optval) {
+ sock_set_errno(sock, EFAULT);
+ return -1;
+ }
+
+ /* Do length and type checks for the various options first, to keep it readable. */
+ switch (level) {
+
+/* Level: SOL_SOCKET */
+ case SOL_SOCKET:
+ switch (optname) {
+
+ case SO_BROADCAST:
+ /* UNIMPL case SO_DEBUG: */
+ /* UNIMPL case SO_DONTROUTE: */
+ case SO_KEEPALIVE:
+ /* UNIMPL case case SO_CONTIMEO: */
+ /* UNIMPL case case SO_SNDTIMEO: */
+#if LWIP_SO_RCVTIMEO
+ case SO_RCVTIMEO:
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVBUF
+ case SO_RCVBUF:
+#endif /* LWIP_SO_RCVBUF */
+ /* UNIMPL case SO_OOBINLINE: */
+ /* UNIMPL case SO_SNDBUF: */
+ /* UNIMPL case SO_RCVLOWAT: */
+ /* UNIMPL case SO_SNDLOWAT: */
+#if SO_REUSE
+ case SO_REUSEADDR:
+ case SO_REUSEPORT:
+#endif /* SO_REUSE */
+ /* UNIMPL case SO_USELOOPBACK: */
+ if (optlen < sizeof(int)) {
+ err = EINVAL;
+ }
+ break;
+ case SO_NO_CHECK:
+ if (optlen < sizeof(int)) {
+ err = EINVAL;
+ }
+#if LWIP_UDP
+ if ((sock->conn->type != NETCONN_UDP) ||
+ ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0)) {
+ /* this flag is only available for UDP, not for UDP lite */
+ err = EAFNOSUPPORT;
+ }
+#endif /* LWIP_UDP */
+ break;
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ } /* switch (optname) */
+ break;
+
+/* Level: IPPROTO_IP */
+ case IPPROTO_IP:
+ switch (optname) {
+ /* UNIMPL case IP_HDRINCL: */
+ /* UNIMPL case IP_RCVDSTADDR: */
+ /* UNIMPL case IP_RCVIF: */
+ case IP_TTL:
+ case IP_TOS:
+ if (optlen < sizeof(int)) {
+ err = EINVAL;
+ }
+ break;
+#if LWIP_IGMP
+ case IP_MULTICAST_TTL:
+ if (optlen < sizeof(u8_t)) {
+ err = EINVAL;
+ }
+ if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP) {
+ err = EAFNOSUPPORT;
+ }
+ break;
+ case IP_MULTICAST_IF:
+ if (optlen < sizeof(struct in_addr)) {
+ err = EINVAL;
+ }
+ if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP) {
+ err = EAFNOSUPPORT;
+ }
+ break;
+ case IP_MULTICAST_LOOP:
+ if (optlen < sizeof(u8_t)) {
+ err = EINVAL;
+ }
+ if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP) {
+ err = EAFNOSUPPORT;
+ }
+ break;
+ case IP_ADD_MEMBERSHIP:
+ case IP_DROP_MEMBERSHIP:
+ if (optlen < sizeof(struct ip_mreq)) {
+ err = EINVAL;
+ }
+ if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP) {
+ err = EAFNOSUPPORT;
+ }
+ break;
+#endif /* LWIP_IGMP */
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ } /* switch (optname) */
+ break;
+
+#if LWIP_TCP
+/* Level: IPPROTO_TCP */
+ case IPPROTO_TCP:
+ if (optlen < sizeof(int)) {
+ err = EINVAL;
+ break;
+ }
+
+ /* If this is no TCP socket, ignore any options. */
+ if (sock->conn->type != NETCONN_TCP)
+ return 0;
+
+ switch (optname) {
+ case TCP_NODELAY:
+ case TCP_KEEPALIVE:
+#if LWIP_TCP_KEEPALIVE
+ case TCP_KEEPIDLE:
+ case TCP_KEEPINTVL:
+ case TCP_KEEPCNT:
+#endif /* LWIP_TCP_KEEPALIVE */
+ break;
+
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_TCP */
+#if LWIP_UDP && LWIP_UDPLITE
+/* Level: IPPROTO_UDPLITE */
+ case IPPROTO_UDPLITE:
+ if (optlen < sizeof(int)) {
+ err = EINVAL;
+ break;
+ }
+
+ /* If this is no UDP lite socket, ignore any options. */
+ if (sock->conn->type != NETCONN_UDPLITE)
+ return 0;
+
+ switch (optname) {
+ case UDPLITE_SEND_CSCOV:
+ case UDPLITE_RECV_CSCOV:
+ break;
+
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_UDP && LWIP_UDPLITE */
+/* UNDEFINED LEVEL */
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n",
+ s, level, optname));
+ err = ENOPROTOOPT;
+ } /* switch (level) */
+
+
+ if (err != ERR_OK) {
+ sock_set_errno(sock, err);
+ return -1;
+ }
+
+
+ /* Now do the actual option processing */
+ data.sock = sock;
+#ifdef LWIP_DEBUG
+ data.s = s;
+#endif /* LWIP_DEBUG */
+ data.level = level;
+ data.optname = optname;
+ data.optval = (void*)optval;
+ data.optlen = &optlen;
+ data.err = err;
+ tcpip_callback(lwip_setsockopt_internal, &data);
+ sys_arch_sem_wait(&sock->conn->op_completed, 0);
+ /* maybe lwip_setsockopt_internal has changed err */
+ err = data.err;
+
+ sock_set_errno(sock, err);
+ return err ? -1 : 0;
+}
+
+static void
+lwip_setsockopt_internal(void *arg)
+{
+ struct lwip_sock *sock;
+#ifdef LWIP_DEBUG
+ int s;
+#endif /* LWIP_DEBUG */
+ int level, optname;
+ const void *optval;
+ struct lwip_setgetsockopt_data *data;
+
+ LWIP_ASSERT("arg != NULL", arg != NULL);
+
+ data = (struct lwip_setgetsockopt_data*)arg;
+ sock = data->sock;
+#ifdef LWIP_DEBUG
+ s = data->s;
+#endif /* LWIP_DEBUG */
+ level = data->level;
+ optname = data->optname;
+ optval = data->optval;
+
+ switch (level) {
+
+/* Level: SOL_SOCKET */
+ case SOL_SOCKET:
+ switch (optname) {
+
+ /* The option flags */
+ case SO_BROADCAST:
+ /* UNIMPL case SO_DEBUG: */
+ /* UNIMPL case SO_DONTROUTE: */
+ case SO_KEEPALIVE:
+ /* UNIMPL case SO_OOBINCLUDE: */
+#if SO_REUSE
+ case SO_REUSEADDR:
+ case SO_REUSEPORT:
+#endif /* SO_REUSE */
+ /* UNIMPL case SO_USELOOPBACK: */
+ if (*(int*)optval) {
+ sock->conn->pcb.ip->so_options |= optname;
+ } else {
+ sock->conn->pcb.ip->so_options &= ~optname;
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, optname=0x%x, ..) -> %s\n",
+ s, optname, (*(int*)optval?"on":"off")));
+ break;
+#if LWIP_SO_RCVTIMEO
+ case SO_RCVTIMEO:
+ netconn_set_recvtimeout(sock->conn, *(int*)optval);
+ break;
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVBUF
+ case SO_RCVBUF:
+ netconn_set_recvbufsize(sock->conn, *(int*)optval);
+ break;
+#endif /* LWIP_SO_RCVBUF */
+#if LWIP_UDP
+ case SO_NO_CHECK:
+ if (*(int*)optval) {
+ udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) | UDP_FLAGS_NOCHKSUM);
+ } else {
+ udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) & ~UDP_FLAGS_NOCHKSUM);
+ }
+ break;
+#endif /* LWIP_UDP */
+ default:
+ LWIP_ASSERT("unhandled optname", 0);
+ break;
+ } /* switch (optname) */
+ break;
+
+/* Level: IPPROTO_IP */
+ case IPPROTO_IP:
+ switch (optname) {
+ case IP_TTL:
+ sock->conn->pcb.ip->ttl = (u8_t)(*(int*)optval);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TTL, ..) -> %d\n",
+ s, sock->conn->pcb.ip->ttl));
+ break;
+ case IP_TOS:
+ sock->conn->pcb.ip->tos = (u8_t)(*(int*)optval);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TOS, ..)-> %d\n",
+ s, sock->conn->pcb.ip->tos));
+ break;
+#if LWIP_IGMP
+ case IP_MULTICAST_TTL:
+ sock->conn->pcb.udp->ttl = (u8_t)(*(u8_t*)optval);
+ break;
+ case IP_MULTICAST_IF:
+ inet_addr_to_ipaddr(&sock->conn->pcb.udp->multicast_ip, (struct in_addr*)optval);
+ break;
+ case IP_MULTICAST_LOOP:
+ if (*(u8_t*)optval) {
+ udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) | UDP_FLAGS_MULTICAST_LOOP);
+ } else {
+ udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) & ~UDP_FLAGS_MULTICAST_LOOP);
+ }
+ break;
+ case IP_ADD_MEMBERSHIP:
+ case IP_DROP_MEMBERSHIP:
+ {
+ /* If this is a TCP or a RAW socket, ignore these options. */
+ struct ip_mreq *imr = (struct ip_mreq *)optval;
+ ip_addr_t if_addr;
+ ip_addr_t multi_addr;
+ inet_addr_to_ipaddr(&if_addr, &imr->imr_interface);
+ inet_addr_to_ipaddr(&multi_addr, &imr->imr_multiaddr);
+ if(optname == IP_ADD_MEMBERSHIP){
+ data->err = igmp_joingroup(&if_addr, &multi_addr);
+ } else {
+ data->err = igmp_leavegroup(&if_addr, &multi_addr);
+ }
+ if(data->err != ERR_OK) {
+ data->err = EADDRNOTAVAIL;
+ }
+ }
+ break;
+#endif /* LWIP_IGMP */
+ default:
+ LWIP_ASSERT("unhandled optname", 0);
+ break;
+ } /* switch (optname) */
+ break;
+
+#if LWIP_TCP
+/* Level: IPPROTO_TCP */
+ case IPPROTO_TCP:
+ switch (optname) {
+ case TCP_NODELAY:
+ if (*(int*)optval) {
+ tcp_nagle_disable(sock->conn->pcb.tcp);
+ } else {
+ tcp_nagle_enable(sock->conn->pcb.tcp);
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_NODELAY) -> %s\n",
+ s, (*(int *)optval)?"on":"off") );
+ break;
+ case TCP_KEEPALIVE:
+ sock->conn->pcb.tcp->keep_idle = (u32_t)(*(int*)optval);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPALIVE) -> %"U32_F"\n",
+ s, sock->conn->pcb.tcp->keep_idle));
+ break;
+
+#if LWIP_TCP_KEEPALIVE
+ case TCP_KEEPIDLE:
+ sock->conn->pcb.tcp->keep_idle = 1000*(u32_t)(*(int*)optval);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPIDLE) -> %"U32_F"\n",
+ s, sock->conn->pcb.tcp->keep_idle));
+ break;
+ case TCP_KEEPINTVL:
+ sock->conn->pcb.tcp->keep_intvl = 1000*(u32_t)(*(int*)optval);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPINTVL) -> %"U32_F"\n",
+ s, sock->conn->pcb.tcp->keep_intvl));
+ break;
+ case TCP_KEEPCNT:
+ sock->conn->pcb.tcp->keep_cnt = (u32_t)(*(int*)optval);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPCNT) -> %"U32_F"\n",
+ s, sock->conn->pcb.tcp->keep_cnt));
+ break;
+#endif /* LWIP_TCP_KEEPALIVE */
+ default:
+ LWIP_ASSERT("unhandled optname", 0);
+ break;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_TCP*/
+#if LWIP_UDP && LWIP_UDPLITE
+ /* Level: IPPROTO_UDPLITE */
+ case IPPROTO_UDPLITE:
+ switch (optname) {
+ case UDPLITE_SEND_CSCOV:
+ if ((*(int*)optval != 0) && ((*(int*)optval < 8)) || (*(int*)optval > 0xffff)) {
+ /* don't allow illegal values! */
+ sock->conn->pcb.udp->chksum_len_tx = 8;
+ } else {
+ sock->conn->pcb.udp->chksum_len_tx = (u16_t)*(int*)optval;
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) -> %d\n",
+ s, (*(int*)optval)) );
+ break;
+ case UDPLITE_RECV_CSCOV:
+ if ((*(int*)optval != 0) && ((*(int*)optval < 8)) || (*(int*)optval > 0xffff)) {
+ /* don't allow illegal values! */
+ sock->conn->pcb.udp->chksum_len_rx = 8;
+ } else {
+ sock->conn->pcb.udp->chksum_len_rx = (u16_t)*(int*)optval;
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) -> %d\n",
+ s, (*(int*)optval)) );
+ break;
+ default:
+ LWIP_ASSERT("unhandled optname", 0);
+ break;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_UDP */
+ default:
+ LWIP_ASSERT("unhandled level", 0);
+ break;
+ } /* switch (level) */
+ sys_sem_signal(&sock->conn->op_completed);
+}
+
+int
+lwip_ioctl(int s, long cmd, void *argp)
+{
+ struct lwip_sock *sock = get_socket(s);
+ u8_t val;
+#if LWIP_SO_RCVBUF
+ u16_t buflen = 0;
+ s16_t recv_avail;
+#endif /* LWIP_SO_RCVBUF */
+
+ if (!sock) {
+ return -1;
+ }
+
+ switch (cmd) {
+#if LWIP_SO_RCVBUF
+ case FIONREAD:
+ if (!argp) {
+ sock_set_errno(sock, EINVAL);
+ return -1;
+ }
+
+ SYS_ARCH_GET(sock->conn->recv_avail, recv_avail);
+ if (recv_avail < 0) {
+ recv_avail = 0;
+ }
+ *((u16_t*)argp) = (u16_t)recv_avail;
+
+ /* Check if there is data left from the last recv operation. /maq 041215 */
+ if (sock->lastdata) {
+ struct pbuf *p = (struct pbuf *)sock->lastdata;
+ if (netconn_type(sock->conn) != NETCONN_TCP) {
+ p = ((struct netbuf *)p)->p;
+ }
+ buflen = p->tot_len;
+ buflen -= sock->lastoffset;
+
+ *((u16_t*)argp) += buflen;
+ }
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONREAD, %p) = %"U16_F"\n", s, argp, *((u16_t*)argp)));
+ sock_set_errno(sock, 0);
+ return 0;
+#endif /* LWIP_SO_RCVBUF */
+
+ case FIONBIO:
+ val = 0;
+ if (argp && *(u32_t*)argp) {
+ val = 1;
+ }
+ netconn_set_nonblocking(sock->conn, val);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONBIO, %d)\n", s, val));
+ sock_set_errno(sock, 0);
+ return 0;
+
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, UNIMPL: 0x%lx, %p)\n", s, cmd, argp));
+ sock_set_errno(sock, ENOSYS); /* not yet implemented */
+ return -1;
+ } /* switch (cmd) */
+}
+
+/** A minimal implementation of fcntl.
+ * Currently only the commands F_GETFL and F_SETFL are implemented.
+ * Only the flag O_NONBLOCK is implemented.
+ */
+int
+lwip_fcntl(int s, int cmd, int val)
+{
+ struct lwip_sock *sock = get_socket(s);
+ int ret = -1;
+
+ if (!sock || !sock->conn) {
+ return -1;
+ }
+
+ switch (cmd) {
+ case F_GETFL:
+ ret = netconn_is_nonblocking(sock->conn) ? O_NONBLOCK : 0;
+ break;
+ case F_SETFL:
+ if ((val & ~O_NONBLOCK) == 0) {
+ /* only O_NONBLOCK, all other bits are zero */
+ netconn_set_nonblocking(sock->conn, val & O_NONBLOCK);
+ ret = 0;
+ }
+ break;
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_fcntl(%d, UNIMPL: %d, %d)\n", s, cmd, val));
+ break;
+ }
+ return ret;
+}
+
+#endif /* LWIP_SOCKET */
diff --git a/app/lwip/api/tcpip.c b/app/lwip/api/tcpip.c
new file mode 100644
index 00000000..01a49d56
--- /dev/null
+++ b/app/lwip/api/tcpip.c
@@ -0,0 +1,460 @@
+/**
+ * @file
+ * Sequential API Main thread module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if !NO_SYS /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/sys.h"
+#include "lwip/memp.h"
+#include "lwip/mem.h"
+#include "lwip/pbuf.h"
+#include "lwip/tcpip.h"
+#include "lwip/init.h"
+#include "netif/etharp.h"
+#include "netif/ppp_oe.h"
+
+/* global variables */
+static tcpip_init_done_fn tcpip_init_done;
+static void *tcpip_init_done_arg;
+static sys_mbox_t mbox;
+
+#if LWIP_TCPIP_CORE_LOCKING
+/** The global semaphore to lock the stack. */
+sys_mutex_t lock_tcpip_core;
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+
+/**
+ * The main lwIP thread. This thread has exclusive access to lwIP core functions
+ * (unless access to them is not locked). Other threads communicate with this
+ * thread using message boxes.
+ *
+ * It also starts all the timers to make sure they are running in the right
+ * thread context.
+ *
+ * @param arg unused argument
+ */
+static void
+tcpip_thread(void *arg)
+{
+ struct tcpip_msg *msg;
+ LWIP_UNUSED_ARG(arg);
+
+ if (tcpip_init_done != NULL) {//ûעԶʼ
+ tcpip_init_done(tcpip_init_done_arg);
+ }
+
+ LOCK_TCPIP_CORE();
+ while (1) { /* MAIN Loop */
+ UNLOCK_TCPIP_CORE();
+ LWIP_TCPIP_THREAD_ALIVE();
+ /* wait for a message, timeouts are processed while waiting */
+ sys_timeouts_mbox_fetch(&mbox, (void **)&msg);
+ LOCK_TCPIP_CORE();
+ switch (msg->type) {
+#if LWIP_NETCONN
+ case TCPIP_MSG_API://API
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API message %p\n", (void *)msg));
+ msg->msg.apimsg->function(&(msg->msg.apimsg->msg));
+ break;
+#endif /* LWIP_NETCONN */
+
+#if !LWIP_TCPIP_CORE_LOCKING_INPUT
+ case TCPIP_MSG_INPKT://ײݰ
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: PACKET %p\n", (void *)msg));
+#if LWIP_ETHERNET
+ if (msg->msg.inp.netif->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) {//֧ARP
+ ethernet_input(msg->msg.inp.p, msg->msg.inp.netif);//ARP
+ } else
+#endif /* LWIP_ETHERNET */
+ {
+ ip_input(msg->msg.inp.p, msg->msg.inp.netif);//IP
+ }
+ memp_free(MEMP_TCPIP_MSG_INPKT, msg);
+ break;
+#endif /* LWIP_TCPIP_CORE_LOCKING_INPUT */
+
+#if LWIP_NETIF_API
+ case TCPIP_MSG_NETIFAPI:
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: Netif API message %p\n", (void *)msg));
+ msg->msg.netifapimsg->function(&(msg->msg.netifapimsg->msg));
+ break;
+#endif /* LWIP_NETIF_API */
+
+ case TCPIP_MSG_CALLBACK://ϲصʽִһ
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK %p\n", (void *)msg));
+ msg->msg.cb.function(msg->msg.cb.ctx);
+ memp_free(MEMP_TCPIP_MSG_API, msg);
+ break;
+
+#if LWIP_TCPIP_TIMEOUT
+ case TCPIP_MSG_TIMEOUT://ϲעһʱ¼
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: TIMEOUT %p\n", (void *)msg));
+ sys_timeout(msg->msg.tmo.msecs, msg->msg.tmo.h, msg->msg.tmo.arg);
+ memp_free(MEMP_TCPIP_MSG_API, msg);
+ break;
+ case TCPIP_MSG_UNTIMEOUT://ϲɾһʱ¼
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: UNTIMEOUT %p\n", (void *)msg));
+ sys_untimeout(msg->msg.tmo.h, msg->msg.tmo.arg);
+ memp_free(MEMP_TCPIP_MSG_API, msg);
+ break;
+#endif /* LWIP_TCPIP_TIMEOUT */
+
+ default:
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: %d\n", msg->type));
+ LWIP_ASSERT("tcpip_thread: invalid message", 0);
+ break;
+ }
+ }
+}
+
+/**
+ * Pass a received packet to tcpip_thread for input processing
+ *
+ * @param p the received packet, p->payload pointing to the Ethernet header or
+ * to an IP header (if inp doesn't have NETIF_FLAG_ETHARP or
+ * NETIF_FLAG_ETHERNET flags)
+ * @param inp the network interface on which the packet was received
+ */
+err_t
+tcpip_input(struct pbuf *p, struct netif *inp)
+{
+#if LWIP_TCPIP_CORE_LOCKING_INPUT
+ err_t ret;
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_input: PACKET %p/%p\n", (void *)p, (void *)inp));
+ LOCK_TCPIP_CORE();
+#if LWIP_ETHERNET
+ if (inp->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) {
+ ret = ethernet_input(p, inp);
+ } else
+#endif /* LWIP_ETHERNET */
+ {
+ ret = ip_input(p, inp);
+ }
+ UNLOCK_TCPIP_CORE();
+ return ret;
+#else /* LWIP_TCPIP_CORE_LOCKING_INPUT */
+ struct tcpip_msg *msg;
+
+ if (sys_mbox_valid(&mbox)) {
+ msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_INPKT);
+ if (msg == NULL) {
+ return ERR_MEM;
+ }
+
+ msg->type = TCPIP_MSG_INPKT;
+ msg->msg.inp.p = p;
+ msg->msg.inp.netif = inp;
+ if (sys_mbox_trypost(&mbox, msg) != ERR_OK) {
+ memp_free(MEMP_TCPIP_MSG_INPKT, msg);
+ return ERR_MEM;
+ }
+ return ERR_OK;
+ }
+ return ERR_VAL;
+#endif /* LWIP_TCPIP_CORE_LOCKING_INPUT */
+}
+
+/**
+ * Call a specific function in the thread context of
+ * tcpip_thread for easy access synchronization.
+ * A function called in that way may access lwIP core code
+ * without fearing concurrent access.
+ *
+ * @param f the function to call
+ * @param ctx parameter passed to f
+ * @param block 1 to block until the request is posted, 0 to non-blocking mode
+ * @return ERR_OK if the function was called, another err_t if not
+ */
+err_t
+tcpip_callback_with_block(tcpip_callback_fn function, void *ctx, u8_t block)
+{
+ struct tcpip_msg *msg;
+
+ if (sys_mbox_valid(&mbox)) {
+ msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
+ if (msg == NULL) {
+ return ERR_MEM;
+ }
+
+ msg->type = TCPIP_MSG_CALLBACK;
+ msg->msg.cb.function = function;
+ msg->msg.cb.ctx = ctx;
+ if (block) {
+ sys_mbox_post(&mbox, msg);
+ } else {
+ if (sys_mbox_trypost(&mbox, msg) != ERR_OK) {
+ memp_free(MEMP_TCPIP_MSG_API, msg);
+ return ERR_MEM;
+ }
+ }
+ return ERR_OK;
+ }
+ return ERR_VAL;
+}
+
+#if LWIP_TCPIP_TIMEOUT
+/**
+ * call sys_timeout in tcpip_thread
+ *
+ * @param msec time in milliseconds for timeout
+ * @param h function to be called on timeout
+ * @param arg argument to pass to timeout function h
+ * @return ERR_MEM on memory error, ERR_OK otherwise
+ */
+err_t
+tcpip_timeout(u32_t msecs, sys_timeout_handler h, void *arg)
+{
+ struct tcpip_msg *msg;
+
+ if (sys_mbox_valid(&mbox)) {
+ msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
+ if (msg == NULL) {
+ return ERR_MEM;
+ }
+
+ msg->type = TCPIP_MSG_TIMEOUT;
+ msg->msg.tmo.msecs = msecs;
+ msg->msg.tmo.h = h;
+ msg->msg.tmo.arg = arg;
+ sys_mbox_post(&mbox, msg);
+ return ERR_OK;
+ }
+ return ERR_VAL;
+}
+
+/**
+ * call sys_untimeout in tcpip_thread
+ *
+ * @param msec time in milliseconds for timeout
+ * @param h function to be called on timeout
+ * @param arg argument to pass to timeout function h
+ * @return ERR_MEM on memory error, ERR_OK otherwise
+ */
+err_t
+tcpip_untimeout(sys_timeout_handler h, void *arg)
+{
+ struct tcpip_msg *msg;
+
+ if (sys_mbox_valid(&mbox)) {
+ msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
+ if (msg == NULL) {
+ return ERR_MEM;
+ }
+
+ msg->type = TCPIP_MSG_UNTIMEOUT;
+ msg->msg.tmo.h = h;
+ msg->msg.tmo.arg = arg;
+ sys_mbox_post(&mbox, msg);
+ return ERR_OK;
+ }
+ return ERR_VAL;
+}
+#endif /* LWIP_TCPIP_TIMEOUT */
+
+#if LWIP_NETCONN
+/**
+ * Call the lower part of a netconn_* function
+ * This function is then running in the thread context
+ * of tcpip_thread and has exclusive access to lwIP core code.
+ *
+ * @param apimsg a struct containing the function to call and its parameters
+ * @return ERR_OK if the function was called, another err_t if not
+ */
+err_t
+tcpip_apimsg(struct api_msg *apimsg)
+{
+ struct tcpip_msg msg;
+#ifdef LWIP_DEBUG
+ /* catch functions that don't set err */
+ apimsg->msg.err = ERR_VAL;
+#endif
+
+ if (sys_mbox_valid(&mbox)) {//ںЧ
+ msg.type = TCPIP_MSG_API;
+ msg.msg.apimsg = apimsg;
+ sys_mbox_post(&mbox, &msg);//ͶϢ
+ sys_arch_sem_wait(&apimsg->msg.conn->op_completed, 0);//ȴϢ
+ return apimsg->msg.err;
+ }
+ return ERR_VAL;
+}
+
+#if LWIP_TCPIP_CORE_LOCKING
+/**
+ * Call the lower part of a netconn_* function
+ * This function has exclusive access to lwIP core code by locking it
+ * before the function is called.
+ *
+ * @param apimsg a struct containing the function to call and its parameters
+ * @return ERR_OK (only for compatibility fo tcpip_apimsg())
+ */
+err_t
+tcpip_apimsg_lock(struct api_msg *apimsg)
+{
+#ifdef LWIP_DEBUG
+ /* catch functions that don't set err */
+ apimsg->msg.err = ERR_VAL;
+#endif
+
+ LOCK_TCPIP_CORE();
+ apimsg->function(&(apimsg->msg));
+ UNLOCK_TCPIP_CORE();
+ return apimsg->msg.err;
+
+}
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+#endif /* LWIP_NETCONN */
+
+#if LWIP_NETIF_API
+#if !LWIP_TCPIP_CORE_LOCKING
+/**
+ * Much like tcpip_apimsg, but calls the lower part of a netifapi_*
+ * function.
+ *
+ * @param netifapimsg a struct containing the function to call and its parameters
+ * @return error code given back by the function that was called
+ */
+err_t
+tcpip_netifapi(struct netifapi_msg* netifapimsg)
+{
+ struct tcpip_msg msg;
+
+ if (sys_mbox_valid(&mbox)) {
+ err_t err = sys_sem_new(&netifapimsg->msg.sem, 0);
+ if (err != ERR_OK) {
+ netifapimsg->msg.err = err;
+ return err;
+ }
+
+ msg.type = TCPIP_MSG_NETIFAPI;
+ msg.msg.netifapimsg = netifapimsg;
+ sys_mbox_post(&mbox, &msg);
+ sys_sem_wait(&netifapimsg->msg.sem);
+ sys_sem_free(&netifapimsg->msg.sem);
+ return netifapimsg->msg.err;
+ }
+ return ERR_VAL;
+}
+#else /* !LWIP_TCPIP_CORE_LOCKING */
+/**
+ * Call the lower part of a netifapi_* function
+ * This function has exclusive access to lwIP core code by locking it
+ * before the function is called.
+ *
+ * @param netifapimsg a struct containing the function to call and its parameters
+ * @return ERR_OK (only for compatibility fo tcpip_netifapi())
+ */
+err_t
+tcpip_netifapi_lock(struct netifapi_msg* netifapimsg)
+{
+ LOCK_TCPIP_CORE();
+ netifapimsg->function(&(netifapimsg->msg));
+ UNLOCK_TCPIP_CORE();
+ return netifapimsg->msg.err;
+}
+#endif /* !LWIP_TCPIP_CORE_LOCKING */
+#endif /* LWIP_NETIF_API */
+
+/**
+ * Initialize this module:
+ * - initialize all sub modules
+ * - start the tcpip_thread
+ *
+ * @param initfunc a function to call when tcpip_thread is running and finished initializing
+ * @param arg argument to pass to initfunc
+ */
+void
+tcpip_init(tcpip_init_done_fn initfunc, void *arg)
+{
+ lwip_init();//ʼں
+
+ tcpip_init_done = initfunc;//עûԶ庯
+ tcpip_init_done_arg = arg;//
+ if(sys_mbox_new(&mbox, TCPIP_MBOX_SIZE) != ERR_OK) {//ں
+ LWIP_ASSERT("failed to create tcpip_thread mbox", 0);
+ }
+#if LWIP_TCPIP_CORE_LOCKING
+ if(sys_mutex_new(&lock_tcpip_core) != ERR_OK) {
+ LWIP_ASSERT("failed to create lock_tcpip_core", 0);
+ }
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+ sys_thread_new(TCPIP_THREAD_NAME, tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO);//ں˽
+}
+
+/**
+ * Simple callback function used with tcpip_callback to free a pbuf
+ * (pbuf_free has a wrong signature for tcpip_callback)
+ *
+ * @param p The pbuf (chain) to be dereferenced.
+ */
+static void
+pbuf_free_int(void *p)
+{
+ struct pbuf *q = (struct pbuf *)p;
+ pbuf_free(q);
+}
+
+/**
+ * A simple wrapper function that allows you to free a pbuf from interrupt context.
+ *
+ * @param p The pbuf (chain) to be dereferenced.
+ * @return ERR_OK if callback could be enqueued, an err_t if not
+ */
+err_t
+pbuf_free_callback(struct pbuf *p)
+{
+ return tcpip_callback_with_block(pbuf_free_int, p, 0);
+}
+
+/**
+ * A simple wrapper function that allows you to free heap memory from
+ * interrupt context.
+ *
+ * @param m the heap memory to free
+ * @return ERR_OK if callback could be enqueued, an err_t if not
+ */
+err_t
+mem_free_callback(void *m)
+{
+ return tcpip_callback_with_block(mem_free, m, 0);
+}
+
+#endif /* !NO_SYS */
diff --git a/app/lwip/app/Makefile b/app/lwip/app/Makefile
new file mode 100644
index 00000000..3c2954ef
--- /dev/null
+++ b/app/lwip/app/Makefile
@@ -0,0 +1,46 @@
+
+#############################################################
+# Required variables for each makefile
+# Discard this section from all parent makefiles
+# Expected variables (with automatic defaults):
+# CSRCS (all "C" files in the dir)
+# SUBDIRS (all subdirs with a Makefile)
+# GEN_LIBS - list of libs to be generated ()
+# GEN_IMAGES - list of images to be generated ()
+# COMPONENTS_xxx - a list of libs/objs in the form
+# subdir/lib to be extracted and rolled up into
+# a generated lib/image xxx.a ()
+#
+ifndef PDIR
+
+GEN_LIBS = liblwipapp.a
+
+endif
+
+
+#############################################################
+# Configuration i.e. compile options etc.
+# Target specific stuff (defines etc.) goes in here!
+# Generally values applying to a tree are captured in the
+# makefile at its root level - these are then overridden
+# for a subtree within the makefile rooted therein
+#
+#DEFINES +=
+
+#############################################################
+# Recursion Magic - Don't touch this!!
+#
+# Each subtree potentially has an include directory
+# corresponding to the common APIs applicable to modules
+# rooted at that subtree. Accordingly, the INCLUDE PATH
+# of a module can only contain the include directories up
+# its parent path, and not its siblings
+#
+# Required for each makefile to inherit from the parent
+#
+
+INCLUDES := $(INCLUDES) -I $(PDIR)include
+INCLUDES += -I ./
+PDIR := ../$(PDIR)
+sinclude $(PDIR)Makefile
+
diff --git a/app/lwip/app/dhcpserver.c b/app/lwip/app/dhcpserver.c
new file mode 100644
index 00000000..81b9a3d4
--- /dev/null
+++ b/app/lwip/app/dhcpserver.c
@@ -0,0 +1,858 @@
+#include "lwip/inet.h"
+#include "lwip/err.h"
+#include "lwip/pbuf.h"
+#include "lwip/udp.h"
+#include "lwip/mem.h"
+//#include "crypto/common.h"
+#include "osapi.h"
+#include "lwip/app/dhcpserver.h"
+
+#ifndef LWIP_OPEN_SRC
+#include "net80211/ieee80211_var.h"
+#endif
+#include "user_interface.h"
+
+////////////////////////////////////////////////////////////////////////////////////
+static const uint8_t xid[4] = {0xad, 0xde, 0x12, 0x23};
+static u8_t old_xid[4] = {0};
+static const uint8_t magic_cookie[4] = {99, 130, 83, 99};
+static struct udp_pcb *pcb_dhcps = NULL;
+static struct ip_addr broadcast_dhcps;
+static struct ip_addr server_address;
+static struct ip_addr client_address;//added
+static struct ip_addr client_address_plus;
+static struct dhcps_msg msg_dhcps;
+struct dhcps_state s;
+
+static struct dhcps_lease dhcps_lease;
+static bool dhcps_lease_flag = true;
+static list_node *plist = NULL;
+/******************************************************************************
+ * FunctionName : node_insert_to_list
+ * Description : insert the node to the list
+ * Parameters : arg -- Additional argument to pass to the callback function
+ * Returns : none
+*******************************************************************************/
+void ICACHE_FLASH_ATTR node_insert_to_list(list_node **phead, list_node* pinsert)
+{
+ list_node *plist = NULL;
+
+ if (*phead == NULL)
+ *phead = pinsert;
+ else {
+ plist = *phead;
+ while (plist->pnext != NULL) {
+ plist = plist->pnext;
+ }
+ plist->pnext = pinsert;
+ }
+ pinsert->pnext = NULL;
+}
+
+/******************************************************************************
+ * FunctionName : node_delete_from_list
+ * Description : remove the node from list
+ * Parameters : arg -- Additional argument to pass to the callback function
+ * Returns : none
+*******************************************************************************/
+void ICACHE_FLASH_ATTR node_remove_from_list(list_node **phead, list_node* pdelete)
+{
+ list_node *plist = NULL;
+
+ plist = *phead;
+ if (plist == NULL){
+ *phead = NULL;
+ } else {
+ if (plist == pdelete){
+ *phead = plist->pnext;
+ } else {
+ while (plist != NULL) {
+ if (plist->pnext == pdelete){
+ plist->pnext = pdelete->pnext;
+ }
+ plist = plist->pnext;
+ }
+ }
+ }
+}
+///////////////////////////////////////////////////////////////////////////////////
+/*
+ * ��DHCP msg��Ϣ�ṹ����������
+ *
+ * @param optptr -- DHCP msg��Ϣλ��
+ * @param type -- Ҫ��ӵ�����option
+ *
+ * @return uint8_t* ����DHCP msgƫ�Ƶ�ַ
+ */
+///////////////////////////////////////////////////////////////////////////////////
+static uint8_t* ICACHE_FLASH_ATTR add_msg_type(uint8_t *optptr, uint8_t type)
+{
+
+ *optptr++ = DHCP_OPTION_MSG_TYPE;
+ *optptr++ = 1;
+ *optptr++ = type;
+ return optptr;
+}
+///////////////////////////////////////////////////////////////////////////////////
+/*
+ * ��DHCP msg�ṹ������offerӦ������
+ *
+ * @param optptr -- DHCP msg��Ϣλ��
+ *
+ * @return uint8_t* ����DHCP msgƫ�Ƶ�ַ
+ */
+///////////////////////////////////////////////////////////////////////////////////
+static uint8_t* ICACHE_FLASH_ATTR add_offer_options(uint8_t *optptr)
+{
+ struct ip_addr ipadd;
+
+ ipadd.addr = *( (uint32_t *) &server_address);
+
+#ifdef USE_CLASS_B_NET
+ *optptr++ = DHCP_OPTION_SUBNET_MASK;
+ *optptr++ = 4; //length
+ *optptr++ = 255;
+ *optptr++ = 240;
+ *optptr++ = 0;
+ *optptr++ = 0;
+#else
+ *optptr++ = DHCP_OPTION_SUBNET_MASK;
+ *optptr++ = 4;
+ *optptr++ = 255;
+ *optptr++ = 255;
+ *optptr++ = 255;
+ *optptr++ = 0;
+#endif
+
+ *optptr++ = DHCP_OPTION_LEASE_TIME;
+ *optptr++ = 4;
+ *optptr++ = 0x00;
+ *optptr++ = 0x01;
+ *optptr++ = 0x51;
+ *optptr++ = 0x80;
+
+ *optptr++ = DHCP_OPTION_SERVER_ID;
+ *optptr++ = 4;
+ *optptr++ = ip4_addr1( &ipadd);
+ *optptr++ = ip4_addr2( &ipadd);
+ *optptr++ = ip4_addr3( &ipadd);
+ *optptr++ = ip4_addr4( &ipadd);
+
+ *optptr++ = DHCP_OPTION_ROUTER;
+ *optptr++ = 4;
+ *optptr++ = ip4_addr1( &ipadd);
+ *optptr++ = ip4_addr2( &ipadd);
+ *optptr++ = ip4_addr3( &ipadd);
+ *optptr++ = ip4_addr4( &ipadd);
+
+#ifdef USE_DNS
+ *optptr++ = DHCP_OPTION_DNS_SERVER;
+ *optptr++ = 4;
+ *optptr++ = ip4_addr1( &ipadd);
+ *optptr++ = ip4_addr2( &ipadd);
+ *optptr++ = ip4_addr3( &ipadd);
+ *optptr++ = ip4_addr4( &ipadd);
+#endif
+
+#ifdef CLASS_B_NET
+ *optptr++ = DHCP_OPTION_BROADCAST_ADDRESS;
+ *optptr++ = 4;
+ *optptr++ = ip4_addr1( &ipadd);
+ *optptr++ = 255;
+ *optptr++ = 255;
+ *optptr++ = 255;
+#else
+ *optptr++ = DHCP_OPTION_BROADCAST_ADDRESS;
+ *optptr++ = 4;
+ *optptr++ = ip4_addr1( &ipadd);
+ *optptr++ = ip4_addr2( &ipadd);
+ *optptr++ = ip4_addr3( &ipadd);
+ *optptr++ = 255;
+#endif
+
+ *optptr++ = DHCP_OPTION_INTERFACE_MTU;
+ *optptr++ = 2;
+#ifdef CLASS_B_NET
+ *optptr++ = 0x05;
+ *optptr++ = 0xdc;
+#else
+ *optptr++ = 0x02;
+ *optptr++ = 0x40;
+#endif
+
+ *optptr++ = DHCP_OPTION_PERFORM_ROUTER_DISCOVERY;
+ *optptr++ = 1;
+ *optptr++ = 0x00;
+
+ *optptr++ = 43;
+ *optptr++ = 6;
+
+ *optptr++ = 0x01;
+ *optptr++ = 4;
+ *optptr++ = 0x00;
+ *optptr++ = 0x00;
+ *optptr++ = 0x00;
+ *optptr++ = 0x02;
+
+ return optptr;
+}
+///////////////////////////////////////////////////////////////////////////////////
+/*
+ * ��DHCP msg�ṹ����ӽ����־����
+ *
+ * @param optptr -- DHCP msg��Ϣλ��
+ *
+ * @return uint8_t* ����DHCP msgƫ�Ƶ�ַ
+ */
+///////////////////////////////////////////////////////////////////////////////////
+static uint8_t* ICACHE_FLASH_ATTR add_end(uint8_t *optptr)
+{
+
+ *optptr++ = DHCP_OPTION_END;
+ return optptr;
+}
+///////////////////////////////////////////////////////////////////////////////////
+/*
+ * ����һ��DHCP msg�ṹ��
+ *
+ * @param -- m ָ����DHCP msg�ṹ�����?
+ */
+///////////////////////////////////////////////////////////////////////////////////
+static void ICACHE_FLASH_ATTR create_msg(struct dhcps_msg *m)
+{
+ struct ip_addr client;
+
+ client.addr = *( (uint32_t *) &client_address);
+
+ m->op = DHCP_REPLY;
+ m->htype = DHCP_HTYPE_ETHERNET;
+ m->hlen = 6;
+ m->hops = 0;
+ os_memcpy((char *) xid, (char *) m->xid, sizeof(m->xid));
+ m->secs = 0;
+ m->flags = htons(BOOTP_BROADCAST);
+
+ os_memcpy((char *) m->yiaddr, (char *) &client.addr, sizeof(m->yiaddr));
+
+ os_memset((char *) m->ciaddr, 0, sizeof(m->ciaddr));
+ os_memset((char *) m->siaddr, 0, sizeof(m->siaddr));
+ os_memset((char *) m->giaddr, 0, sizeof(m->giaddr));
+ os_memset((char *) m->sname, 0, sizeof(m->sname));
+ os_memset((char *) m->file, 0, sizeof(m->file));
+
+ os_memset((char *) m->options, 0, sizeof(m->options));
+ os_memcpy((char *) m->options, (char *) magic_cookie, sizeof(magic_cookie));
+}
+///////////////////////////////////////////////////////////////////////////////////
+/*
+ * ����һ��OFFER
+ *
+ * @param -- m ָ����Ҫ���͵�DHCP msg����
+ */
+///////////////////////////////////////////////////////////////////////////////////
+static void ICACHE_FLASH_ATTR send_offer(struct dhcps_msg *m)
+{
+ uint8_t *end;
+ struct pbuf *p, *q;
+ u8_t *data;
+ u16_t cnt=0;
+ u16_t i;
+ err_t SendOffer_err_t;
+ create_msg(m);
+
+ end = add_msg_type(&m->options[4], DHCPOFFER);
+ end = add_offer_options(end);
+ end = add_end(end);
+
+ p = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcps_msg), PBUF_RAM);
+#if DHCPS_DEBUG
+ os_printf("udhcp: send_offer>>p->ref = %d\n", p->ref);
+#endif
+ if(p != NULL){
+
+#if DHCPS_DEBUG
+ os_printf("dhcps: send_offer>>pbuf_alloc succeed\n");
+ os_printf("dhcps: send_offer>>p->tot_len = %d\n", p->tot_len);
+ os_printf("dhcps: send_offer>>p->len = %d\n", p->len);
+#endif
+ q = p;
+ while(q != NULL){
+ data = (u8_t *)q->payload;
+ for(i=0; ilen; i++)
+ {
+ data[i] = ((u8_t *) m)[cnt++];
+#if DHCPS_DEBUG
+ os_printf("%02x ",data[i]);
+ if((i+1)%16 == 0){
+ os_printf("\n");
+ }
+#endif
+ }
+
+ q = q->next;
+ }
+ }else{
+
+#if DHCPS_DEBUG
+ os_printf("dhcps: send_offer>>pbuf_alloc failed\n");
+#endif
+ return;
+ }
+ SendOffer_err_t = udp_sendto( pcb_dhcps, p, &broadcast_dhcps, DHCPS_CLIENT_PORT );
+#if DHCPS_DEBUG
+ os_printf("dhcps: send_offer>>udp_sendto result %x\n",SendOffer_err_t);
+#endif
+ if(p->ref != 0){
+#if DHCPS_DEBUG
+ os_printf("udhcp: send_offer>>free pbuf\n");
+#endif
+ pbuf_free(p);
+ }
+}
+///////////////////////////////////////////////////////////////////////////////////
+/*
+ * ����һ��NAK��Ϣ
+ *
+ * @param m ָ����Ҫ���͵�DHCP msg����
+ */
+///////////////////////////////////////////////////////////////////////////////////
+static void ICACHE_FLASH_ATTR send_nak(struct dhcps_msg *m)
+{
+
+ u8_t *end;
+ struct pbuf *p, *q;
+ u8_t *data;
+ u16_t cnt=0;
+ u16_t i;
+ err_t SendNak_err_t;
+ create_msg(m);
+
+ end = add_msg_type(&m->options[4], DHCPNAK);
+ end = add_end(end);
+
+ p = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcps_msg), PBUF_RAM);
+#if DHCPS_DEBUG
+ os_printf("udhcp: send_nak>>p->ref = %d\n", p->ref);
+#endif
+ if(p != NULL){
+
+#if DHCPS_DEBUG
+ os_printf("dhcps: send_nak>>pbuf_alloc succeed\n");
+ os_printf("dhcps: send_nak>>p->tot_len = %d\n", p->tot_len);
+ os_printf("dhcps: send_nak>>p->len = %d\n", p->len);
+#endif
+ q = p;
+ while(q != NULL){
+ data = (u8_t *)q->payload;
+ for(i=0; ilen; i++)
+ {
+ data[i] = ((u8_t *) m)[cnt++];
+#if DHCPS_DEBUG
+ os_printf("%02x ",data[i]);
+ if((i+1)%16 == 0){
+ os_printf("\n");
+ }
+#endif
+ }
+
+ q = q->next;
+ }
+ }else{
+
+#if DHCPS_DEBUG
+ os_printf("dhcps: send_nak>>pbuf_alloc failed\n");
+#endif
+ return;
+ }
+ SendNak_err_t = udp_sendto( pcb_dhcps, p, &broadcast_dhcps, DHCPS_CLIENT_PORT );
+#if DHCPS_DEBUG
+ os_printf("dhcps: send_nak>>udp_sendto result %x\n",SendNak_err_t);
+#endif
+ if(p->ref != 0){
+#if DHCPS_DEBUG
+ os_printf("udhcp: send_nak>>free pbuf\n");
+#endif
+ pbuf_free(p);
+ }
+}
+///////////////////////////////////////////////////////////////////////////////////
+/*
+ * ����һ��ACK��DHCP�ͻ���
+ *
+ * @param m ָ����Ҫ���͵�DHCP msg����
+ */
+///////////////////////////////////////////////////////////////////////////////////
+static void ICACHE_FLASH_ATTR send_ack(struct dhcps_msg *m)
+{
+
+ u8_t *end;
+ struct pbuf *p, *q;
+ u8_t *data;
+ u16_t cnt=0;
+ u16_t i;
+ err_t SendAck_err_t;
+ create_msg(m);
+
+ end = add_msg_type(&m->options[4], DHCPACK);
+ end = add_offer_options(end);
+ end = add_end(end);
+
+ p = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcps_msg), PBUF_RAM);
+#if DHCPS_DEBUG
+ os_printf("udhcp: send_ack>>p->ref = %d\n", p->ref);
+#endif
+ if(p != NULL){
+
+#if DHCPS_DEBUG
+ os_printf("dhcps: send_ack>>pbuf_alloc succeed\n");
+ os_printf("dhcps: send_ack>>p->tot_len = %d\n", p->tot_len);
+ os_printf("dhcps: send_ack>>p->len = %d\n", p->len);
+#endif
+ q = p;
+ while(q != NULL){
+ data = (u8_t *)q->payload;
+ for(i=0; ilen; i++)
+ {
+ data[i] = ((u8_t *) m)[cnt++];
+#if DHCPS_DEBUG
+ os_printf("%02x ",data[i]);
+ if((i+1)%16 == 0){
+ os_printf("\n");
+ }
+#endif
+ }
+
+ q = q->next;
+ }
+ }else{
+
+#if DHCPS_DEBUG
+ os_printf("dhcps: send_ack>>pbuf_alloc failed\n");
+#endif
+ return;
+ }
+ SendAck_err_t = udp_sendto( pcb_dhcps, p, &broadcast_dhcps, DHCPS_CLIENT_PORT );
+#if DHCPS_DEBUG
+ os_printf("dhcps: send_ack>>udp_sendto result %x\n",SendAck_err_t);
+#endif
+
+ if(p->ref != 0){
+#if DHCPS_DEBUG
+ os_printf("udhcp: send_ack>>free pbuf\n");
+#endif
+ pbuf_free(p);
+ }
+}
+///////////////////////////////////////////////////////////////////////////////////
+/*
+ * ����DHCP�ͻ��˷�����DHCP����������Ϣ�����Բ�ͬ��DHCP��������������Ӧ��Ӧ��
+ *
+ * @param optptr DHCP msg���������
+ * @param len ��������Ĵ��?(byte)
+ *
+ * @return uint8_t ���ش�����DHCP Server״ֵ̬
+ */
+///////////////////////////////////////////////////////////////////////////////////
+static uint8_t ICACHE_FLASH_ATTR parse_options(uint8_t *optptr, sint16_t len)
+{
+ struct ip_addr client;
+ bool is_dhcp_parse_end = false;
+
+ client.addr = *( (uint32_t *) &client_address);// Ҫ�����DHCP�ͻ��˵�IP
+
+ u8_t *end = optptr + len;
+ u16_t type = 0;
+
+ s.state = DHCPS_STATE_IDLE;
+
+ while (optptr < end) {
+#if DHCPS_DEBUG
+ os_printf("dhcps: (sint16_t)*optptr = %d\n", (sint16_t)*optptr);
+#endif
+ switch ((sint16_t) *optptr) {
+
+ case DHCP_OPTION_MSG_TYPE: //53
+ type = *(optptr + 2);
+ break;
+
+ case DHCP_OPTION_REQ_IPADDR://50
+ if( os_memcmp( (char *) &client.addr, (char *) optptr+2,4)==0 ) {
+#if DHCPS_DEBUG
+ os_printf("dhcps: DHCP_OPTION_REQ_IPADDR = 0 ok\n");
+#endif
+ s.state = DHCPS_STATE_ACK;
+ }else {
+#if DHCPS_DEBUG
+ os_printf("dhcps: DHCP_OPTION_REQ_IPADDR != 0 err\n");
+#endif
+ s.state = DHCPS_STATE_NAK;
+ }
+ break;
+ case DHCP_OPTION_END:
+ {
+ is_dhcp_parse_end = true;
+ }
+ break;
+ }
+
+ if(is_dhcp_parse_end){
+ break;
+ }
+
+ optptr += optptr[1] + 2;
+ }
+
+ switch (type){
+
+ case DHCPDISCOVER://1
+ s.state = DHCPS_STATE_OFFER;
+#if DHCPS_DEBUG
+ os_printf("dhcps: DHCPD_STATE_OFFER\n");
+#endif
+ break;
+
+ case DHCPREQUEST://3
+ if ( !(s.state == DHCPS_STATE_ACK || s.state == DHCPS_STATE_NAK) ) {
+ s.state = DHCPS_STATE_NAK;
+#if DHCPS_DEBUG
+ os_printf("dhcps: DHCPD_STATE_NAK\n");
+#endif
+ }
+ break;
+
+ case DHCPDECLINE://4
+ s.state = DHCPS_STATE_IDLE;
+#if DHCPS_DEBUG
+ os_printf("dhcps: DHCPD_STATE_IDLE\n");
+#endif
+ break;
+
+ case DHCPRELEASE://7
+ s.state = DHCPS_STATE_IDLE;
+#if DHCPS_DEBUG
+ os_printf("dhcps: DHCPD_STATE_IDLE\n");
+#endif
+ break;
+ }
+#if DHCPS_DEBUG
+ os_printf("dhcps: return s.state = %d\n", s.state);
+#endif
+ return s.state;
+}
+///////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////
+static sint16_t ICACHE_FLASH_ATTR parse_msg(struct dhcps_msg *m, u16_t len)
+{
+ if(os_memcmp((char *)m->options,
+ (char *)magic_cookie,
+ sizeof(magic_cookie)) == 0){
+#if DHCPS_DEBUG
+ os_printf("dhcps: len = %d\n", len);
+#endif
+ /*
+ * ��¼��ǰ��xid���ﴦ���?
+ * �˺�ΪDHCP�ͻ����������û�ͳһ��ȡIPʱ��
+ */
+// if((old_xid[0] == 0) &&
+// (old_xid[1] == 0) &&
+// (old_xid[2] == 0) &&
+// (old_xid[3] == 0)){
+// /*
+// * old_xidδ��¼�κ����?
+// * �϶��ǵ�һ��ʹ��
+// */
+// os_memcpy((char *)old_xid, (char *)m->xid, sizeof(m->xid));
+// }else{
+// /*
+// * ���δ����DHCP msg��Я���xid���ϴμ�¼�IJ�ͬ��
+// * �϶�Ϊ��ͬ��DHCP�ͻ��˷��ͣ���ʱ����Ҫ����Ŀͻ���IP
+// * ���� 192.168.4.100(0x6404A8C0) <--> 192.168.4.200(0xC804A8C0)
+// *
+// */
+// if(os_memcmp((char *)old_xid, (char *)m->xid, sizeof(m->xid)) != 0){
+ /*
+ * ��¼���ε�xid�ţ�ͬʱ�����IP����
+ */
+ struct ip_addr addr_tmp;
+ os_memcpy((char *)old_xid, (char *)m->xid, sizeof(m->xid));
+
+ {
+ struct dhcps_pool *pdhcps_pool = NULL;
+ list_node *pnode = NULL;
+ list_node *pback_node = NULL;
+
+ POOL_START:
+ client_address.addr = client_address_plus.addr;
+// addr_tmp.addr = htonl(client_address_plus.addr);
+// addr_tmp.addr++;
+// client_address_plus.addr = htonl(addr_tmp.addr);
+ for (pback_node = plist; pback_node != NULL;pback_node = pback_node->pnext) {
+ pdhcps_pool = pback_node->pnode;
+ if (os_memcmp(pdhcps_pool->mac, m->chaddr, sizeof(pdhcps_pool->mac)) == 0){
+// os_printf("the same device request ip\n");
+ client_address.addr = pdhcps_pool->ip.addr;
+ pdhcps_pool->lease_timer = DHCPS_LEASE_TIMER;
+ goto POOL_CHECK;
+ } else if (pdhcps_pool->ip.addr == client_address_plus.addr){
+// client_address.addr = client_address_plus.addr;
+// os_printf("the ip addr has been request\n");
+ addr_tmp.addr = htonl(client_address_plus.addr);
+ addr_tmp.addr++;
+ client_address_plus.addr = htonl(addr_tmp.addr);
+ client_address.addr = client_address_plus.addr;
+ }
+ }
+ pdhcps_pool = (struct dhcps_pool *)os_zalloc(sizeof(struct dhcps_pool));
+ pdhcps_pool->ip.addr = client_address.addr;
+ os_memcpy(pdhcps_pool->mac, m->chaddr, sizeof(pdhcps_pool->mac));
+ pdhcps_pool->lease_timer = DHCPS_LEASE_TIMER;
+ pnode = (list_node *)os_zalloc(sizeof(list_node ));
+ pnode->pnode = pdhcps_pool;
+ node_insert_to_list(&plist,pnode);
+
+ POOL_CHECK:
+ if ((client_address_plus.addr > dhcps_lease.end_ip) || (ip_addr_isany(&client_address))){
+ client_address_plus.addr = dhcps_lease.start_ip;
+ goto POOL_START;
+ }
+
+ if (wifi_softap_set_station_info(m->chaddr, &client_address) == false) {
+ return 0;
+ }
+ }
+
+#if DHCPS_DEBUG
+ os_printf("dhcps: xid changed\n");
+ os_printf("dhcps: client_address.addr = %x\n", client_address.addr);
+#endif
+
+// }
+
+// }
+
+ return parse_options(&m->options[4], len);
+ }
+ return 0;
+}
+///////////////////////////////////////////////////////////////////////////////////
+/*
+ * DHCP ��������ݰ���մ���ص�����˺�����LWIP UDPģ������ʱ������
+ * ��Ҫ����udp_recv()������LWIP����ע��.
+ *
+ * @param arg
+ * @param pcb ���յ�UDP��Ŀ��ƿ�?
+ * @param p ���յ���UDP�е��������?
+ * @param addr ���ʹ�UDP���Դ�����IP��ַ
+ * @param port ���ʹ�UDP���Դ�����UDPͨ���˿ں�
+ */
+///////////////////////////////////////////////////////////////////////////////////
+static void ICACHE_FLASH_ATTR handle_dhcp(void *arg,
+ struct udp_pcb *pcb,
+ struct pbuf *p,
+ struct ip_addr *addr,
+ uint16_t port)
+{
+
+ sint16_t tlen;
+ u16_t i;
+ u16_t dhcps_msg_cnt=0;
+ u8_t *p_dhcps_msg = (u8_t *)&msg_dhcps;
+ u8_t *data;
+
+#if DHCPS_DEBUG
+ os_printf("dhcps: handle_dhcp-> receive a packet\n");
+#endif
+ if (p==NULL) return;
+
+ tlen = p->tot_len;
+ data = p->payload;
+
+#if DHCPS_DEBUG
+ os_printf("dhcps: handle_dhcp-> p->tot_len = %d\n", tlen);
+ os_printf("dhcps: handle_dhcp-> p->len = %d\n", p->len);
+#endif
+
+ os_memset(&msg_dhcps, 0, sizeof(dhcps_msg));
+ for(i=0; ilen; i++){
+ p_dhcps_msg[dhcps_msg_cnt++] = data[i];
+#if DHCPS_DEBUG
+ os_printf("%02x ",data[i]);
+ if((i+1)%16 == 0){
+ os_printf("\n");
+ }
+#endif
+ }
+
+ if(p->next != NULL) {
+#if DHCPS_DEBUG
+ os_printf("dhcps: handle_dhcp-> p->next != NULL\n");
+ os_printf("dhcps: handle_dhcp-> p->next->tot_len = %d\n",p->next->tot_len);
+ os_printf("dhcps: handle_dhcp-> p->next->len = %d\n",p->next->len);
+#endif
+
+ data = p->next->payload;
+ for(i=0; inext->len; i++){
+ p_dhcps_msg[dhcps_msg_cnt++] = data[i];
+#if DHCPS_DEBUG
+ os_printf("%02x ",data[i]);
+ if((i+1)%16 == 0){
+ os_printf("\n");
+ }
+#endif
+ }
+ }
+
+ /*
+ * DHCP �ͻ���������Ϣ����
+ */
+#if DHCPS_DEBUG
+ os_printf("dhcps: handle_dhcp-> parse_msg(p)\n");
+#endif
+
+ switch(parse_msg(&msg_dhcps, tlen - 240)) {
+
+ case DHCPS_STATE_OFFER://1
+#if DHCPS_DEBUG
+ os_printf("dhcps: handle_dhcp-> DHCPD_STATE_OFFER\n");
+#endif
+ send_offer(&msg_dhcps);
+ break;
+ case DHCPS_STATE_ACK://3
+#if DHCPS_DEBUG
+ os_printf("dhcps: handle_dhcp-> DHCPD_STATE_ACK\n");
+#endif
+ send_ack(&msg_dhcps);
+ break;
+ case DHCPS_STATE_NAK://4
+#if DHCPS_DEBUG
+ os_printf("dhcps: handle_dhcp-> DHCPD_STATE_NAK\n");
+#endif
+ send_nak(&msg_dhcps);
+ break;
+ default :
+ break;
+ }
+#if DHCPS_DEBUG
+ os_printf("dhcps: handle_dhcp-> pbuf_free(p)\n");
+#endif
+ pbuf_free(p);
+}
+///////////////////////////////////////////////////////////////////////////////////
+static void ICACHE_FLASH_ATTR wifi_softap_init_dhcps_lease(uint32 ip)
+{
+ uint32 softap_ip = 0,local_ip = 0;
+
+ if (dhcps_lease_flag) {
+ local_ip = softap_ip = htonl(ip);
+ softap_ip &= 0xFFFFFF00;
+ local_ip &= 0xFF;
+ if (local_ip >= 0x80)
+ local_ip -= DHCPS_MAX_LEASE;
+ else
+ local_ip ++;
+
+ os_bzero(&dhcps_lease, sizeof(dhcps_lease));
+ dhcps_lease.start_ip = softap_ip | local_ip;
+ dhcps_lease.end_ip = softap_ip | (local_ip + DHCPS_MAX_LEASE);
+ }
+ dhcps_lease.start_ip = htonl(dhcps_lease.start_ip);
+ dhcps_lease.end_ip= htonl(dhcps_lease.end_ip);
+// os_printf("start_ip = 0x%x, end_ip = 0x%x\n",dhcps_lease.start_ip, dhcps_lease.end_ip);
+}
+///////////////////////////////////////////////////////////////////////////////////
+void ICACHE_FLASH_ATTR dhcps_start(struct ip_info *info)
+{
+ os_memset(&msg_dhcps, 0, sizeof(dhcps_msg));
+ pcb_dhcps = udp_new();
+ if (pcb_dhcps == NULL || info ==NULL) {
+ os_printf("dhcps_start(): could not obtain pcb\n");
+ }
+
+ IP4_ADDR(&broadcast_dhcps, 255, 255, 255, 255);
+
+ server_address = info->ip;
+ wifi_softap_init_dhcps_lease(server_address.addr);
+ client_address_plus.addr = dhcps_lease.start_ip;
+
+ udp_bind(pcb_dhcps, IP_ADDR_ANY, DHCPS_SERVER_PORT);
+ udp_recv(pcb_dhcps, handle_dhcp, NULL);
+#if DHCPS_DEBUG
+ os_printf("dhcps:dhcps_start->udp_recv function Set a receive callback handle_dhcp for UDP_PCB pcb_dhcps\n");
+#endif
+
+}
+
+void ICACHE_FLASH_ATTR dhcps_stop(void)
+{
+ udp_disconnect(pcb_dhcps);
+ udp_remove(pcb_dhcps);
+ list_node *pnode = NULL;
+ list_node *pback_node = NULL;
+ pnode = plist;
+ while (pnode != NULL) {
+ pback_node = pnode;
+ pnode = pback_node->pnext;
+ node_remove_from_list(&plist, pback_node);
+ os_free(pback_node->pnode);
+ pback_node->pnode = NULL;
+ os_free(pback_node);
+ pback_node = NULL;
+ }
+}
+
+bool ICACHE_FLASH_ATTR wifi_softap_set_dhcps_lease(struct dhcps_lease *please)
+{
+ struct ip_info info;
+ uint32 softap_ip = 0;
+ if (please == NULL)
+ return false;
+
+ os_bzero(&info, sizeof(struct ip_info));
+ wifi_get_ip_info(SOFTAP_IF, &info);
+ softap_ip = htonl(info.ip.addr);
+ please->start_ip = htonl(please->start_ip);
+ please->end_ip = htonl(please->end_ip);
+
+ /*config ip information can't contain local ip*/
+ if ((please->start_ip <= softap_ip) && (softap_ip <= please->end_ip))
+ return false;
+
+ /*config ip information must be in the same segment as the local ip*/
+ softap_ip >>= 8;
+ if ((please->start_ip >> 8 != softap_ip)
+ || (please->end_ip >> 8 != softap_ip)) {
+ return false;
+ }
+
+ if (please->end_ip - please->start_ip > DHCPS_MAX_LEASE)
+ return false;
+
+ os_bzero(&dhcps_lease, sizeof(dhcps_lease));
+ dhcps_lease.start_ip = please->start_ip;
+ dhcps_lease.end_ip = please->end_ip;
+ dhcps_lease_flag = false;
+ return true;
+}
+
+void ICACHE_FLASH_ATTR dhcps_coarse_tmr(void)
+{
+ list_node *pback_node = NULL;
+ list_node *pnode = NULL;
+ struct dhcps_pool *pdhcps_pool = NULL;
+ pnode = plist;
+ while (pnode != NULL) {
+ pdhcps_pool = pnode->pnode;
+ pdhcps_pool->lease_timer --;
+ if (pdhcps_pool->lease_timer == 0){
+ pback_node = pnode;
+ pnode = pback_node->pnext;
+ node_remove_from_list(&plist,pback_node);
+ os_free(pback_node->pnode);
+ pback_node->pnode = NULL;
+ os_free(pback_node);
+ pback_node = NULL;
+ } else {
+ pnode = pnode ->pnext;
+ }
+ }
+}
diff --git a/app/lwip/app/espconn.c b/app/lwip/app/espconn.c
new file mode 100644
index 00000000..15dcc2dd
--- /dev/null
+++ b/app/lwip/app/espconn.c
@@ -0,0 +1,719 @@
+/******************************************************************************
+ * Copyright 2013-2014 Espressif Systems (Wuxi)
+ *
+ * FileName: espconn.c
+ *
+ * Description: espconn interface for user
+ *
+ * Modification history:
+ * 2014/3/31, v1.0 create this file.
+*******************************************************************************/
+
+#include "lwip/netif.h"
+#include "lwip/inet.h"
+#include "netif/etharp.h"
+#include "lwip/tcp.h"
+#include "lwip/ip.h"
+#include "lwip/init.h"
+#include "ets_sys.h"
+#include "os_type.h"
+//#include "os.h"
+#include "lwip/mem.h"
+
+#include "lwip/app/espconn_tcp.h"
+#include "lwip/app/espconn_udp.h"
+#include "lwip/app/espconn.h"
+
+#include "user_interface.h"
+
+espconn_msg *plink_active = NULL;
+espconn_msg *pserver_list = NULL;
+remot_info premot[5];
+uint32 link_timer = 0;
+
+/******************************************************************************
+ * FunctionName : espconn_copy_partial
+ * Description : reconnect with host
+ * Parameters : arg -- Additional argument to pass to the callback function
+ * Returns : none
+*******************************************************************************/
+void ICACHE_FLASH_ATTR
+espconn_copy_partial(struct espconn *pesp_dest, struct espconn *pesp_source)
+{
+ pesp_dest->type = pesp_source->type;
+ pesp_dest->state = pesp_source->state;
+ if (pesp_source->type == ESPCONN_TCP){
+ pesp_dest->proto.tcp->remote_port = pesp_source->proto.tcp->remote_port;
+ pesp_dest->proto.tcp->local_port = pesp_source->proto.tcp->local_port;
+ os_memcpy(pesp_dest->proto.tcp->remote_ip, pesp_source->proto.tcp->remote_ip, 4);
+ os_memcpy(pesp_dest->proto.tcp->local_ip, pesp_source->proto.tcp->local_ip, 4);
+ pesp_dest->proto.tcp->connect_callback = pesp_source->proto.tcp->connect_callback;
+ pesp_dest->proto.tcp->reconnect_callback = pesp_source->proto.tcp->reconnect_callback;
+ pesp_dest->proto.tcp->disconnect_callback = pesp_source->proto.tcp->disconnect_callback;
+ } else {
+ pesp_dest->proto.udp->remote_port = pesp_source->proto.udp->remote_port;
+ pesp_dest->proto.udp->local_port = pesp_source->proto.udp->local_port;
+ os_memcpy(pesp_dest->proto.udp->remote_ip, pesp_source->proto.udp->remote_ip, 4);
+ os_memcpy(pesp_dest->proto.udp->local_ip, pesp_source->proto.udp->local_ip, 4);
+ }
+ pesp_dest->recv_callback = pesp_source->recv_callback;
+ pesp_dest->sent_callback = pesp_source->sent_callback;
+ pesp_dest->link_cnt = pesp_source->link_cnt;
+ pesp_dest->reverse = pesp_source->reverse;
+}
+
+/******************************************************************************
+ * FunctionName : espconn_copy_partial
+ * Description : insert the node to the active connection list
+ * Parameters : arg -- Additional argument to pass to the callback function
+ * Returns : none
+*******************************************************************************/
+void ICACHE_FLASH_ATTR espconn_list_creat(espconn_msg **phead, espconn_msg* pinsert)
+{
+ espconn_msg *plist = NULL;
+// espconn_msg *ptest = NULL;
+ if (*phead == NULL)
+ *phead = pinsert;
+ else {
+ plist = *phead;
+ while (plist->pnext != NULL) {
+ plist = plist->pnext;
+ }
+ plist->pnext = pinsert;
+ }
+ pinsert->pnext = NULL;
+
+/* ptest = *phead;
+ while(ptest != NULL){
+ os_printf("espconn_list_creat %p\n", ptest);
+ ptest = ptest->pnext;
+ }*/
+}
+
+/******************************************************************************
+ * FunctionName : espconn_list_delete
+ * Description : remove the node from the active connection list
+ * Parameters : arg -- Additional argument to pass to the callback function
+ * Returns : none
+*******************************************************************************/
+void ICACHE_FLASH_ATTR espconn_list_delete(espconn_msg **phead, espconn_msg* pdelete)
+{
+ espconn_msg *plist = NULL;
+// espconn_msg *ptest = NULL;
+ plist = *phead;
+ if (plist == NULL){
+ *phead = NULL;
+ } else {
+ if (plist == pdelete){
+ *phead = plist->pnext;
+ } else {
+ while (plist != NULL) {
+ if (plist->pnext == pdelete){
+ plist->pnext = pdelete->pnext;
+ }
+ plist = plist->pnext;
+ }
+ }
+ }
+/* ptest = *phead;
+ while(ptest != NULL){
+ os_printf("espconn_list_delete %p\n", ptest);
+ ptest = ptest->pnext;
+ }*/
+}
+
+/******************************************************************************
+ * FunctionName : espconn_find_connection
+ * Description : Initialize the server: set up a listening PCB and bind it to
+ * the defined port
+ * Parameters : espconn -- the espconn used to build server
+ * Returns : none
+ *******************************************************************************/
+bool ICACHE_FLASH_ATTR espconn_find_connection(struct espconn *pespconn, espconn_msg **pnode)
+{
+ espconn_msg *plist = NULL;
+ struct ip_addr ip_remot;
+ struct ip_addr ip_list;
+ plist = plink_active;
+
+ while(plist != NULL){
+ if (pespconn == plist->pespconn){
+ *pnode = plist;
+ return true;
+ } else {
+ IP4_ADDR(&ip_remot, pespconn->proto.tcp->remote_ip[0], pespconn->proto.tcp->remote_ip[1],
+ pespconn->proto.tcp->remote_ip[2], pespconn->proto.tcp->remote_ip[3]);
+
+ IP4_ADDR(&ip_list, plist->pcommon.remote_ip[0], plist->pcommon.remote_ip[1],
+ plist->pcommon.remote_ip[2], plist->pcommon.remote_ip[3]);
+ if ((ip_list.addr == ip_remot.addr) && (pespconn->proto.tcp->remote_port == plist->pcommon.remote_port)){
+ *pnode = plist;
+ return true;
+ }
+ }
+ plist = plist ->pnext;
+ }
+ return false;
+}
+
+/******************************************************************************
+ * FunctionName : espconn_connect
+ * Description : The function given as the connect
+ * Parameters : espconn -- the espconn used to listen the connection
+ * Returns : none
+*******************************************************************************/
+sint8 ICACHE_FLASH_ATTR
+espconn_connect(struct espconn *espconn)
+{
+ struct ip_addr ipaddr;
+ struct ip_info ipinfo;
+ uint8 connect_status = 0;
+ sint8 value = ESPCONN_OK;
+ espconn_msg *plist = NULL;
+
+ if (espconn == NULL) {
+ return ESPCONN_ARG;
+ } else if (espconn ->type != ESPCONN_TCP)
+ return ESPCONN_ARG;
+
+ if (wifi_get_opmode() == ESPCONN_STA){
+ wifi_get_ip_info(STA_NETIF,&ipinfo);
+ if (ipinfo.ip.addr == 0){
+ return ESPCONN_RTE;
+ }
+ } else if(wifi_get_opmode() == ESPCONN_AP){
+ wifi_get_ip_info(AP_NETIF,&ipinfo);
+ if (ipinfo.ip.addr == 0){
+ return ESPCONN_RTE;
+ }
+ } else if(wifi_get_opmode() == ESPCONN_AP_STA){
+ IP4_ADDR(&ipaddr, espconn->proto.tcp->remote_ip[0],
+ espconn->proto.tcp->remote_ip[1],
+ espconn->proto.tcp->remote_ip[2],
+ espconn->proto.tcp->remote_ip[3]);
+ ipaddr.addr <<= 8;
+ wifi_get_ip_info(AP_NETIF,&ipinfo);
+ ipinfo.ip.addr <<= 8;
+ espconn_printf("softap_addr = %x, remote_addr = %x\n", ipinfo.ip.addr, ipaddr.addr);
+
+ if (ipaddr.addr != ipinfo.ip.addr){
+ connect_status = wifi_station_get_connect_status();
+ if (connect_status == STATION_GOT_IP){
+ wifi_get_ip_info(STA_NETIF,&ipinfo);
+ if (ipinfo.ip.addr == 0)
+ return ESPCONN_RTE;
+ } else {
+ return connect_status;
+ }
+ }
+ }
+
+ for (plist = plink_active; plist != NULL; plist = plist->pnext){
+ if (plist->pespconn->type == ESPCONN_TCP){
+ if (espconn->proto.tcp->local_port == plist->pespconn->proto.tcp->local_port){
+ return ESPCONN_ISCONN;
+ }
+ }
+ }
+
+ value = espconn_tcp_client(espconn);
+
+ return value;
+}
+
+/******************************************************************************
+ * FunctionName : espconn_create
+ * Description : sent data for client or server
+ * Parameters : espconn -- espconn to the data transmission
+ * Returns : result
+*******************************************************************************/
+sint8 ICACHE_FLASH_ATTR
+espconn_create(struct espconn *espconn)
+{
+ sint8 value = ESPCONN_OK;
+ espconn_msg *plist = NULL;
+
+ if (espconn == NULL) {
+ return ESPCONN_ARG;
+ } else if (espconn ->type != ESPCONN_UDP){
+ return ESPCONN_ARG;
+ }
+
+ for (plist = plink_active; plist != NULL; plist = plist->pnext){
+ if (plist->pespconn->type == ESPCONN_UDP){
+ if (espconn->proto.udp->local_port == plist->pespconn->proto.udp->local_port){
+ return ESPCONN_ISCONN;
+ }
+ }
+ }
+
+ value = espconn_udp_server(espconn);
+
+ return value;
+}
+
+/******************************************************************************
+ * FunctionName : espconn_sent
+ * Description : sent data for client or server
+ * Parameters : espconn -- espconn to set for client or server
+ * psent -- data to send
+ * length -- length of data to send
+ * Returns : none
+*******************************************************************************/
+sint8 ICACHE_FLASH_ATTR
+espconn_sent(struct espconn *espconn, uint8 *psent, uint16 length)
+{
+ espconn_msg *pnode = NULL;
+ bool value = false;
+ if (espconn == NULL) {
+ return ESPCONN_ARG;
+ }
+ espconn ->state = ESPCONN_WRITE;
+ value = espconn_find_connection(espconn, &pnode);
+ switch (espconn ->type) {
+ case ESPCONN_TCP:
+ if (value && (pnode->pcommon.write_len == pnode->pcommon.write_total)){
+ espconn_tcp_sent(pnode, psent, length);
+ }else
+ return ESPCONN_ARG;
+ break;
+
+ case ESPCONN_UDP: {
+ if (value)
+ espconn_udp_sent(pnode, psent, length);
+ else
+ return ESPCONN_ARG;
+ break;
+ }
+
+ default :
+ break;
+ }
+ return ESPCONN_OK;
+}
+
+/******************************************************************************
+ * FunctionName : espconn_tcp_get_max_con
+ * Description : get the number of simulatenously active TCP connections
+ * Parameters : espconn -- espconn to set the connect callback
+ * Returns : none
+*******************************************************************************/
+uint8 ICACHE_FLASH_ATTR espconn_tcp_get_max_con(void)
+{
+ uint8 tcp_num = 0;
+
+ tcp_num = MEMP_NUM_TCP_PCB;
+
+ return tcp_num;
+}
+
+/******************************************************************************
+ * FunctionName : espconn_tcp_set_max_con
+ * Description : set the number of simulatenously active TCP connections
+ * Parameters : espconn -- espconn to set the connect callback
+ * Returns : none
+*******************************************************************************/
+sint8 ICACHE_FLASH_ATTR espconn_tcp_set_max_con(uint8 num)
+{
+ if (num == 0)
+ return ESPCONN_ARG;
+
+ MEMP_NUM_TCP_PCB = num;
+ return ESPCONN_OK;
+}
+
+/******************************************************************************
+ * FunctionName : espconn_tcp_get_max_con_allow
+ * Description : get the count of simulatenously active connections on the server
+ * Parameters : espconn -- espconn to get the count
+ * Returns : result
+*******************************************************************************/
+sint8 ICACHE_FLASH_ATTR espconn_tcp_get_max_con_allow(struct espconn *espconn)
+{
+ espconn_msg *pget_msg = NULL;
+ if ((espconn == NULL) || (espconn->type == ESPCONN_UDP))
+ return ESPCONN_ARG;
+
+ pget_msg = pserver_list;
+ while (pget_msg != NULL){
+ if (pget_msg->pespconn == espconn){
+ return pget_msg->count_opt;
+ }
+ pget_msg = pget_msg->pnext;
+ }
+ return ESPCONN_ARG;
+}
+
+/******************************************************************************
+ * FunctionName : espconn_tcp_set_max_con_allow
+ * Description : set the count of simulatenously active connections on the server
+ * Parameters : espconn -- espconn to set the count
+ * Returns : result
+*******************************************************************************/
+sint8 ICACHE_FLASH_ATTR espconn_tcp_set_max_con_allow(struct espconn *espconn, uint8 num)
+{
+ espconn_msg *pset_msg = NULL;
+ if ((espconn == NULL) || (num > MEMP_NUM_TCP_PCB) || (espconn->type == ESPCONN_UDP))
+ return ESPCONN_ARG;
+
+ pset_msg = pserver_list;
+ while (pset_msg != NULL){
+ if (pset_msg->pespconn == espconn){
+ pset_msg->count_opt = num;
+ return ESPCONN_OK;
+ }
+ pset_msg = pset_msg->pnext;
+ }
+ return ESPCONN_ARG;
+}
+
+/******************************************************************************
+ * FunctionName : espconn_regist_sentcb
+ * Description : Used to specify the function that should be called when data
+ * has been successfully delivered to the remote host.
+ * Parameters : espconn -- espconn to set the sent callback
+ * sent_cb -- sent callback function to call for this espconn
+ * when data is successfully sent
+ * Returns : none
+*******************************************************************************/
+sint8 ICACHE_FLASH_ATTR
+espconn_regist_sentcb(struct espconn *espconn, espconn_sent_callback sent_cb)
+{
+ if (espconn == NULL) {
+ return ESPCONN_ARG;
+ }
+
+ espconn ->sent_callback = sent_cb;
+ return ESPCONN_OK;
+}
+
+/******************************************************************************
+ * FunctionName : espconn_regist_connectcb
+ * Description : used to specify the function that should be called when
+ * connects to host.
+ * Parameters : espconn -- espconn to set the connect callback
+ * connect_cb -- connected callback function to call when connected
+ * Returns : none
+*******************************************************************************/
+sint8 ICACHE_FLASH_ATTR
+espconn_regist_connectcb(struct espconn *espconn, espconn_connect_callback connect_cb)
+{
+ if (espconn == NULL) {
+ return ESPCONN_ARG;
+ }
+
+ espconn->proto.tcp->connect_callback = connect_cb;
+ return ESPCONN_OK;
+}
+
+/******************************************************************************
+ * FunctionName : espconn_regist_recvcb
+ * Description : used to specify the function that should be called when recv
+ * data from host.
+ * Parameters : espconn -- espconn to set the recv callback
+ * recv_cb -- recv callback function to call when recv data
+ * Returns : none
+*******************************************************************************/
+sint8 ICACHE_FLASH_ATTR
+espconn_regist_recvcb(struct espconn *espconn, espconn_recv_callback recv_cb)
+{
+ if (espconn == NULL) {
+ return ESPCONN_ARG;
+ }
+
+ espconn ->recv_callback = recv_cb;
+ return ESPCONN_OK;
+}
+
+/******************************************************************************
+ * FunctionName : espconn_regist_reconcb
+ * Description : used to specify the function that should be called when connection
+ * because of err disconnect.
+ * Parameters : espconn -- espconn to set the err callback
+ * recon_cb -- err callback function to call when err
+ * Returns : none
+*******************************************************************************/
+sint8 ICACHE_FLASH_ATTR
+espconn_regist_reconcb(struct espconn *espconn, espconn_reconnect_callback recon_cb)
+{
+ if (espconn == NULL) {
+ return ESPCONN_ARG;
+ }
+
+ espconn ->proto.tcp->reconnect_callback = recon_cb;
+ return ESPCONN_OK;
+}
+
+/******************************************************************************
+ * FunctionName : espconn_regist_disconcb
+ * Description : used to specify the function that should be called when disconnect
+ * Parameters : espconn -- espconn to set the err callback
+ * discon_cb -- err callback function to call when err
+ * Returns : none
+*******************************************************************************/
+sint8 ICACHE_FLASH_ATTR
+espconn_regist_disconcb(struct espconn *espconn, espconn_connect_callback discon_cb)
+{
+ if (espconn == NULL) {
+ return ESPCONN_ARG;
+ }
+
+ espconn ->proto.tcp->disconnect_callback = discon_cb;
+ return ESPCONN_OK;
+}
+
+/******************************************************************************
+ * FunctionName : espconn_get_connection_info
+ * Description : used to specify the function that should be called when disconnect
+ * Parameters : espconn -- espconn to set the err callback
+ * discon_cb -- err callback function to call when err
+ * Returns : none
+*******************************************************************************/
+sint8 ICACHE_FLASH_ATTR
+espconn_get_connection_info(struct espconn *pespconn, remot_info **pcon_info, uint8 typeflags)
+{
+ espconn_msg *plist = NULL;
+
+ if (pespconn == NULL)
+ return ESPCONN_ARG;
+
+ os_memset(premot, 0, sizeof(premot));
+ pespconn->link_cnt = 0;
+ plist = plink_active;
+ switch (pespconn->type){
+ case ESPCONN_TCP:
+ while(plist != NULL){
+ if ((plist->pespconn->type == ESPCONN_TCP) && (plist->preverse == pespconn)){
+ switch (typeflags){
+ case ESPCONN_SSL:
+ if (plist->pssl != NULL){
+ premot[pespconn->link_cnt].state = plist->pespconn->state;
+ premot[pespconn->link_cnt].remote_port = plist->pcommon.remote_port;
+ os_memcpy(premot[pespconn->link_cnt].remote_ip, plist->pcommon.remote_ip, 4);
+ pespconn->link_cnt ++;
+ }
+ break;
+ case ESPCONN_NORM:
+ if (plist->pssl == NULL){
+ premot[pespconn->link_cnt].state = plist->pespconn->state;
+ premot[pespconn->link_cnt].remote_port = plist->pcommon.remote_port;
+ os_memcpy(premot[pespconn->link_cnt].remote_ip, plist->pcommon.remote_ip, 4);
+ pespconn->link_cnt ++;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ plist = plist->pnext;
+ }
+
+ break;
+ case ESPCONN_UDP:
+ while(plist != NULL){
+ if (plist->pespconn->type == ESPCONN_UDP){
+ premot[pespconn->link_cnt].state = plist->pespconn->state;
+ premot[pespconn->link_cnt].remote_port = plist->pcommon.remote_port;
+ os_memcpy(premot[pespconn->link_cnt].remote_ip, plist->pcommon.remote_ip, 4);
+ pespconn->link_cnt ++;
+ }
+ plist = plist->pnext;
+ }
+ break;
+ default:
+ break;
+ }
+ *pcon_info = premot;
+ return ESPCONN_OK;
+}
+
+/******************************************************************************
+ * FunctionName : espconn_accept
+ * Description : The function given as the listen
+ * Parameters : espconn -- the espconn used to listen the connection
+ * Returns :
+*******************************************************************************/
+sint8 ICACHE_FLASH_ATTR
+espconn_accept(struct espconn *espconn)
+{
+ sint8 value = ESPCONN_OK;
+ espconn_msg *plist = NULL;
+
+ if (espconn == NULL) {
+ return ESPCONN_ARG;
+ } else if (espconn ->type != ESPCONN_TCP)
+ return ESPCONN_ARG;
+
+ for (plist = plink_active; plist != NULL; plist = plist->pnext){
+ if (plist->pespconn->type == ESPCONN_TCP){
+ if (espconn->proto.tcp->local_port == plist->pespconn->proto.tcp->local_port){
+ return ESPCONN_ISCONN;
+ }
+ }
+ }
+ value = espconn_tcp_server(espconn);
+
+ return value;
+}
+
+/******************************************************************************
+ * FunctionName : espconn_regist_time
+ * Description : used to specify the time that should be called when don't recv data
+ * Parameters : espconn -- the espconn used to the connection
+ * interval -- the timer when don't recv data
+ * Returns : none
+*******************************************************************************/
+sint8 ICACHE_FLASH_ATTR espconn_regist_time(struct espconn *espconn, uint32 interval, uint8 type_flag)
+{
+ espconn_msg *pnode = NULL;
+ bool value = false;
+ if ((espconn == NULL) || (type_flag > 0x01))
+ return ESPCONN_ARG;
+
+ if (type_flag == 0x01){
+ value = espconn_find_connection(espconn, &pnode);
+ if (value){
+ pnode->pcommon.timeout = interval;
+ return ESPCONN_OK;
+ } else
+ return ESPCONN_ARG;
+ } else {
+ link_timer = interval;
+ os_printf("espconn_regist_time %d\n", link_timer);
+ return ESPCONN_OK;
+ }
+}
+
+/******************************************************************************
+ * FunctionName : espconn_disconnect
+ * Description : disconnect with host
+ * Parameters : espconn -- the espconn used to disconnect the connection
+ * Returns : none
+*******************************************************************************/
+sint8 ICACHE_FLASH_ATTR
+espconn_disconnect(struct espconn *espconn)
+{
+ espconn_msg *pnode = NULL;
+ bool value = false;
+
+ if (espconn == NULL) {
+ return ESPCONN_ARG;;
+ } else if (espconn ->type != ESPCONN_TCP)
+ return ESPCONN_ARG;
+
+ value = espconn_find_connection(espconn, &pnode);
+
+ if (value){
+ espconn_tcp_disconnect(pnode);
+ return ESPCONN_OK;
+ } else
+ return ESPCONN_ARG;
+}
+
+/******************************************************************************
+ * FunctionName : espconn_set_opt
+ * Description : access port value for client so that we don't end up bouncing
+ * all connections at the same time .
+ * Parameters : none
+ * Returns : access port value
+*******************************************************************************/
+sint8 ICACHE_FLASH_ATTR
+espconn_set_opt(struct espconn *espconn, uint8 opt)
+{
+ espconn_msg *pnode = NULL;
+ bool value = false;
+
+ if (espconn == NULL || opt > ESPCONN_END) {
+ return ESPCONN_ARG;;
+ } else if (espconn->type != ESPCONN_TCP)
+ return ESPCONN_ARG;
+
+ value = espconn_find_connection(espconn, &pnode);
+ if (value) {
+ pnode->pcommon.espconn_opt = opt;
+ return ESPCONN_OK;
+ } else
+ return ESPCONN_ARG;
+}
+
+/******************************************************************************
+ * FunctionName : espconn_delete
+ * Description : disconnect with host
+ * Parameters : espconn -- the espconn used to disconnect the connection
+ * Returns : none
+*******************************************************************************/
+sint8 ICACHE_FLASH_ATTR
+espconn_delete(struct espconn *espconn)
+{
+ espconn_msg *pnode = NULL;
+ bool value = false;
+
+ if (espconn == NULL) {
+ return ESPCONN_ARG;
+ } else if (espconn ->type != ESPCONN_UDP)
+ return espconn_tcp_delete(espconn);
+
+ value = espconn_find_connection(espconn, &pnode);
+
+ if (value){
+ espconn_udp_disconnect(pnode);
+ return ESPCONN_OK;
+ } else
+ return ESPCONN_ARG;
+}
+
+/******************************************************************************
+ * FunctionName : espconn_port
+ * Description : access port value for client so that we don't end up bouncing
+ * all connections at the same time .
+ * Parameters : none
+ * Returns : access port value
+*******************************************************************************/
+uint32 espconn_port(void)
+{
+ uint32 port = 0;
+ static uint32 randnum = 0;
+
+ do {
+ port = system_get_time();
+
+ if (port < 0) {
+ port = os_random() - port;
+ }
+
+ port %= 0xc350;
+
+ if (port < 0x400) {
+ port += 0x400;
+ }
+
+ } while (port == randnum);
+
+ randnum = port;
+
+ return port;
+}
+
+/******************************************************************************
+ * FunctionName : espconn_gethostbyname
+ * Description : Resolve a hostname (string) into an IP address.
+ * Parameters : pespconn -- espconn to resolve a hostname
+ * hostname -- the hostname that is to be queried
+ * addr -- pointer to a ip_addr_t where to store the address if
+ * it is already cached in the dns_table (only valid if
+ * ESPCONN_OK is returned!)
+ * found -- a callback function to be called on success, failure
+ * or timeout (only if ERR_INPROGRESS is returned!)
+ * Returns : err_t return code
+ * - ESPCONN_OK if hostname is a valid IP address string or the host
+ * name is already in the local names table.
+ * - ESPCONN_INPROGRESS enqueue a request to be sent to the DNS server
+ * for resolution if no errors are present.
+ * - ESPCONN_ARG: dns client not initialized or invalid hostname
+*******************************************************************************/
+err_t ICACHE_FLASH_ATTR
+espconn_gethostbyname(struct espconn *pespconn, const char *hostname, ip_addr_t *addr, dns_found_callback found)
+{
+ return dns_gethostbyname(hostname, addr, found, pespconn);
+}
+
diff --git a/app/lwip/app/espconn_tcp.c b/app/lwip/app/espconn_tcp.c
new file mode 100644
index 00000000..df92929a
--- /dev/null
+++ b/app/lwip/app/espconn_tcp.c
@@ -0,0 +1,954 @@
+/******************************************************************************
+ * Copyright 2013-2014 Espressif Systems (Wuxi)
+ *
+ * FileName: espconn_tcp.c
+ *
+ * Description: tcp proto interface
+ *
+ * Modification history:
+ * 2014/3/31, v1.0 create this file.
+*******************************************************************************/
+
+#include "lwip/netif.h"
+#include "lwip/inet.h"
+#include "netif/etharp.h"
+#include "lwip/tcp.h"
+#include "lwip/ip.h"
+#include "lwip/init.h"
+#include "lwip/tcp_impl.h"
+#include "lwip/memp.h"
+
+#include "ets_sys.h"
+#include "os_type.h"
+//#include "os.h"
+#include "lwip/mem.h"
+#include "lwip/app/espconn_tcp.h"
+
+extern espconn_msg *plink_active;
+extern uint32 link_timer;
+extern espconn_msg *pserver_list;
+
+static err_t
+espconn_client_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err);
+static void
+espconn_client_close(void *arg, struct tcp_pcb *pcb);
+
+static err_t
+espconn_server_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err);
+static void
+espconn_server_close(void *arg, struct tcp_pcb *pcb);
+
+///////////////////////////////common function/////////////////////////////////
+
+/******************************************************************************
+ * FunctionName : espconn_tcp_reconnect
+ * Description : reconnect with host
+ * Parameters : arg -- Additional argument to pass to the callback function
+ * Returns : none
+*******************************************************************************/
+static void ICACHE_FLASH_ATTR
+espconn_tcp_reconnect(void *arg)
+{
+ espconn_msg *precon_cb = arg;
+ sint8 re_err = 0;
+ if (precon_cb != NULL) {
+ struct espconn *espconn = precon_cb->preverse;
+ re_err = precon_cb->pcommon.err;
+ if (precon_cb->pespconn != NULL){
+ if (espconn != NULL){
+ if (precon_cb->pespconn->proto.tcp != NULL){
+ espconn_copy_partial(espconn, precon_cb->pespconn);
+ espconn_printf("server: %d.%d.%d.%d : %d reconnection\n", espconn->proto.tcp->remote_ip[0],
+ espconn->proto.tcp->remote_ip[1],espconn->proto.tcp->remote_ip[2],
+ espconn->proto.tcp->remote_ip[3],espconn->proto.tcp->remote_port);
+ os_free(precon_cb->pespconn->proto.tcp);
+ precon_cb->pespconn->proto.tcp = NULL;
+ }
+ os_free(precon_cb->pespconn);
+ precon_cb->pespconn = NULL;
+ } else {
+ espconn = precon_cb->pespconn;
+ espconn_printf("client: %d.%d.%d.%d : %d reconnection\n", espconn->proto.tcp->local_ip[0],
+ espconn->proto.tcp->local_ip[1],espconn->proto.tcp->local_ip[2],
+ espconn->proto.tcp->local_ip[3],espconn->proto.tcp->local_port);
+ }
+ }
+ os_free(precon_cb);
+ precon_cb = NULL;
+
+ if (espconn->proto.tcp->reconnect_callback != NULL) {
+ espconn->proto.tcp->reconnect_callback(espconn, re_err);
+ }
+ } else {
+ espconn_printf("espconn_tcp_reconnect err\n");
+ }
+}
+
+/******************************************************************************
+ * FunctionName : espconn_tcp_disconnect
+ * Description : disconnect with host
+ * Parameters : arg -- Additional argument to pass to the callback function
+ * Returns : none
+*******************************************************************************/
+static void ICACHE_FLASH_ATTR
+espconn_tcp_disconnect_successful(void *arg)
+{
+ espconn_msg *pdiscon_cb = arg;
+ sint8 dis_err = 0;
+ if (pdiscon_cb != NULL) {
+ struct espconn *espconn = pdiscon_cb->preverse;
+
+ dis_err = pdiscon_cb->pcommon.err;
+ if (pdiscon_cb->pespconn != NULL){
+ struct tcp_pcb *pcb = NULL;
+ if (espconn != NULL){
+ if (pdiscon_cb->pespconn->proto.tcp != NULL){
+ espconn_copy_partial(espconn, pdiscon_cb->pespconn);
+ espconn_printf("server: %d.%d.%d.%d : %d disconnect\n", espconn->proto.tcp->remote_ip[0],
+ espconn->proto.tcp->remote_ip[1],espconn->proto.tcp->remote_ip[2],
+ espconn->proto.tcp->remote_ip[3],espconn->proto.tcp->remote_port);
+ os_free(pdiscon_cb->pespconn->proto.tcp);
+ pdiscon_cb->pespconn->proto.tcp = NULL;
+ }
+ os_free(pdiscon_cb->pespconn);
+ pdiscon_cb->pespconn = NULL;
+ } else {
+ espconn = pdiscon_cb->pespconn;
+ espconn_printf("client: %d.%d.%d.%d : %d disconnect\n", espconn->proto.tcp->local_ip[0],
+ espconn->proto.tcp->local_ip[1],espconn->proto.tcp->local_ip[2],
+ espconn->proto.tcp->local_ip[3],espconn->proto.tcp->local_port);
+ }
+ pcb = pdiscon_cb->pcommon.pcb;
+ tcp_arg(pcb, NULL);
+ tcp_err(pcb, NULL);
+ /*delete TIME_WAIT State pcb after 2MSL time,for not all data received by application.*/
+ if (pdiscon_cb->pcommon.espconn_opt == ESPCONN_REUSEADDR){
+ if (pcb->state == TIME_WAIT){
+ tcp_pcb_remove(&tcp_tw_pcbs,pcb);
+ memp_free(MEMP_TCP_PCB,pcb);
+ }
+ }
+ }
+ os_free(pdiscon_cb);
+ pdiscon_cb = NULL;
+
+ if (espconn->proto.tcp->disconnect_callback != NULL) {
+ espconn->proto.tcp->disconnect_callback(espconn);
+ }
+ } else {
+ espconn_printf("espconn_tcp_disconnect err\n");
+ }
+}
+
+/******************************************************************************
+ * FunctionName : espconn_tcp_sent
+ * Description : sent data for client or server
+ * Parameters : void *arg -- client or server to send
+ * uint8* psent -- Data to send
+ * uint16 length -- Length of data to send
+ * Returns : none
+*******************************************************************************/
+void ICACHE_FLASH_ATTR
+espconn_tcp_sent(void *arg, uint8 *psent, uint16 length)
+{
+ espconn_msg *ptcp_sent = arg;
+ struct tcp_pcb *pcb = NULL;
+ err_t err = 0;
+ u16_t len = 0;
+ u8_t data_to_send = false;
+
+ espconn_printf("espconn_tcp_sent ptcp_sent %p psent %p length %d\n", ptcp_sent, psent, length);
+
+ if (ptcp_sent == NULL || psent == NULL || length == 0) {
+ return;
+ }
+
+ pcb = ptcp_sent->pcommon.pcb;
+ if (tcp_sndbuf(pcb) < length) {
+ len = tcp_sndbuf(pcb);
+ } else {
+ len = length;
+ LWIP_ASSERT("length did not fit into uint16!", (len == length));
+ }
+
+ if (len > (2*pcb->mss)) {
+ len = 2*pcb->mss;
+ }
+
+ do {
+ espconn_printf("espconn_tcp_sent writing %d bytes %p\n", len, pcb);
+ err = tcp_write(pcb, psent, len, 0);
+
+ if (err == ERR_MEM) {
+ len /= 2;
+ }
+ } while (err == ERR_MEM && len > 1);
+
+ if (err == ERR_OK) {
+ data_to_send = true;
+ ptcp_sent->pcommon.ptrbuf = psent + len;
+ ptcp_sent->pcommon.cntr = length - len;
+ ptcp_sent->pcommon.write_len = len;
+ espconn_printf("espconn_tcp_sent sending %d bytes, remain %d\n", len, ptcp_sent->pcommon.cntr);
+ }
+
+ if (data_to_send == true) {
+ err = tcp_output(pcb);
+ } else {
+ ptcp_sent->pespconn ->state = ESPCONN_CLOSE;
+ }
+}
+
+/******************************************************************************
+ * FunctionName : espconn_close
+ * Description : The connection has been successfully closed.
+ * Parameters : arg -- Additional argument to pass to the callback function
+ * Returns : none
+*******************************************************************************/
+void ICACHE_FLASH_ATTR espconn_tcp_disconnect(espconn_msg *pdiscon)
+{
+ if (pdiscon != NULL){
+ if (pdiscon->preverse != NULL)
+ espconn_server_close(pdiscon, pdiscon->pcommon.pcb);
+ else
+ espconn_client_close(pdiscon, pdiscon->pcommon.pcb);
+ } else{
+ espconn_printf("espconn_server_disconnect err.\n");
+ }
+}
+
+///////////////////////////////client function/////////////////////////////////
+/******************************************************************************
+ * FunctionName : espconn_close
+ * Description : The connection has been successfully closed.
+ * Parameters : arg -- Additional argument to pass to the callback function
+ * Returns : none
+*******************************************************************************/
+static void ICACHE_FLASH_ATTR
+espconn_cclose_cb(void *arg)
+{
+ espconn_msg *pclose_cb = arg;
+
+ if (pclose_cb == NULL) {
+ return;
+ }
+
+ struct tcp_pcb *pcb = pclose_cb->pcommon.pcb;
+
+ espconn_printf("espconn_close %d %d\n", pcb->state, pcb->nrtx);
+
+ if (pcb->state == TIME_WAIT || pcb->state == CLOSED) {
+ pclose_cb ->pespconn ->state = ESPCONN_CLOSE;
+ /*remove the node from the client's active connection list*/
+ espconn_list_delete(&plink_active, pclose_cb);
+ espconn_tcp_disconnect_successful((void*)pclose_cb);
+ } else {
+ os_timer_arm(&pclose_cb->pcommon.ptimer, TCP_FAST_INTERVAL, 0);
+ }
+}
+
+/******************************************************************************
+ * FunctionName : espconn_client_close
+ * Description : The connection shall be actively closed.
+ * Parameters : pcb -- Additional argument to pass to the callback function
+ * pcb -- the pcb to close
+ * Returns : none
+*******************************************************************************/
+static void ICACHE_FLASH_ATTR
+espconn_client_close(void *arg, struct tcp_pcb *pcb)
+{
+ err_t err;
+ espconn_msg *pclose = arg;
+
+ os_timer_disarm(&pclose->pcommon.ptimer);
+
+ tcp_recv(pcb, NULL);
+ err = tcp_close(pcb);
+ os_timer_setfn(&pclose->pcommon.ptimer, espconn_cclose_cb, pclose);
+ os_timer_arm(&pclose->pcommon.ptimer, TCP_FAST_INTERVAL, 0);
+
+ if (err != ERR_OK) {
+ /* closing failed, try again later */
+ tcp_recv(pcb, espconn_client_recv);
+ } else {
+ /* closing succeeded */
+ tcp_sent(pcb, NULL);
+ }
+}
+
+/******************************************************************************
+ * FunctionName : espconn_client_recv
+ * Description : Data has been received on this pcb.
+ * Parameters : arg -- Additional argument to pass to the callback function
+ * pcb -- The connection pcb which received data
+ * p -- The received data (or NULL when the connection has been closed!)
+ * err -- An error code if there has been an error receiving
+ * Returns : ERR_ABRT: if you have called tcp_abort from within the function!
+*******************************************************************************/
+static err_t ICACHE_FLASH_ATTR
+espconn_client_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
+{
+ espconn_msg *precv_cb = arg;
+
+ tcp_arg(pcb, arg);
+
+ if (p != NULL) {
+ tcp_recved(pcb, p ->tot_len);
+ }
+
+ if (err == ERR_OK && p != NULL) {
+ char *pdata = NULL;
+ u16_t length = 0;
+ pdata = (char *)os_zalloc(p ->tot_len + 1);
+ length = pbuf_copy_partial(p, pdata, p ->tot_len, 0);
+ pbuf_free(p);
+
+ if (length != 0) {
+ precv_cb->pespconn ->state = ESPCONN_READ;
+ precv_cb->pcommon.pcb = pcb;
+ if (precv_cb->pespconn->recv_callback != NULL) {
+ precv_cb->pespconn->recv_callback(precv_cb->pespconn, pdata, length);
+ }
+ precv_cb->pespconn ->state = ESPCONN_CONNECT;
+ }
+
+ os_free(pdata);
+ pdata = NULL;
+ }
+
+ if (err == ERR_OK && p == NULL) {
+ espconn_client_close(precv_cb, pcb);
+ }
+
+ return ERR_OK;
+}
+
+/******************************************************************************
+ * FunctionName : espconn_client_sent
+ * Description : Data has been sent and acknowledged by the remote host.
+ * This means that more data can be sent.
+ * Parameters : arg -- Additional argument to pass to the callback function
+ * pcb -- The connection pcb for which data has been acknowledged
+ * len -- The amount of bytes acknowledged
+ * Returns : ERR_OK: try to send some data by calling tcp_output
+ * ERR_ABRT: if you have called tcp_abort from within the function!
+*******************************************************************************/
+static err_t ICACHE_FLASH_ATTR
+espconn_client_sent(void *arg, struct tcp_pcb *pcb, u16_t len)
+{
+ espconn_msg *psent_cb = arg;
+
+ psent_cb->pcommon.pcb = pcb;
+ psent_cb->pcommon.write_total += len;
+ espconn_printf("espconn_client_sent sent %d %d\n", len, psent_cb->pcommon.write_total);
+ if (psent_cb->pcommon.write_total == psent_cb->pcommon.write_len){
+ psent_cb->pcommon.write_total = 0;
+ psent_cb->pcommon.write_len = 0;
+ if (psent_cb->pcommon.cntr == 0) {
+ psent_cb->pespconn->state = ESPCONN_CONNECT;
+
+ if (psent_cb->pespconn->sent_callback != NULL) {
+ psent_cb->pespconn->sent_callback(psent_cb->pespconn);
+ }
+ } else
+ espconn_tcp_sent(psent_cb, psent_cb->pcommon.ptrbuf, psent_cb->pcommon.cntr);
+ } else {
+
+ }
+ return ERR_OK;
+}
+
+/******************************************************************************
+ * FunctionName : espconn_client_poll
+ * Description : The poll function is called every 2nd second.
+ * If there has been no data sent (which resets the retries) in 8 seconds, close.
+ * If the last portion of a file has not been sent in 2 seconds, close.
+ *
+ * This could be increased, but we don't want to waste resources for bad connections.
+ * Parameters : arg -- Additional argument to pass to the callback function
+ * pcb -- The connection pcb for which data has been acknowledged
+ * Returns : ERR_OK: try to send some data by calling tcp_output
+ * ERR_ABRT: if you have called tcp_abort from within the function!
+*******************************************************************************/
+static err_t ICACHE_FLASH_ATTR
+espconn_client_poll(void *arg, struct tcp_pcb *pcb)
+{
+ espconn_msg *ppoll_cb = arg;
+ espconn_printf("espconn_client_poll pcb %p %p %d\n", pcb, arg, pcb->state);
+ if (arg == NULL) {
+ tcp_abandon(pcb, 0);
+ tcp_poll(pcb, NULL, 0);
+ return ERR_ABRT;
+ }
+ ppoll_cb->pcommon.pcb = pcb;
+ if (pcb->state == ESTABLISHED) {
+ } else {
+ tcp_poll(pcb, espconn_client_poll, 0);
+ espconn_client_close(ppoll_cb->pespconn, pcb);
+ }
+
+ return ERR_OK;
+}
+
+/******************************************************************************
+ * FunctionName : espconn_client_err
+ * Description : The pcb had an error and is already deallocated.
+ * The argument might still be valid (if != NULL).
+ * Parameters : arg -- Additional argument to pass to the callback function
+ * err -- Error code to indicate why the pcb has been closed
+ * Returns : none
+*******************************************************************************/
+static void ICACHE_FLASH_ATTR
+espconn_client_err(void *arg, err_t err)
+{
+ espconn_msg *perr_cb = arg;
+ struct tcp_pcb *pcb = NULL;
+ LWIP_UNUSED_ARG(err);
+
+ if (perr_cb != NULL) {
+ os_timer_disarm(&perr_cb->pcommon.ptimer);
+ pcb = perr_cb->pcommon.pcb;
+ perr_cb->pespconn->state = ESPCONN_CLOSE;
+ espconn_printf("espconn_client_err %d %d %d\n", pcb->state, pcb->nrtx, err);
+
+ /*remove the node from the client's active connection list*/
+ espconn_list_delete(&plink_active, perr_cb);
+
+ if (err == ERR_ABRT) {
+ switch (pcb->state) {
+ case SYN_SENT:
+ if (pcb->nrtx == TCP_SYNMAXRTX) {
+ perr_cb->pcommon.err = ESPCONN_CONN;
+ } else {
+ perr_cb->pcommon.err = err;
+ }
+
+ break;
+
+ case ESTABLISHED:
+ if (pcb->nrtx == TCP_MAXRTX) {
+ perr_cb->pcommon.err = ESPCONN_TIMEOUT;
+ } else {
+ perr_cb->pcommon.err = err;
+ }
+ break;
+
+ case FIN_WAIT_1:
+ if (pcb->nrtx == TCP_MAXRTX) {
+ perr_cb->pcommon.err = ESPCONN_CLSD;
+ } else {
+ perr_cb->pcommon.err = err;
+ }
+ break;
+ case FIN_WAIT_2:
+ perr_cb->pcommon.err = ESPCONN_CLSD;
+ break;
+ case CLOSED:
+ perr_cb->pcommon.err = ESPCONN_CONN;
+ break;
+ }
+ } else {
+ perr_cb->pcommon.err = err;
+ }
+ os_timer_setfn(&perr_cb->pcommon.ptimer,
+ (os_timer_func_t *) espconn_tcp_reconnect, perr_cb);
+ os_timer_arm(&perr_cb->pcommon.ptimer, 10, 0);
+ }
+}
+
+/******************************************************************************
+ * FunctionName : espconn_client_connect
+ * Description : A new incoming connection has been connected.
+ * Parameters : arg -- Additional argument to pass to the callback function
+ * tpcb -- The connection pcb which is connected
+ * err -- An unused error code, always ERR_OK currently
+ * Returns : connection result
+*******************************************************************************/
+static err_t ICACHE_FLASH_ATTR
+espconn_client_connect(void *arg, struct tcp_pcb *tpcb, err_t err)
+{
+ espconn_msg *pcon = arg;
+ espconn_printf("espconn_client_connect pcon %p tpcb %p\n", pcon, tpcb);
+ if (err == ERR_OK){
+ pcon->pespconn->state = ESPCONN_CONNECT;
+ pcon->pcommon.err = err;
+ pcon->pcommon.pcb = tpcb;
+ tcp_arg(tpcb, (void *)pcon);
+ tcp_sent(tpcb, espconn_client_sent);
+ tcp_recv(tpcb, espconn_client_recv);
+
+ //tcp_poll(tpcb, espconn_client_poll, 1);
+ if (pcon->pespconn->proto.tcp->connect_callback != NULL) {
+ pcon->pespconn->proto.tcp->connect_callback(pcon->pespconn);
+ }
+ } else{
+ os_printf("err in host connected (%s)\n",lwip_strerr(err));
+ }
+ return err;
+}
+
+/******************************************************************************
+ * FunctionName : espconn_tcp_client
+ * Description : Initialize the client: set up a connect PCB and bind it to
+ * the defined port
+ * Parameters : espconn -- the espconn used to build client
+ * Returns : none
+*******************************************************************************/
+sint8 ICACHE_FLASH_ATTR
+espconn_tcp_client(struct espconn *espconn)
+{
+ struct tcp_pcb *pcb = NULL;
+ struct ip_addr ipaddr;
+ espconn_msg *pclient = NULL;
+
+ pclient = (espconn_msg *)os_zalloc(sizeof(espconn_msg));
+ if (pclient == NULL){
+ return ESPCONN_MEM;
+ }
+
+ IP4_ADDR(&ipaddr, espconn->proto.tcp->remote_ip[0],
+ espconn->proto.tcp->remote_ip[1],
+ espconn->proto.tcp->remote_ip[2],
+ espconn->proto.tcp->remote_ip[3]);
+
+ pcb = tcp_new();
+
+ if (pcb == NULL) {
+// pclient ->pespconn ->state = ESPCONN_NONE;
+ os_free(pclient);
+ pclient = NULL;
+ return ESPCONN_MEM;
+ } else {
+ /*insert the node to the active connection list*/
+ espconn_list_creat(&plink_active, pclient);
+ tcp_arg(pcb, (void *)pclient);
+ tcp_err(pcb, espconn_client_err);
+ pclient->preverse = NULL;
+ pclient->pespconn = espconn;
+ pclient->pespconn->state = ESPCONN_WAIT;
+ pclient->pcommon.pcb = pcb;
+ tcp_bind(pcb, IP_ADDR_ANY, pclient->pespconn->proto.tcp->local_port);
+ pclient->pcommon.err = tcp_connect(pcb, &ipaddr,
+ pclient->pespconn->proto.tcp->remote_port, espconn_client_connect);
+ return pclient->pcommon.err;
+ }
+}
+
+///////////////////////////////server function/////////////////////////////////
+/******************************************************************************
+ * FunctionName : espconn_closed
+ * Description : The connection has been successfully closed.
+ * Parameters : arg -- Additional argument to pass to the callback function
+ * Returns : none
+*******************************************************************************/
+static void ICACHE_FLASH_ATTR
+espconn_sclose_cb(void *arg)
+{
+ espconn_msg *psclose_cb = arg;
+ if (psclose_cb == NULL) {
+ return;
+ }
+
+ struct tcp_pcb *pcb = psclose_cb ->pcommon.pcb;
+ espconn_printf("espconn_sclose_cb %d %d\n", pcb->state, pcb->nrtx);
+ if (pcb->state == CLOSED || pcb->state == TIME_WAIT) {
+ psclose_cb ->pespconn ->state = ESPCONN_CLOSE;
+ /*remove the node from the server's active connection list*/
+ espconn_list_delete(&plink_active, psclose_cb);
+ espconn_tcp_disconnect_successful(psclose_cb);
+ } else {
+ os_timer_arm(&psclose_cb->pcommon.ptimer, TCP_FAST_INTERVAL, 0);
+ }
+}
+
+/******************************************************************************
+ * FunctionName : espconn_server_close
+ * Description : The connection shall be actively closed.
+ * Parameters : arg -- Additional argument to pass to the callback function
+ * pcb -- the pcb to close
+ * Returns : none
+*******************************************************************************/
+static void ICACHE_FLASH_ATTR
+espconn_server_close(void *arg, struct tcp_pcb *pcb)
+{
+ err_t err;
+ espconn_msg *psclose = arg;
+
+ os_timer_disarm(&psclose->pcommon.ptimer);
+
+ tcp_recv(pcb, NULL);
+ err = tcp_close(pcb);
+ os_timer_setfn(&psclose->pcommon.ptimer, espconn_sclose_cb, psclose);
+ os_timer_arm(&psclose->pcommon.ptimer, TCP_FAST_INTERVAL, 0);
+
+ if (err != ERR_OK) {
+ /* closing failed, try again later */
+ tcp_recv(pcb, espconn_server_recv);
+ } else {
+ /* closing succeeded */
+ tcp_poll(pcb, NULL, 0);
+ tcp_sent(pcb, NULL);
+ }
+}
+
+/******************************************************************************
+ * FunctionName : espconn_server_recv
+ * Description : Data has been received on this pcb.
+ * Parameters : arg -- Additional argument to pass to the callback function
+ * pcb -- The connection pcb which received data
+ * p -- The received data (or NULL when the connection has been closed!)
+ * err -- An error code if there has been an error receiving
+ * Returns : ERR_ABRT: if you have called tcp_abort from within the function!
+*******************************************************************************/
+static err_t ICACHE_FLASH_ATTR
+espconn_server_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
+{
+ espconn_msg *precv_cb = arg;
+
+ tcp_arg(pcb, arg);
+ espconn_printf("server has application data received: %d\n", system_get_free_heap_size());
+ if (p != NULL) {
+ tcp_recved(pcb, p->tot_len);
+ }
+
+ if (err == ERR_OK && p != NULL) {
+ u8_t *data_ptr = NULL;
+ u32_t data_cntr = 0;
+ precv_cb->pcommon.recv_check = 0;
+ data_ptr = (u8_t *)os_zalloc(p ->tot_len + 1);
+ data_cntr = pbuf_copy_partial(p, data_ptr, p ->tot_len, 0);
+ pbuf_free(p);
+
+ if (data_cntr != 0) {
+ precv_cb->pespconn ->state = ESPCONN_READ;
+ precv_cb->pcommon.pcb = pcb;
+ if (precv_cb->pespconn->recv_callback != NULL) {
+ precv_cb->pespconn->recv_callback(precv_cb->pespconn, data_ptr, data_cntr);
+ }
+ precv_cb->pespconn ->state = ESPCONN_CONNECT;
+ }
+
+ os_free(data_ptr);
+ data_ptr = NULL;
+ espconn_printf("server's application data has been processed: %d\n", system_get_free_heap_size());
+ } else {
+ if (p != NULL) {
+ pbuf_free(p);
+ }
+
+ espconn_server_close(precv_cb, pcb);
+ }
+
+ return ERR_OK;
+}
+
+/******************************************************************************
+ * FunctionName : espconn_server_sent
+ * Description : Data has been sent and acknowledged by the remote host.
+ * This means that more data can be sent.
+ * Parameters : arg -- Additional argument to pass to the callback function
+ * pcb -- The connection pcb for which data has been acknowledged
+ * len -- The amount of bytes acknowledged
+ * Returns : ERR_OK: try to send some data by calling tcp_output
+ * ERR_ABRT: if you have called tcp_abort from within the function!
+*******************************************************************************/
+static err_t ICACHE_FLASH_ATTR
+espconn_server_sent(void *arg, struct tcp_pcb *pcb, u16_t len)
+{
+ espconn_msg *psent_cb = arg;
+
+ psent_cb->pcommon.pcb = pcb;
+ psent_cb->pcommon.recv_check = 0;
+ psent_cb->pcommon.write_total += len;
+ espconn_printf("espconn_server_sent sent %d %d\n", len, psent_cb->pcommon.write_total);
+ if (psent_cb->pcommon.write_total == psent_cb->pcommon.write_len){
+ psent_cb->pcommon.write_total = 0;
+ psent_cb->pcommon.write_len = 0;
+ if (psent_cb ->pcommon.cntr == 0) {
+ psent_cb ->pespconn ->state = ESPCONN_CONNECT;
+
+ if (psent_cb ->pespconn ->sent_callback != NULL) {
+ psent_cb ->pespconn ->sent_callback(psent_cb ->pespconn);
+ }
+ } else
+ espconn_tcp_sent(psent_cb, psent_cb ->pcommon.ptrbuf, psent_cb ->pcommon.cntr);
+ } else {
+
+ }
+ return ERR_OK;
+}
+
+/******************************************************************************
+ * FunctionName : espconn_server_poll
+ * Description : The poll function is called every 3nd second.
+ * If there has been no data sent (which resets the retries) in 3 seconds, close.
+ * If the last portion of a file has not been sent in 3 seconds, close.
+ *
+ * This could be increased, but we don't want to waste resources for bad connections.
+ * Parameters : arg -- Additional argument to pass to the callback function
+ * pcb -- The connection pcb for which data has been acknowledged
+ * Returns : ERR_OK: try to send some data by calling tcp_output
+ * ERR_ABRT: if you have called tcp_abort from within the function!
+*******************************************************************************/
+static err_t ICACHE_FLASH_ATTR
+espconn_server_poll(void *arg, struct tcp_pcb *pcb)
+{
+ espconn_msg *pspoll_cb = arg;
+
+ if (arg == NULL) {
+ tcp_abandon(pcb, 0);
+ tcp_poll(pcb, NULL, 0);
+ return ERR_ABRT;
+ }
+
+ espconn_printf("espconn_server_poll %d %d\n", pspoll_cb->pcommon.recv_check, pcb->state);
+ pspoll_cb->pcommon.pcb = pcb;
+ if (pcb->state == ESTABLISHED) {
+ pspoll_cb->pcommon.recv_check++;
+ if (pspoll_cb->pcommon.timeout != 0){
+ if (pspoll_cb->pcommon.recv_check == pspoll_cb->pcommon.timeout) {
+ pspoll_cb->pcommon.recv_check = 0;
+ espconn_server_close(pspoll_cb, pcb);
+ }
+ } else if (link_timer != 0){
+ if (pspoll_cb->pcommon.recv_check == link_timer) {
+ pspoll_cb->pcommon.recv_check = 0;
+ espconn_server_close(pspoll_cb, pcb);
+ }
+ } else {
+ if (pspoll_cb->pcommon.recv_check == 0x0a) {
+ pspoll_cb->pcommon.recv_check = 0;
+ espconn_server_close(pspoll_cb, pcb);
+ }
+ }
+ } else {
+ espconn_server_close(pspoll_cb, pcb);
+ }
+
+ return ERR_OK;
+}
+
+/******************************************************************************
+ * FunctionName : esponn_server_err
+ * Description : The pcb had an error and is already deallocated.
+ * The argument might still be valid (if != NULL).
+ * Parameters : arg -- Additional argument to pass to the callback function
+ * err -- Error code to indicate why the pcb has been closed
+ * Returns : none
+*******************************************************************************/
+static void ICACHE_FLASH_ATTR
+esponn_server_err(void *arg, err_t err)
+{
+ espconn_msg *pserr_cb = arg;
+ struct tcp_pcb *pcb = NULL;
+ if (pserr_cb != NULL) {
+ os_timer_disarm(&pserr_cb->pcommon.ptimer);
+ pcb = pserr_cb->pcommon.pcb;
+ pserr_cb->pespconn->state = ESPCONN_CLOSE;
+
+ /*remove the node from the server's active connection list*/
+ espconn_list_delete(&plink_active, pserr_cb);
+
+ if (err == ERR_ABRT) {
+ switch (pcb->state) {
+ case SYN_RCVD:
+ if (pcb->nrtx == TCP_SYNMAXRTX) {
+ pserr_cb->pcommon.err = ESPCONN_CONN;
+ } else {
+ pserr_cb->pcommon.err = err;
+ }
+
+ break;
+
+ case ESTABLISHED:
+ if (pcb->nrtx == TCP_MAXRTX) {
+ pserr_cb->pcommon.err = ESPCONN_TIMEOUT;
+ } else {
+ pserr_cb->pcommon.err = err;
+ }
+
+ break;
+
+ case CLOSE_WAIT:
+ if (pcb->nrtx == TCP_MAXRTX) {
+ pserr_cb->pcommon.err = ESPCONN_CLSD;
+ } else {
+ pserr_cb->pcommon.err = err;
+ }
+ break;
+ case LAST_ACK:
+ pserr_cb->pcommon.err = ESPCONN_CLSD;
+ break;
+
+ case CLOSED:
+ pserr_cb->pcommon.err = ESPCONN_CONN;
+ break;
+ default :
+ break;
+ }
+ } else {
+ pserr_cb->pcommon.err = err;
+ }
+
+ os_timer_setfn(&pserr_cb->pcommon.ptimer,
+ (os_timer_func_t *) espconn_tcp_reconnect, pserr_cb);
+ os_timer_arm(&pserr_cb->pcommon.ptimer, 10, 0);
+ }
+}
+
+/******************************************************************************
+ * FunctionName : espconn_tcp_accept
+ * Description : A new incoming connection has been accepted.
+ * Parameters : arg -- Additional argument to pass to the callback function
+ * pcb -- The connection pcb which is accepted
+ * err -- An unused error code, always ERR_OK currently
+ * Returns : acception result
+*******************************************************************************/
+static err_t ICACHE_FLASH_ATTR
+espconn_tcp_accept(void *arg, struct tcp_pcb *pcb, err_t err)
+{
+ struct espconn *espconn = arg;
+ espconn_msg *paccept = NULL;
+ remot_info *pinfo = NULL;
+ LWIP_UNUSED_ARG(err);
+
+ if(4096>system_get_free_heap_size()){
+ return ERR_MEM;
+ }
+
+ paccept = (espconn_msg *)os_zalloc(sizeof(espconn_msg));
+ tcp_arg(pcb, paccept);
+ tcp_err(pcb, esponn_server_err);
+ if (paccept == NULL)
+ return ERR_MEM;
+ /*insert the node to the active connection list*/
+ espconn_list_creat(&plink_active, paccept);
+
+ paccept->preverse = espconn;
+ paccept->pespconn = (struct espconn *)os_zalloc(sizeof(struct espconn));
+ if (paccept->pespconn == NULL)
+ return ERR_MEM;
+ paccept->pespconn->proto.tcp = (esp_tcp *)os_zalloc(sizeof(esp_tcp));
+ if (paccept->pespconn->proto.tcp == NULL)
+ return ERR_MEM;
+
+ //paccept->pcommon.timeout = 0x0a;
+ //link_timer = 0x0a;
+
+ paccept->pcommon.pcb = pcb;
+
+ paccept->pcommon.remote_port = pcb->remote_port;
+ paccept->pcommon.remote_ip[0] = ip4_addr1_16(&pcb->remote_ip);
+ paccept->pcommon.remote_ip[1] = ip4_addr2_16(&pcb->remote_ip);
+ paccept->pcommon.remote_ip[2] = ip4_addr3_16(&pcb->remote_ip);
+ paccept->pcommon.remote_ip[3] = ip4_addr4_16(&pcb->remote_ip);
+
+ os_memcpy(espconn->proto.tcp->remote_ip, paccept->pcommon.remote_ip, 4);
+ espconn->proto.tcp->remote_port = pcb->remote_port;
+ espconn->state = ESPCONN_CONNECT;
+ espconn_copy_partial(paccept->pespconn, espconn);
+ espconn_get_connection_info(espconn, &pinfo , 0);
+ espconn_printf("espconn_tcp_accept link_cnt: %d\n", espconn->link_cnt);
+ if (espconn->link_cnt == espconn_tcp_get_max_con_allow(espconn) + 1)
+ return ERR_ISCONN;
+
+ tcp_sent(pcb, espconn_server_sent);
+ tcp_recv(pcb, espconn_server_recv);
+ tcp_poll(pcb, espconn_server_poll, 8); /* every 1 seconds */
+
+ if (paccept->pespconn->proto.tcp->connect_callback != NULL) {
+ paccept->pespconn->proto.tcp->connect_callback(paccept->pespconn);
+ }
+
+ return ERR_OK;
+}
+
+/******************************************************************************
+ * FunctionName : espconn_tcp_server
+ * Description : Initialize the server: set up a listening PCB and bind it to
+ * the defined port
+ * Parameters : espconn -- the espconn used to build server
+ * Returns : none
+*******************************************************************************/
+sint8 ICACHE_FLASH_ATTR
+espconn_tcp_server(struct espconn *espconn)
+{
+ struct tcp_pcb *pcb = NULL;
+ espconn_msg *pserver = NULL;
+
+ pserver = (espconn_msg *)os_zalloc(sizeof(espconn_msg));
+ if (pserver == NULL){
+ return ESPCONN_MEM;
+ }
+
+ pcb = tcp_new();
+ if (pcb == NULL) {
+// espconn ->state = ESPCONN_NONE;
+ os_free(pserver);
+ pserver = NULL;
+ return ESPCONN_MEM;
+ } else {
+ tcp_bind(pcb, IP_ADDR_ANY, espconn->proto.tcp->local_port);
+ pcb = tcp_listen(pcb);
+ if (pcb != NULL) {
+ /*insert the node to the active connection list*/
+ espconn_list_creat(&pserver_list, pserver);
+ pserver->preverse = pcb;
+ pserver->pespconn = espconn;
+ pserver->count_opt = MEMP_NUM_TCP_PCB;
+
+ espconn ->state = ESPCONN_LISTEN;
+ tcp_arg(pcb, (void *)espconn);
+// tcp_err(pcb, esponn_server_err);
+ tcp_accept(pcb, espconn_tcp_accept);
+ return ESPCONN_OK;
+ } else {
+// espconn ->state = ESPCONN_NONE;
+ os_free(pserver);
+ pserver = NULL;
+ return ESPCONN_MEM;
+ }
+ }
+}
+
+/******************************************************************************
+ * FunctionName : espconn_tcp_delete
+ * Description : delete the server: delete a listening PCB and free it
+ * Parameters : pdeletecon -- the espconn used to delete a server
+ * Returns : none
+*******************************************************************************/
+sint8 ICACHE_FLASH_ATTR espconn_tcp_delete(struct espconn *pdeletecon)
+{
+ err_t err;
+ remot_info *pinfo = NULL;
+ espconn_msg *pdelete_msg = NULL;
+ struct tcp_pcb *pcb = NULL;
+
+ if (pdeletecon == NULL)
+ return ESPCONN_ARG;
+
+ espconn_get_connection_info(pdeletecon, &pinfo , 0);
+ if (pdeletecon->link_cnt != 0)
+ return ESPCONN_INPROGRESS;
+ else {
+ os_printf("espconn_tcp_delete %p\n",pdeletecon);
+ pdelete_msg = pserver_list;
+ while (pdelete_msg != NULL){
+ if (pdelete_msg->pespconn == pdeletecon){
+ /*remove the node from the client's active connection list*/
+ espconn_list_delete(&pserver_list, pdelete_msg);
+ pcb = pdelete_msg->preverse;
+ os_printf("espconn_tcp_delete %d\n",pcb->state);
+ err = tcp_close(pcb);
+ os_free(pdelete_msg);
+ pdelete_msg = NULL;
+ break;
+ }
+ pdelete_msg = pdelete_msg->pnext;
+ }
+ if (err == ERR_OK)
+ return err;
+ else
+ return ESPCONN_ARG;
+ }
+}
diff --git a/app/lwip/app/espconn_udp.c b/app/lwip/app/espconn_udp.c
new file mode 100644
index 00000000..a9dd7f0c
--- /dev/null
+++ b/app/lwip/app/espconn_udp.c
@@ -0,0 +1,298 @@
+/******************************************************************************
+ * Copyright 2013-2014 Espressif Systems (Wuxi)
+ *
+ * FileName: espconn_udp.c
+ *
+ * Description: udp proto interface
+ *
+ * Modification history:
+ * 2014/3/31, v1.0 create this file.
+*******************************************************************************/
+
+#include "ets_sys.h"
+#include "os_type.h"
+//#include "os.h"
+
+#include "lwip/inet.h"
+#include "lwip/err.h"
+#include "lwip/pbuf.h"
+#include "lwip/mem.h"
+#include "lwip/tcp_impl.h"
+#include "lwip/udp.h"
+
+#include "lwip/app/espconn_udp.h"
+
+extern espconn_msg *plink_active;
+
+static void ICACHE_FLASH_ATTR espconn_data_sentcb(struct espconn *pespconn)
+{
+ if (pespconn == NULL) {
+ return;
+ }
+
+ if (pespconn->sent_callback != NULL) {
+ pespconn->sent_callback(pespconn);
+ }
+}
+
+static void ICACHE_FLASH_ATTR espconn_data_sent(void *arg)
+{
+ espconn_msg *psent = arg;
+
+ if (psent == NULL) {
+ return;
+ }
+
+ if (psent->pcommon.cntr == 0) {
+ psent->pespconn->state = ESPCONN_CONNECT;
+ sys_timeout(TCP_FAST_INTERVAL, espconn_data_sentcb, psent->pespconn);
+ } else {
+ espconn_udp_sent(arg, psent->pcommon.ptrbuf, psent->pcommon.cntr);
+ }
+}
+
+/******************************************************************************
+ * FunctionName : espconn_udp_sent
+ * Description : sent data for client or server
+ * Parameters : void *arg -- client or server to send
+ * uint8* psent -- Data to send
+ * uint16 length -- Length of data to send
+ * Returns : none
+*******************************************************************************/
+void ICACHE_FLASH_ATTR
+espconn_udp_sent(void *arg, uint8 *psent, uint16 length)
+{
+ espconn_msg *pudp_sent = arg;
+ struct udp_pcb *upcb = pudp_sent->pcommon.pcb;
+ struct pbuf *p, *q;
+ u8_t *data = NULL;
+ u16_t cnt = 0;
+ u16_t datalen = 0;
+ u16_t i = 0;
+ err_t err;
+ LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %d %p\n", __LINE__, length, upcb));
+
+ if (pudp_sent == NULL || upcb == NULL || psent == NULL || length == 0) {
+ return;
+ }
+
+ if (1024 < length) {
+ datalen = 1024;
+ } else {
+ datalen = length;
+ }
+
+ p = pbuf_alloc(PBUF_TRANSPORT, datalen, PBUF_RAM);
+ LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %p\n", __LINE__, p));
+
+ if (p != NULL) {
+ q = p;
+
+ while (q != NULL) {
+ data = (u8_t *)q->payload;
+ LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %p\n", __LINE__, data));
+
+ for (i = 0; i < q->len; i++) {
+ data[i] = ((u8_t *) psent)[cnt++];
+ }
+
+ q = q->next;
+ }
+ } else {
+ return;
+ }
+
+ upcb->remote_port = pudp_sent->pespconn->proto.udp->remote_port;
+ IP4_ADDR(&upcb->remote_ip, pudp_sent->pespconn->proto.udp->remote_ip[0],
+ pudp_sent->pespconn->proto.udp->remote_ip[1],
+ pudp_sent->pespconn->proto.udp->remote_ip[2],
+ pudp_sent->pespconn->proto.udp->remote_ip[3]);
+
+ LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %x %d\n", __LINE__, upcb->remote_ip, upcb->remote_port));
+ err = udp_send(upcb, p);
+ LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %d\n", __LINE__, err));
+
+ if (p->ref != 0) {
+ LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %p\n", __LINE__, p));
+ pbuf_free(p);
+ pudp_sent->pcommon.ptrbuf = psent + datalen;
+ pudp_sent->pcommon.cntr = length - datalen;
+ espconn_data_sent(pudp_sent);
+ }
+}
+
+/******************************************************************************
+ * FunctionName : espconn_udp_server_recv
+ * Description : This callback will be called when receiving a datagram.
+ * Parameters : arg -- user supplied argument
+ * upcb -- the udp_pcb which received data
+ * p -- the packet buffer that was received
+ * addr -- the remote IP address from which the packet was received
+ * port -- the remote port from which the packet was received
+ * Returns : none
+*******************************************************************************/
+static void ICACHE_FLASH_ATTR
+espconn_udp_recv(void *arg, struct udp_pcb *upcb, struct pbuf *p,
+ struct ip_addr *addr, u16_t port)
+{
+ espconn_msg *precv = arg;
+ struct pbuf *q = NULL;
+ u8_t *pdata = NULL;
+ u16_t length = 0;
+ struct ip_info ipconfig;
+
+ LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_server_recv %d %p\n", __LINE__, upcb));
+
+ upcb->remote_port = port;
+ upcb->remote_ip = *addr;
+
+ precv->pcommon.remote_ip[0] = ip4_addr1_16(&upcb->remote_ip);
+ precv->pcommon.remote_ip[1] = ip4_addr2_16(&upcb->remote_ip);
+ precv->pcommon.remote_ip[2] = ip4_addr3_16(&upcb->remote_ip);
+ precv->pcommon.remote_ip[3] = ip4_addr4_16(&upcb->remote_ip);
+ os_memcpy(precv->pespconn->proto.udp->remote_ip, precv->pcommon.remote_ip, 4);
+ precv->pespconn->proto.udp->remote_port = port;
+ precv->pcommon.remote_port = port;
+ precv->pcommon.pcb = upcb;
+
+ if (wifi_get_opmode() != 1) {
+ wifi_get_ip_info(1, &ipconfig);
+
+ if (!ip_addr_netcmp((struct ip_addr *)precv->pespconn->proto.udp->remote_ip, &ipconfig.ip, &ipconfig.netmask)) {
+ wifi_get_ip_info(0, &ipconfig);
+ }
+ } else {
+ wifi_get_ip_info(0, &ipconfig);
+ }
+ upcb->local_ip = ipconfig.ip;
+ precv->pespconn->proto.udp->local_ip[0] = ip4_addr1_16(&upcb->local_ip);
+ precv->pespconn->proto.udp->local_ip[1] = ip4_addr2_16(&upcb->local_ip);
+ precv->pespconn->proto.udp->local_ip[2] = ip4_addr3_16(&upcb->local_ip);
+ precv->pespconn->proto.udp->local_ip[3] = ip4_addr4_16(&upcb->local_ip);
+
+ if (p != NULL) {
+// q = p;
+
+// while (q != NULL) {
+// pdata = (u8_t *)os_zalloc(q ->len + 1);
+// length = pbuf_copy_partial(q, pdata, q ->len, 0);
+//
+// LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_server_recv %d %x\n", __LINE__, length));
+// precv->pcommon.pcb = upcb;
+//
+// if (length != 0) {
+// if (precv->pespconn->recv_callback != NULL) {
+// precv->pespconn->recv_callback(precv->pespconn, pdata, length);
+// }
+// }
+//
+// q = q->next;
+// os_free(pdata);
+// }
+ pdata = (u8_t *)os_zalloc(p ->tot_len + 1);
+ length = pbuf_copy_partial(p, pdata, p ->tot_len, 0);
+ precv->pcommon.pcb = upcb;
+ pbuf_free(p);
+ if (length != 0) {
+ if (precv->pespconn->recv_callback != NULL) {
+ precv->pespconn->recv_callback(precv->pespconn, pdata, length);
+ }
+ }
+ os_free(pdata);
+ } else {
+ return;
+ }
+}
+
+/******************************************************************************
+ * FunctionName : espconn_udp_disconnect
+ * Description : A new incoming connection has been disconnected.
+ * Parameters : espconn -- the espconn used to disconnect with host
+ * Returns : none
+*******************************************************************************/
+void ICACHE_FLASH_ATTR espconn_udp_disconnect(espconn_msg *pdiscon)
+{
+ if (pdiscon == NULL) {
+ return;
+ }
+
+ struct udp_pcb *upcb = pdiscon->pcommon.pcb;
+
+ udp_disconnect(upcb);
+
+ udp_remove(upcb);
+
+ espconn_list_delete(&plink_active, pdiscon);
+
+ os_free(pdiscon);
+ pdiscon = NULL;
+}
+
+/******************************************************************************
+ * FunctionName : espconn_udp_server
+ * Description : Initialize the server: set up a PCB and bind it to the port
+ * Parameters : pespconn -- the espconn used to build server
+ * Returns : none
+*******************************************************************************/
+sint8 ICACHE_FLASH_ATTR
+espconn_udp_server(struct espconn *pespconn)
+{
+ struct udp_pcb *upcb = NULL;
+ espconn_msg *pserver = NULL;
+ upcb = udp_new();
+
+ if (upcb == NULL) {
+ return ESPCONN_MEM;
+ } else {
+ pserver = (espconn_msg *)os_zalloc(sizeof(espconn_msg));
+
+ if (pserver == NULL) {
+ udp_remove(upcb);
+ return ESPCONN_MEM;
+ }
+
+ pserver->pcommon.pcb = upcb;
+ pserver->pespconn = pespconn;
+ espconn_list_creat(&plink_active, pserver);
+ udp_bind(upcb, IP_ADDR_ANY, pserver->pespconn->proto.udp->local_port);
+ udp_recv(upcb, espconn_udp_recv, (void *)pserver);
+ return ESPCONN_OK;
+ }
+}
+
+/******************************************************************************
+ * FunctionName : espconn_igmp_leave
+ * Description : leave a multicast group
+ * Parameters : host_ip -- the ip address of udp server
+ * multicast_ip -- multicast ip given by user
+ * Returns : none
+*******************************************************************************/
+sint8 ICACHE_FLASH_ATTR
+espconn_igmp_leave(ip_addr_t *host_ip, ip_addr_t *multicast_ip)
+{
+ if (igmp_leavegroup(host_ip, multicast_ip) != ERR_OK) {
+ LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("udp_leave_multigrup failed!\n"));
+ return -1;
+ };
+
+ return ESPCONN_OK;
+}
+
+/******************************************************************************
+ * FunctionName : espconn_igmp_join
+ * Description : join a multicast group
+ * Parameters : host_ip -- the ip address of udp server
+ * multicast_ip -- multicast ip given by user
+ * Returns : none
+*******************************************************************************/
+sint8 ICACHE_FLASH_ATTR
+espconn_igmp_join(ip_addr_t *host_ip, ip_addr_t *multicast_ip)
+{
+ if (igmp_joingroup(host_ip, multicast_ip) != ERR_OK) {
+ LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("udp_join_multigrup failed!\n"));
+ return -1;
+ };
+
+ /* join to any IP address at the port */
+ return ESPCONN_OK;
+}
diff --git a/app/lwip/app/netio.c b/app/lwip/app/netio.c
new file mode 100644
index 00000000..4124b912
--- /dev/null
+++ b/app/lwip/app/netio.c
@@ -0,0 +1,365 @@
+/**
+ * @file
+ * MetIO Server
+ *
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+#include "lwip/opt.h"
+
+#if LWIP_TCP
+#include "lwip/tcp.h"
+
+/*
+ * This implements a netio server.
+ * The client sends a command word (4 bytes) then a data length word (4 bytes).
+ * If the command is "receive", the server is to consume "data length" bytes into
+ * a circular buffer until the first byte is non-zero, then it is to consume
+ * another command/data pair.
+ * If the command is "send", the server is to send "data length" bytes from a circular
+ * buffer with the first byte being zero, until "some time" (6 seconds in the
+ * current netio126.zip download) has passed and then send one final buffer with
+ * the first byte being non-zero. Then it is to consume another command/data pair.
+ */
+
+/* See http://www.nwlab.net/art/netio/netio.html to get the netio tool */
+
+/* implementation options */
+#define NETIO_BUF_SIZE (4 * 1024)
+#define NETIO_USE_STATIC_BUF 0
+
+/* NetIO server state definition */
+#define NETIO_STATE_WAIT_FOR_CMD 0
+#define NETIO_STATE_RECV_DATA 1
+#define NETIO_STATE_SEND_DATA 2
+#define NETIO_STATE_SEND_DATA_LAST 3
+#define NETIO_STATE_DONE 4
+
+struct netio_state {
+ u32_t state;
+ u32_t cmd;
+ u32_t data_len;
+ u32_t cntr;
+ u8_t * buf_ptr;
+ u32_t buf_pos;
+ u32_t first_byte;
+ u32_t time_stamp;
+};
+
+/* NetIO command protocol definition */
+#define NETIO_CMD_QUIT 0
+#define NETIO_CMD_C2S 1
+#define NETIO_CMD_S2C 2
+#define NETIO_CMD_RES 3
+
+static err_t netio_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err);
+
+static void ICACHE_FLASH_ATTR
+netio_close(void *arg, struct tcp_pcb *pcb)
+{
+ err_t err;
+
+ struct netio_state *ns = arg;
+ ns->state = NETIO_STATE_DONE;
+ tcp_recv(pcb, NULL);
+ err = tcp_close(pcb);
+
+ if (err != ERR_OK) {
+ /* closing failed, try again later */
+ tcp_recv(pcb, netio_recv);
+ } else {
+ /* closing succeeded */
+#if NETIO_USE_STATIC_BUF != 1
+ if(ns->buf_ptr != NULL){
+ mem_free(ns->buf_ptr);
+ }
+#endif
+ tcp_arg(pcb, NULL);
+ tcp_poll(pcb, NULL, 0);
+ tcp_sent(pcb, NULL);
+ if (arg != NULL) {
+ mem_free(arg);
+ }
+ }
+}
+
+static err_t ICACHE_FLASH_ATTR
+netio_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
+{
+ struct netio_state *ns = arg;
+ u8_t * data_ptr;
+ u32_t data_cntr;
+ struct pbuf *q = p;
+ u16_t len;
+
+ if (p != NULL) {
+ tcp_recved(pcb, p->tot_len);
+ }
+
+ if (err == ERR_OK && q != NULL) {
+
+ while (q != NULL) {
+ data_cntr = q->len;
+ data_ptr = q->payload;
+ while (data_cntr--) {
+ if (ns->state == NETIO_STATE_DONE){
+ netio_close(ns, pcb);
+ break;
+ } else if (ns->state == NETIO_STATE_WAIT_FOR_CMD) {
+ if (ns->cntr < 4) {
+ /* build up the CMD field */
+ ns->cmd <<= 8;
+ ns->cmd |= *data_ptr++;
+ ns->cntr++;
+ } else if (ns->cntr < 8) {
+ /* build up the DATA field */
+ ns->data_len <<= 8;
+ ns->data_len |= *data_ptr++;
+ ns->cntr++;
+
+ if (ns->cntr == 8) {
+ /* now we have full command and data words */
+ ns->cntr = 0;
+ ns->buf_pos = 0;
+ ns->buf_ptr[0] = 0;
+ if (ns->cmd == NETIO_CMD_C2S) {
+ ns->state = NETIO_STATE_RECV_DATA;
+ } else if (ns->cmd == NETIO_CMD_S2C) {
+ ns->state = NETIO_STATE_SEND_DATA;
+ /* start timer */
+ ns->time_stamp = sys_now();
+ /* send first round of data */
+
+ len = tcp_sndbuf(pcb);
+ len = LWIP_MIN(len, ns->data_len - ns->cntr);
+ len = LWIP_MIN(len, NETIO_BUF_SIZE - ns->buf_pos);
+
+ do {
+ err = tcp_write(pcb, ns->buf_ptr + ns->buf_pos, len, TCP_WRITE_FLAG_COPY);
+ if (err == ERR_MEM) {
+ len /= 2;
+ }
+ } while ((err == ERR_MEM) && (len > 1));
+
+ ns->buf_pos += len;
+ ns->cntr += len;
+
+ } else {
+ /* unrecognized command, punt */
+ ns->cntr = 0;
+ ns->buf_pos = 0;
+ ns->buf_ptr[0] = 0;
+ netio_close(ns, pcb);
+ break;
+ }
+ }
+ } else {
+ /* in trouble... shouldn't be in this state! */
+ }
+
+ } else if (ns->state == NETIO_STATE_RECV_DATA) {
+
+ if(ns->cntr == 0){
+ /* save the first byte of this new round of data
+ * this will not match ns->buf_ptr[0] in the case that
+ * NETIO_BUF_SIZE is less than ns->data_len.
+ */
+ ns->first_byte = *data_ptr;
+ }
+
+ ns->buf_ptr[ns->buf_pos++] = *data_ptr++;
+ ns->cntr++;
+
+ if (ns->buf_pos == NETIO_BUF_SIZE) {
+ /* circularize the buffer */
+ ns->buf_pos = 0;
+ }
+
+ if(ns->cntr == ns->data_len){
+ ns->cntr = 0;
+ if (ns->first_byte != 0) {
+ /* if this last round did not start with 0,
+ * go look for another command */
+ ns->state = NETIO_STATE_WAIT_FOR_CMD;
+ ns->data_len = 0;
+ ns->cmd = 0;
+ /* TODO LWIP_DEBUGF( print out some throughput calculation results... ); */
+ } else {
+ /* stay here and wait on more data */
+ }
+ }
+
+ } else if (ns->state == NETIO_STATE_SEND_DATA
+ || ns->state == NETIO_STATE_SEND_DATA_LAST) {
+ /* I don't think this should happen... */
+ } else {
+ /* done / quit */
+ netio_close(ns, pcb);
+ break;
+ } /* end of ns->state condition */
+ } /* end of while data still in this pbuf */
+
+ q = q->next;
+ }
+
+ pbuf_free(p);
+
+ } else {
+
+ /* error or closed by other side */
+ if (p != NULL) {
+ pbuf_free(p);
+ }
+
+ /* close the connection */
+ netio_close(ns, pcb);
+
+ }
+ return ERR_OK;
+
+}
+
+static err_t ICACHE_FLASH_ATTR
+netio_sent(void *arg, struct tcp_pcb *pcb, u16_t len)
+{
+ struct netio_state *ns = arg;
+ err_t err = ERR_OK;
+
+ if (ns->cntr >= ns->data_len && ns->state == NETIO_STATE_SEND_DATA) {
+ /* done with this round of sending */
+ ns->buf_pos = 0;
+ ns->cntr = 0;
+
+ /* check if timer expired */
+ if (sys_now() - ns->time_stamp > 600) {
+ ns->buf_ptr[0] = 1;
+ ns->state = NETIO_STATE_SEND_DATA_LAST;
+ } else {
+ ns->buf_ptr[0] = 0;
+ }
+ }
+
+ if(ns->state == NETIO_STATE_SEND_DATA_LAST || ns->state == NETIO_STATE_SEND_DATA){
+ len = tcp_sndbuf(pcb);
+ len = LWIP_MIN(len, ns->data_len - ns->cntr);
+ len = LWIP_MIN(len, NETIO_BUF_SIZE - ns->buf_pos);
+
+ if(ns->cntr < ns->data_len){
+ do {
+ err = tcp_write(pcb, ns->buf_ptr + ns->buf_pos, len, TCP_WRITE_FLAG_COPY);
+ if (err == ERR_MEM) {
+ len /= 2;
+ }
+ } while ((err == ERR_MEM) && (len > 1));
+
+ ns->buf_pos += len;
+ if(ns->buf_pos >= NETIO_BUF_SIZE){
+ ns->buf_pos = 0;
+ }
+
+ ns->cntr += len;
+ }
+ }
+
+ if(ns->cntr >= ns->data_len && ns->state == NETIO_STATE_SEND_DATA_LAST){
+ /* we have buffered up all our data to send this last round, go look for a command */
+ ns->state = NETIO_STATE_WAIT_FOR_CMD;
+ ns->cntr = 0;
+ /* TODO LWIP_DEBUGF( print out some throughput calculation results... ); */
+ }
+
+ return ERR_OK;
+}
+
+static err_t ICACHE_FLASH_ATTR
+netio_poll(void *arg, struct tcp_pcb *pcb)
+{
+ struct netio_state * ns = arg;
+ if(ns->state == NETIO_STATE_SEND_DATA){
+
+ } else if(ns->state == NETIO_STATE_DONE){
+ netio_close(ns, pcb);
+ }
+
+ return ERR_OK;
+
+}
+
+#if NETIO_USE_STATIC_BUF == 1
+static u8_t netio_buf[NETIO_BUF_SIZE];
+#endif
+
+static err_t ICACHE_FLASH_ATTR
+netio_accept(void *arg, struct tcp_pcb *pcb, err_t err)
+{
+ struct netio_state * ns;
+
+ LWIP_UNUSED_ARG(err);
+
+ ns = mem_malloc(sizeof(struct netio_state));
+
+ if(ns == NULL){
+ return ERR_MEM;
+ }
+
+ ns->state = NETIO_STATE_WAIT_FOR_CMD;
+ ns->data_len = 0;
+ ns->cmd = 0;
+ ns->cntr = 0;
+ ns->buf_pos = 0;
+#if NETIO_USE_STATIC_BUF == 1
+ ns->buf_ptr = netio_buf;
+#else
+ ns->buf_ptr = mem_malloc(NETIO_BUF_SIZE);
+
+ if(ns->buf_ptr == NULL){
+ mem_free(ns);
+ return ERR_MEM;
+ }
+#endif
+
+ ns->buf_ptr[0] = 0;
+
+ tcp_arg(pcb, ns);
+ tcp_sent(pcb, netio_sent);
+ tcp_recv(pcb, netio_recv);
+ tcp_poll(pcb, netio_poll, 4); /* every 2 seconds */
+ return ERR_OK;
+}
+
+void ICACHE_FLASH_ATTR netio_init(void)
+{
+ struct tcp_pcb *pcb;
+
+ pcb = tcp_new();
+ tcp_bind(pcb, IP_ADDR_ANY, 18767);
+ pcb = tcp_listen(pcb);
+ tcp_accept(pcb, netio_accept);
+}
+
+#endif /* LWIP_TCP */
diff --git a/app/lwip/app/ping.c b/app/lwip/app/ping.c
new file mode 100644
index 00000000..d353fa27
--- /dev/null
+++ b/app/lwip/app/ping.c
@@ -0,0 +1,322 @@
+/**
+ * @file
+ * Ping sender module
+ *
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+/**
+ * This is an example of a "ping" sender (with raw API and socket API).
+ * It can be used as a start point to maintain opened a network connection, or
+ * like a network "watchdog" for your device.
+ *
+ */
+
+/*
+ * copyright (c) 2010 - 2011 Espressif System
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_RAW /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/mem.h"
+#include "lwip/raw.h"
+#include "lwip/icmp.h"
+#include "lwip/netif.h"
+#include "lwip/sys.h"
+#include "lwip/timers.h"
+#include "lwip/inet_chksum.h"
+#include "os_type.h"
+#include "osapi.h"
+
+#include "lwip/app/ping.h"
+
+#if PING_USE_SOCKETS
+#include "lwip/sockets.h"
+#include "lwip/inet.h"
+#endif /* PING_USE_SOCKETS */
+
+/* ping variables */
+static u16_t ping_seq_num = 0;
+static u32_t ping_time;
+
+static void ICACHE_FLASH_ATTR ping_timeout(void* arg)
+{
+ struct ping_msg *pingmsg = (struct ping_msg *)arg;
+ pingmsg->timeout_count ++;
+ if (pingmsg->ping_opt->recv_function == NULL){
+ os_printf("ping timeout\n");
+ } else {
+ struct ping_resp pingresp;
+ os_bzero(&pingresp, sizeof(struct ping_resp));
+ pingresp.ping_err = -1;
+ pingmsg->ping_opt->recv_function(pingmsg->ping_opt, (void*)&pingresp);
+ }
+}
+
+/** Prepare a echo ICMP request */
+static void ICACHE_FLASH_ATTR
+ping_prepare_echo( struct icmp_echo_hdr *iecho, u16_t len)
+{
+ size_t i = 0;
+ size_t data_len = len - sizeof(struct icmp_echo_hdr);
+
+ ICMPH_TYPE_SET(iecho, ICMP_ECHO);
+ ICMPH_CODE_SET(iecho, 0);
+ iecho->chksum = 0;
+ iecho->id = PING_ID;
+ ++ ping_seq_num;
+ iecho->seqno = htons(ping_seq_num);
+
+ /* fill the additional data buffer with some data */
+ for(i = 0; i < data_len; i++) {
+ ((char*)iecho)[sizeof(struct icmp_echo_hdr) + i] = (char)i;
+ }
+
+ iecho->chksum = inet_chksum(iecho, len);
+}
+
+static void ICACHE_FLASH_ATTR
+ping_prepare_er(struct icmp_echo_hdr *iecho, u16_t len)
+{
+
+ ICMPH_TYPE_SET(iecho, ICMP_ER);
+ ICMPH_CODE_SET(iecho, 0);
+ iecho->chksum = 0;
+
+ iecho->chksum = inet_chksum(iecho, len);
+}
+
+/* Ping using the raw ip */
+static u8_t ICACHE_FLASH_ATTR
+ping_recv(void *arg, struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *addr)
+{
+ struct icmp_echo_hdr *iecho = NULL;
+ static u16_t seqno = 0;
+ struct ping_msg *pingmsg = (struct ping_msg*)arg;
+
+ LWIP_UNUSED_ARG(arg);
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_UNUSED_ARG(addr);
+ LWIP_ASSERT("p != NULL", p != NULL);
+
+ if (pbuf_header( p, -PBUF_IP_HLEN)==0) {
+ iecho = (struct icmp_echo_hdr *)p->payload;
+
+ if ((iecho->id == PING_ID) && (iecho->seqno == htons(ping_seq_num)) && iecho->type == ICMP_ER) {
+ LWIP_DEBUGF( PING_DEBUG, ("ping: recv "));
+ ip_addr_debug_print(PING_DEBUG, addr);
+ LWIP_DEBUGF( PING_DEBUG, (" %"U32_F" ms\n", (sys_now()-ping_time)));
+ if (iecho->seqno != seqno){
+ /* do some ping result processing */
+ {
+ struct ip_hdr *iphdr = NULL;
+ char ipaddrstr[16];
+ ip_addr_t source_ip;
+ sys_untimeout(ping_timeout, pingmsg);
+ os_bzero(&source_ip, sizeof(ip_addr_t));
+ os_bzero(ipaddrstr, sizeof(ipaddrstr));
+ uint32 delay = system_relative_time(pingmsg->ping_sent);
+ delay /= PING_COARSE;
+ iphdr = (struct ip_hdr*)((u8*)iecho - PBUF_IP_HLEN);
+ source_ip.addr = iphdr->src.addr;
+ ipaddr_ntoa_r(&source_ip,ipaddrstr, sizeof(ipaddrstr));
+ if (pingmsg->ping_opt->recv_function == NULL){
+ os_printf("recv %s: byte = %d, time = %d ms, seq = %d\n",ipaddrstr, PING_DATA_SIZE, delay, ntohs(iecho->seqno));
+ } else {
+ struct ping_resp pingresp;
+ os_bzero(&pingresp, sizeof(struct ping_resp));
+ pingresp.bytes = PING_DATA_SIZE;
+ pingresp.resp_time = delay;
+ pingresp.seqno = ntohs(iecho->seqno);
+ pingresp.ping_err = 0;
+ pingmsg->ping_opt->recv_function(pingmsg->ping_opt,(void*) &pingresp);
+ }
+ }
+ seqno = iecho->seqno;
+ }
+
+ PING_RESULT(1);
+ pbuf_free(p);
+ return 1; /* eat the packet */
+ }
+// } else if(iecho->type == ICMP_ECHO){
+// struct pbuf *q = NULL;
+// os_printf("receive ping request:seq=%d\n", ntohs(iecho->seqno));
+// q = pbuf_alloc(PBUF_IP, (u16_t)p->tot_len, PBUF_RAM);
+// if (q!=NULL) {
+// pbuf_copy(q, p);
+// iecho = (struct icmp_echo_hdr *)q->payload;
+// ping_prepare_er(iecho, q->tot_len);
+// raw_sendto(pcb, q, addr);
+// pbuf_free(q);
+// }
+// pbuf_free(p);
+// return 1;
+// }
+ }
+
+ return 0; /* don't eat the packet */
+}
+
+static void ICACHE_FLASH_ATTR
+ping_send(struct raw_pcb *raw, ip_addr_t *addr)
+{
+ struct pbuf *p = NULL;
+ struct icmp_echo_hdr *iecho = NULL;
+ size_t ping_size = sizeof(struct icmp_echo_hdr) + PING_DATA_SIZE;
+
+ LWIP_DEBUGF( PING_DEBUG, ("ping: send "));
+ ip_addr_debug_print(PING_DEBUG, addr);
+ LWIP_DEBUGF( PING_DEBUG, ("\n"));
+ LWIP_ASSERT("ping_size <= 0xffff", ping_size <= 0xffff);
+
+ p = pbuf_alloc(PBUF_IP, (u16_t)ping_size, PBUF_RAM);
+ if (!p) {
+ return;
+ }
+ if ((p->len == p->tot_len) && (p->next == NULL)) {
+ iecho = (struct icmp_echo_hdr *)p->payload;
+
+ ping_prepare_echo(iecho, (u16_t)ping_size);
+
+ raw_sendto(raw, p, addr);
+ ping_time = sys_now();
+ }
+ pbuf_free(p);
+}
+
+static void ICACHE_FLASH_ATTR
+ping_coarse_tmr(void *arg)
+{
+ struct ping_msg *pingmsg = (struct ping_msg*)arg;
+ struct ping_option *ping_opt= NULL;
+ struct ping_resp pingresp;
+ ip_addr_t ping_target;
+
+ LWIP_ASSERT("ping_timeout: no pcb given!", pingmsg != NULL);
+ ping_target.addr = pingmsg->ping_opt->ip;
+ ping_opt = pingmsg->ping_opt;
+ if (--pingmsg->sent_count != 0){
+ pingmsg ->ping_sent = system_get_time();
+ ping_send(pingmsg->ping_pcb, &ping_target);
+
+ sys_timeout(PING_TIMEOUT_MS, ping_timeout, pingmsg);
+ sys_timeout(pingmsg->coarse_time, ping_coarse_tmr, pingmsg);
+ } else {
+ uint32 delay = system_relative_time(pingmsg->ping_start);
+ delay /= PING_COARSE;
+ ping_seq_num = 0;
+ if (ping_opt->sent_function == NULL){
+ os_printf("ping %d, timeout %d, total payload %d bytes, %d ms\n",
+ pingmsg->max_count, pingmsg->timeout_count, PING_DATA_SIZE*(pingmsg->max_count - pingmsg->timeout_count),delay);
+ } else {
+ os_bzero(&pingresp, sizeof(struct ping_resp));
+ pingresp.total_count = pingmsg->max_count;
+ pingresp.timeout_count = pingmsg->timeout_count;
+ pingresp.total_bytes = PING_DATA_SIZE*(pingmsg->max_count - pingmsg->timeout_count);
+ pingresp.total_time = delay;
+ pingresp.ping_err = 0;
+ }
+ sys_untimeout(ping_coarse_tmr, pingmsg);
+ raw_remove(pingmsg->ping_pcb);
+ os_free(pingmsg);
+ if (ping_opt->sent_function != NULL)
+ ping_opt->sent_function(ping_opt,(uint8*)&pingresp);
+ }
+}
+
+static bool ICACHE_FLASH_ATTR
+ping_raw_init(struct ping_msg *pingmsg)
+{
+ if (pingmsg == NULL)
+ return false;
+
+ ip_addr_t ping_target;
+ pingmsg->ping_pcb = raw_new(IP_PROTO_ICMP);
+ LWIP_ASSERT("ping_pcb != NULL", pingmsg->ping_pcb != NULL);
+
+ raw_recv(pingmsg->ping_pcb, ping_recv, pingmsg);
+ raw_bind(pingmsg->ping_pcb, IP_ADDR_ANY);
+
+ ping_target.addr = pingmsg->ping_opt->ip;
+ pingmsg ->ping_sent = system_get_time();
+ ping_send(pingmsg->ping_pcb, &ping_target);
+
+ sys_timeout(PING_TIMEOUT_MS, ping_timeout, pingmsg);
+ sys_timeout(pingmsg->coarse_time, ping_coarse_tmr, pingmsg);
+ return true;
+}
+
+bool ICACHE_FLASH_ATTR
+ping_start(struct ping_option *ping_opt)
+{
+ struct ping_msg *pingmsg = NULL;
+ pingmsg = (struct ping_msg *)os_zalloc(sizeof(struct ping_msg));
+ if (pingmsg == NULL || ping_opt == NULL)
+ return false;
+
+ pingmsg->ping_opt = ping_opt;
+ if (ping_opt->count != 0)
+ pingmsg->max_count = ping_opt->count;
+ else
+ pingmsg->max_count = DEFAULT_PING_MAX_COUNT;
+
+ if (ping_opt->coarse_time != 0)
+ pingmsg->coarse_time = ping_opt->coarse_time * PING_COARSE;
+ else
+ pingmsg->coarse_time = PING_COARSE;
+
+ pingmsg->ping_start = system_get_time();
+ pingmsg->sent_count = pingmsg->max_count;
+ return ping_raw_init(pingmsg);
+}
+
+bool ICACHE_FLASH_ATTR
+ping_regist_recv(struct ping_option *ping_opt, ping_recv_function ping_recv)
+{
+ if (ping_opt == NULL)
+ return false;
+
+ ping_opt ->recv_function = ping_recv;
+ return true;
+}
+
+bool ICACHE_FLASH_ATTR
+ping_regist_sent(struct ping_option *ping_opt, ping_sent_function ping_sent)
+{
+ if (ping_opt == NULL)
+ return false;
+
+ ping_opt ->sent_function = ping_sent;
+ return true;
+}
+
+#endif /* LWIP_RAW */
diff --git a/app/lwip/core/Makefile b/app/lwip/core/Makefile
new file mode 100644
index 00000000..7cbb57b6
--- /dev/null
+++ b/app/lwip/core/Makefile
@@ -0,0 +1,46 @@
+
+#############################################################
+# Required variables for each makefile
+# Discard this section from all parent makefiles
+# Expected variables (with automatic defaults):
+# CSRCS (all "C" files in the dir)
+# SUBDIRS (all subdirs with a Makefile)
+# GEN_LIBS - list of libs to be generated ()
+# GEN_IMAGES - list of images to be generated ()
+# COMPONENTS_xxx - a list of libs/objs in the form
+# subdir/lib to be extracted and rolled up into
+# a generated lib/image xxx.a ()
+#
+ifndef PDIR
+
+GEN_LIBS = liblwipcore.a
+
+endif
+
+
+#############################################################
+# Configuration i.e. compile options etc.
+# Target specific stuff (defines etc.) goes in here!
+# Generally values applying to a tree are captured in the
+# makefile at its root level - these are then overridden
+# for a subtree within the makefile rooted therein
+#
+#DEFINES +=
+
+#############################################################
+# Recursion Magic - Don't touch this!!
+#
+# Each subtree potentially has an include directory
+# corresponding to the common APIs applicable to modules
+# rooted at that subtree. Accordingly, the INCLUDE PATH
+# of a module can only contain the include directories up
+# its parent path, and not its siblings
+#
+# Required for each makefile to inherit from the parent
+#
+
+INCLUDES := $(INCLUDES) -I $(PDIR)include
+INCLUDES += -I ./
+PDIR := ../$(PDIR)
+sinclude $(PDIR)Makefile
+
diff --git a/app/lwip/core/def.c b/app/lwip/core/def.c
new file mode 100644
index 00000000..352b5524
--- /dev/null
+++ b/app/lwip/core/def.c
@@ -0,0 +1,108 @@
+/**
+ * @file
+ * Common functions used throughout the stack.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ *
+ */
+
+#include "lwip/opt.h"
+#include "lwip/def.h"
+
+/**
+ * These are reference implementations of the byte swapping functions.
+ * Again with the aim of being simple, correct and fully portable.
+ * Byte swapping is the second thing you would want to optimize. You will
+ * need to port it to your architecture and in your cc.h:
+ *
+ * #define LWIP_PLATFORM_BYTESWAP 1
+ * #define LWIP_PLATFORM_HTONS(x)
+ * #define LWIP_PLATFORM_HTONL(x)
+ *
+ * Note ntohs() and ntohl() are merely references to the htonx counterparts.
+ */
+
+#if (LWIP_PLATFORM_BYTESWAP == 0) && (BYTE_ORDER == LITTLE_ENDIAN)
+
+/**
+ * Convert an u16_t from host- to network byte order.
+ *
+ * @param n u16_t in host byte order
+ * @return n in network byte order
+ */
+u16_t
+lwip_htons(u16_t n)
+{
+ return ((n & 0xff) << 8) | ((n & 0xff00) >> 8);
+}
+
+/**
+ * Convert an u16_t from network- to host byte order.
+ *
+ * @param n u16_t in network byte order
+ * @return n in host byte order
+ */
+u16_t
+lwip_ntohs(u16_t n)
+{
+ return lwip_htons(n);
+}
+
+/**
+ * Convert an u32_t from host- to network byte order.
+ *
+ * @param n u32_t in host byte order
+ * @return n in network byte order
+ */
+u32_t
+lwip_htonl(u32_t n)
+{
+ return ((n & 0xff) << 24) |
+ ((n & 0xff00) << 8) |
+ ((n & 0xff0000UL) >> 8) |
+ ((n & 0xff000000UL) >> 24);
+}
+
+/**
+ * Convert an u32_t from network- to host byte order.
+ *
+ * @param n u32_t in network byte order
+ * @return n in host byte order
+ */
+u32_t
+lwip_ntohl(u32_t n)
+{
+ return lwip_htonl(n);
+}
+
+#endif /* (LWIP_PLATFORM_BYTESWAP == 0) && (BYTE_ORDER == LITTLE_ENDIAN) */
diff --git a/app/lwip/core/dhcp.c b/app/lwip/core/dhcp.c
new file mode 100644
index 00000000..3b189f28
--- /dev/null
+++ b/app/lwip/core/dhcp.c
@@ -0,0 +1,1766 @@
+/**
+ * @file
+ * Dynamic Host Configuration Protocol client
+ *
+ */
+
+/*
+ *
+ * Copyright (c) 2001-2004 Leon Woestenberg
+ * Copyright (c) 2001-2004 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is a contribution to the lwIP TCP/IP stack.
+ * The Swedish Institute of Computer Science and Adam Dunkels
+ * are specifically granted permission to redistribute this
+ * source code.
+ *
+ * Author: Leon Woestenberg
+ *
+ * This is a DHCP client for the lwIP TCP/IP stack. It aims to conform
+ * with RFC 2131 and RFC 2132.
+ *
+ * TODO:
+ * - Support for interfaces other than Ethernet (SLIP, PPP, ...)
+ *
+ * Please coordinate changes and requests with Leon Woestenberg
+ *
+ *
+ * Integration with your code:
+ *
+ * In lwip/dhcp.h
+ * #define DHCP_COARSE_TIMER_SECS (recommended 60 which is a minute)
+ * #define DHCP_FINE_TIMER_MSECS (recommended 500 which equals TCP coarse timer)
+ *
+ * Then have your application call dhcp_coarse_tmr() and
+ * dhcp_fine_tmr() on the defined intervals.
+ *
+ * dhcp_start(struct netif *netif);
+ * starts a DHCP client instance which configures the interface by
+ * obtaining an IP address lease and maintaining it.
+ *
+ * Use dhcp_release(netif) to end the lease and use dhcp_stop(netif)
+ * to remove the DHCP client.
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_DHCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/stats.h"
+#include "lwip/mem.h"
+#include "lwip/udp.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/def.h"
+#include "lwip/sys.h"
+#include "lwip/dhcp.h"
+#include "lwip/autoip.h"
+#include "lwip/dns.h"
+#include "netif/etharp.h"
+
+#include
+
+/** Default for DHCP_GLOBAL_XID is 0xABCD0000
+ * This can be changed by defining DHCP_GLOBAL_XID and DHCP_GLOBAL_XID_HEADER, e.g.
+ * #define DHCP_GLOBAL_XID_HEADER "stdlib.h"
+ * #define DHCP_GLOBAL_XID rand()
+ */
+#ifdef DHCP_GLOBAL_XID_HEADER
+#include DHCP_GLOBAL_XID_HEADER /* include optional starting XID generation prototypes */
+#endif
+
+/** DHCP_OPTION_MAX_MSG_SIZE is set to the MTU
+ * MTU is checked to be big enough in dhcp_start */
+#define DHCP_MAX_MSG_LEN(netif) (netif->mtu)
+#define DHCP_MAX_MSG_LEN_MIN_REQUIRED 576
+/** Minimum length for reply before packet is parsed */
+#define DHCP_MIN_REPLY_LEN 44
+
+#define REBOOT_TRIES 2
+
+/** Option handling: options are parsed in dhcp_parse_reply
+ * and saved in an array where other functions can load them from.
+ * This might be moved into the struct dhcp (not necessarily since
+ * lwIP is single-threaded and the array is only used while in recv
+ * callback). */
+#define DHCP_OPTION_IDX_OVERLOAD 0
+#define DHCP_OPTION_IDX_MSG_TYPE 1
+#define DHCP_OPTION_IDX_SERVER_ID 2
+#define DHCP_OPTION_IDX_LEASE_TIME 3
+#define DHCP_OPTION_IDX_T1 4
+#define DHCP_OPTION_IDX_T2 5
+#define DHCP_OPTION_IDX_SUBNET_MASK 6
+#define DHCP_OPTION_IDX_ROUTER 7
+#define DHCP_OPTION_IDX_DNS_SERVER 8
+#define DHCP_OPTION_IDX_MAX (DHCP_OPTION_IDX_DNS_SERVER + DNS_MAX_SERVERS)
+
+/** Holds the decoded option values, only valid while in dhcp_recv.
+ @todo: move this into struct dhcp? */
+u32_t dhcp_rx_options_val[DHCP_OPTION_IDX_MAX];
+/** Holds a flag which option was received and is contained in dhcp_rx_options_val,
+ only valid while in dhcp_recv.
+ @todo: move this into struct dhcp? */
+u8_t dhcp_rx_options_given[DHCP_OPTION_IDX_MAX];
+
+#define dhcp_option_given(dhcp, idx) (dhcp_rx_options_given[idx] != 0)
+#define dhcp_got_option(dhcp, idx) (dhcp_rx_options_given[idx] = 1)
+#define dhcp_clear_option(dhcp, idx) (dhcp_rx_options_given[idx] = 0)
+#define dhcp_clear_all_options(dhcp) (memset(dhcp_rx_options_given, 0, sizeof(dhcp_rx_options_given)))
+#define dhcp_get_option_value(dhcp, idx) (dhcp_rx_options_val[idx])
+#define dhcp_set_option_value(dhcp, idx, val) (dhcp_rx_options_val[idx] = (val))
+
+
+/* DHCP client state machine functions */
+static err_t dhcp_discover(struct netif *netif);
+static err_t dhcp_select(struct netif *netif);
+static void dhcp_bind(struct netif *netif);
+#if DHCP_DOES_ARP_CHECK
+static err_t dhcp_decline(struct netif *netif);
+#endif /* DHCP_DOES_ARP_CHECK */
+static err_t dhcp_rebind(struct netif *netif);
+static err_t dhcp_reboot(struct netif *netif);
+static void dhcp_set_state(struct dhcp *dhcp, u8_t new_state);
+
+/* receive, unfold, parse and free incoming messages */
+static void dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port);
+
+/* set the DHCP timers */
+static void dhcp_timeout(struct netif *netif);
+static void dhcp_t1_timeout(struct netif *netif);
+static void dhcp_t2_timeout(struct netif *netif);
+
+/* build outgoing messages */
+/* create a DHCP message, fill in common headers */
+static err_t dhcp_create_msg(struct netif *netif, struct dhcp *dhcp, u8_t message_type);
+/* free a DHCP request */
+static void dhcp_delete_msg(struct dhcp *dhcp);
+/* add a DHCP option (type, then length in bytes) */
+static void dhcp_option(struct dhcp *dhcp, u8_t option_type, u8_t option_len);
+/* add option values */
+static void dhcp_option_byte(struct dhcp *dhcp, u8_t value);
+static void dhcp_option_short(struct dhcp *dhcp, u16_t value);
+static void dhcp_option_long(struct dhcp *dhcp, u32_t value);
+/* always add the DHCP options trailer to end and pad */
+static void dhcp_option_trailer(struct dhcp *dhcp);
+
+/**
+ * Back-off the DHCP client (because of a received NAK response).
+ *
+ * Back-off the DHCP client because of a received NAK. Receiving a
+ * NAK means the client asked for something non-sensible, for
+ * example when it tries to renew a lease obtained on another network.
+ *
+ * We clear any existing set IP address and restart DHCP negotiation
+ * afresh (as per RFC2131 3.2.3).
+ *
+ * @param netif the netif under DHCP control
+ */
+static void ICACHE_FLASH_ATTR
+dhcp_handle_nak(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_handle_nak(netif=%p) %c%c%"U16_F"\n",
+ (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+ /* Set the interface down since the address must no longer be used, as per RFC2131 */
+ netif_set_down(netif);
+ /* remove IP address from interface */
+ netif_set_ipaddr(netif, IP_ADDR_ANY);
+ netif_set_gw(netif, IP_ADDR_ANY);
+ netif_set_netmask(netif, IP_ADDR_ANY);
+ /* Change to a defined state */
+ dhcp_set_state(dhcp, DHCP_BACKING_OFF);
+ /* We can immediately restart discovery */
+ dhcp_discover(netif);
+}
+
+#if DHCP_DOES_ARP_CHECK
+/**
+ * Checks if the offered IP address is already in use.
+ *
+ * It does so by sending an ARP request for the offered address and
+ * entering CHECKING state. If no ARP reply is received within a small
+ * interval, the address is assumed to be free for use by us.
+ *
+ * @param netif the netif under DHCP control
+ */
+static void ICACHE_FLASH_ATTR
+dhcp_check(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ err_t result;
+ u16_t msecs;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_check(netif=%p) %c%c\n", (void *)netif, (s16_t)netif->name[0],
+ (s16_t)netif->name[1]));
+ dhcp_set_state(dhcp, DHCP_CHECKING);
+ /* create an ARP query for the offered IP address, expecting that no host
+ responds, as the IP address should not be in use. */
+ result = etharp_query(netif, &dhcp->offered_ip_addr, NULL);
+ if (result != ERR_OK) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("dhcp_check: could not perform ARP query\n"));
+ }
+ dhcp->tries++;
+ msecs = 500;
+ dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_check(): set request timeout %"U16_F" msecs\n", msecs));
+}
+#endif /* DHCP_DOES_ARP_CHECK */
+
+/**
+ * Remember the configuration offered by a DHCP server.
+ *
+ * @param netif the netif under DHCP control
+ */
+static void ICACHE_FLASH_ATTR
+dhcp_handle_offer(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_handle_offer(netif=%p) %c%c%"U16_F"\n",
+ (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+ /* obtain the server address */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_SERVER_ID)) {
+ ip4_addr_set_u32(&dhcp->server_ip_addr, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_SERVER_ID)));
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_handle_offer(): server 0x%08"X32_F"\n",
+ ip4_addr_get_u32(&dhcp->server_ip_addr)));
+ /* remember offered address */
+ ip_addr_copy(dhcp->offered_ip_addr, dhcp->msg_in->yiaddr);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_handle_offer(): offer for 0x%08"X32_F"\n",
+ ip4_addr_get_u32(&dhcp->offered_ip_addr)));
+
+ dhcp_select(netif);
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("dhcp_handle_offer(netif=%p) did not get server ID!\n", (void*)netif));
+ }
+}
+
+/**
+ * Select a DHCP server offer out of all offers.
+ *
+ * Simply select the first offer received.
+ *
+ * @param netif the netif under DHCP control
+ * @return lwIP specific error (see error.h)
+ */
+static err_t ICACHE_FLASH_ATTR
+dhcp_select(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ err_t result;
+ u16_t msecs;
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_select(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+ dhcp_set_state(dhcp, DHCP_REQUESTING);
+
+ /* create and initialize the DHCP message header */
+ result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST);
+ if (result == ERR_OK) {
+ dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif));
+
+ /* MUST request the offered IP address */
+ dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4);
+ dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr)));
+
+ dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4);
+ dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&dhcp->server_ip_addr)));
+
+ dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, 4/*num options*/);
+ dhcp_option_byte(dhcp, DHCP_OPTION_SUBNET_MASK);
+ dhcp_option_byte(dhcp, DHCP_OPTION_ROUTER);
+ dhcp_option_byte(dhcp, DHCP_OPTION_BROADCAST);
+ dhcp_option_byte(dhcp, DHCP_OPTION_DNS_SERVER);
+
+#if LWIP_NETIF_HOSTNAME
+ if (netif->hostname != NULL) {
+ const char *p = (const char*)netif->hostname;
+ u8_t namelen = (u8_t)strlen(p);
+ if (namelen > 0) {
+ LWIP_ASSERT("DHCP: hostname is too long!", namelen < 255);
+ dhcp_option(dhcp, DHCP_OPTION_HOSTNAME, namelen);
+ while (*p) {
+ dhcp_option_byte(dhcp, *p++);
+ }
+ }
+ }
+#endif /* LWIP_NETIF_HOSTNAME */
+
+ dhcp_option_trailer(dhcp);
+ /* shrink the pbuf to the actual content length */
+ pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+ /* send broadcast to any DHCP server */
+ udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
+ dhcp_delete_msg(dhcp);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_select: REQUESTING\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("dhcp_select: could not allocate DHCP request\n"));
+ }
+ dhcp->tries++;
+ msecs = (dhcp->tries < 6 ? 1 << dhcp->tries : 60) * 1000;
+ dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_select(): set request timeout %"U16_F" msecs\n", msecs));
+ return result;
+}
+
+/**
+ * The DHCP timer that checks for lease renewal/rebind timeouts.
+ */
+void ICACHE_FLASH_ATTR
+dhcp_coarse_tmr()
+{
+ struct netif *netif = netif_list;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_coarse_tmr()\n"));
+ /* iterate through all network interfaces */
+ while (netif != NULL) {
+ /* only act on DHCP configured interfaces */
+ if (netif->dhcp != NULL) {
+ /* timer is active (non zero), and triggers (zeroes) now? */
+ if (netif->dhcp->t2_timeout-- == 1) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t2 timeout\n"));
+ /* this clients' rebind timeout triggered */
+ dhcp_t2_timeout(netif);
+ /* timer is active (non zero), and triggers (zeroes) now */
+ } else if (netif->dhcp->t1_timeout-- == 1) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t1 timeout\n"));
+ /* this clients' renewal timeout triggered */
+ dhcp_t1_timeout(netif);
+ }
+ }
+ /* proceed to next netif */
+ netif = netif->next;
+ }
+}
+
+/**
+ * DHCP transaction timeout handling
+ *
+ * A DHCP server is expected to respond within a short period of time.
+ * This timer checks whether an outstanding DHCP request is timed out.
+ */
+void ICACHE_FLASH_ATTR
+dhcp_fine_tmr()
+{
+ struct netif *netif = netif_list;
+ /* loop through netif's */
+ while (netif != NULL) {
+ /* only act on DHCP configured interfaces */
+ if (netif->dhcp != NULL) {
+ /* timer is active (non zero), and is about to trigger now */
+ if (netif->dhcp->request_timeout > 1) {
+ netif->dhcp->request_timeout--;
+ }
+ else if (netif->dhcp->request_timeout == 1) {
+ netif->dhcp->request_timeout--;
+ /* { netif->dhcp->request_timeout == 0 } */
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_fine_tmr(): request timeout\n"));
+ /* this client's request timeout triggered */
+ dhcp_timeout(netif);
+ }
+ }
+ /* proceed to next network interface */
+ netif = netif->next;
+ }
+}
+
+/**
+ * A DHCP negotiation transaction, or ARP request, has timed out.
+ *
+ * The timer that was started with the DHCP or ARP request has
+ * timed out, indicating no response was received in time.
+ *
+ * @param netif the netif under DHCP control
+ */
+static void ICACHE_FLASH_ATTR
+dhcp_timeout(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_timeout()\n"));
+ /* back-off period has passed, or server selection timed out */
+ if ((dhcp->state == DHCP_BACKING_OFF) || (dhcp->state == DHCP_SELECTING)) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_timeout(): restarting discovery\n"));
+ dhcp_discover(netif);
+ /* receiving the requested lease timed out */
+ } else if (dhcp->state == DHCP_REQUESTING) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REQUESTING, DHCP request timed out\n"));
+ if (dhcp->tries <= 5) {
+ dhcp_select(netif);
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REQUESTING, releasing, restarting\n"));
+ dhcp_release(netif);
+ dhcp_discover(netif);
+ }
+#if DHCP_DOES_ARP_CHECK
+ /* received no ARP reply for the offered address (which is good) */
+ } else if (dhcp->state == DHCP_CHECKING) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): CHECKING, ARP request timed out\n"));
+ if (dhcp->tries <= 1) {
+ dhcp_check(netif);
+ /* no ARP replies on the offered address,
+ looks like the IP address is indeed free */
+ } else {
+ /* bind the interface to the offered address */
+ dhcp_bind(netif);
+ }
+#endif /* DHCP_DOES_ARP_CHECK */
+ }
+ /* did not get response to renew request? */
+ else if (dhcp->state == DHCP_RENEWING) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): RENEWING, DHCP request timed out\n"));
+ /* just retry renewal */
+ /* note that the rebind timer will eventually time-out if renew does not work */
+ dhcp_renew(netif);
+ /* did not get response to rebind request? */
+ } else if (dhcp->state == DHCP_REBINDING) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REBINDING, DHCP request timed out\n"));
+ if (dhcp->tries <= 8) {
+ dhcp_rebind(netif);
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): RELEASING, DISCOVERING\n"));
+ dhcp_release(netif);
+ dhcp_discover(netif);
+ }
+ } else if (dhcp->state == DHCP_REBOOTING) {
+ if (dhcp->tries < REBOOT_TRIES) {
+ dhcp_reboot(netif);
+ } else {
+ dhcp_discover(netif);
+ }
+ }
+}
+
+/**
+ * The renewal period has timed out.
+ *
+ * @param netif the netif under DHCP control
+ */
+static void ICACHE_FLASH_ATTR
+dhcp_t1_timeout(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_t1_timeout()\n"));
+ if ((dhcp->state == DHCP_REQUESTING) || (dhcp->state == DHCP_BOUND) ||
+ (dhcp->state == DHCP_RENEWING)) {
+ /* just retry to renew - note that the rebind timer (t2) will
+ * eventually time-out if renew tries fail. */
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("dhcp_t1_timeout(): must renew\n"));
+ /* This slightly different to RFC2131: DHCPREQUEST will be sent from state
+ DHCP_RENEWING, not DHCP_BOUND */
+ dhcp_renew(netif);
+ }
+}
+
+/**
+ * The rebind period has timed out.
+ *
+ * @param netif the netif under DHCP control
+ */
+static void ICACHE_FLASH_ATTR
+dhcp_t2_timeout(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_t2_timeout()\n"));
+ if ((dhcp->state == DHCP_REQUESTING) || (dhcp->state == DHCP_BOUND) ||
+ (dhcp->state == DHCP_RENEWING)) {
+ /* just retry to rebind */
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("dhcp_t2_timeout(): must rebind\n"));
+ /* This slightly different to RFC2131: DHCPREQUEST will be sent from state
+ DHCP_REBINDING, not DHCP_BOUND */
+ dhcp_rebind(netif);
+ }
+}
+
+/**
+ * Handle a DHCP ACK packet
+ *
+ * @param netif the netif under DHCP control
+ */
+static void ICACHE_FLASH_ATTR
+dhcp_handle_ack(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+#if LWIP_DNS
+ u8_t n;
+#endif /* LWIP_DNS */
+
+ /* clear options we might not get from the ACK */
+ ip_addr_set_zero(&dhcp->offered_sn_mask);
+ ip_addr_set_zero(&dhcp->offered_gw_addr);
+#if LWIP_DHCP_BOOTP_FILE
+ ip_addr_set_zero(&dhcp->offered_si_addr);
+#endif /* LWIP_DHCP_BOOTP_FILE */
+
+ /* lease time given? */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_LEASE_TIME)) {
+ /* remember offered lease time */
+ dhcp->offered_t0_lease = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_LEASE_TIME);
+ }
+ /* renewal period given? */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_T1)) {
+ /* remember given renewal period */
+ dhcp->offered_t1_renew = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_T1);
+ } else {
+ /* calculate safe periods for renewal */
+ dhcp->offered_t1_renew = dhcp->offered_t0_lease / 2;
+ }
+
+ /* renewal period given? */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_T2)) {
+ /* remember given rebind period */
+ dhcp->offered_t2_rebind = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_T2);
+ } else {
+ /* calculate safe periods for rebinding */
+ dhcp->offered_t2_rebind = dhcp->offered_t0_lease;
+ }
+
+ /* (y)our internet address */
+ ip_addr_copy(dhcp->offered_ip_addr, dhcp->msg_in->yiaddr);
+
+#if LWIP_DHCP_BOOTP_FILE
+ /* copy boot server address,
+ boot file name copied in dhcp_parse_reply if not overloaded */
+ ip_addr_copy(dhcp->offered_si_addr, dhcp->msg_in->siaddr);
+#endif /* LWIP_DHCP_BOOTP_FILE */
+
+ /* subnet mask given? */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_SUBNET_MASK)) {
+ /* remember given subnet mask */
+ ip4_addr_set_u32(&dhcp->offered_sn_mask, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_SUBNET_MASK)));
+ dhcp->subnet_mask_given = 1;
+ } else {
+ dhcp->subnet_mask_given = 0;
+ }
+
+ /* gateway router */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_ROUTER)) {
+ ip4_addr_set_u32(&dhcp->offered_gw_addr, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_ROUTER)));
+ }
+
+#if LWIP_DNS
+ /* DNS servers */
+ n = 0;
+ while(dhcp_option_given(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n) && (n < DNS_MAX_SERVERS)) {
+ ip_addr_t dns_addr;
+ ip4_addr_set_u32(&dns_addr, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n)));
+ dns_setserver(n, &dns_addr);
+ n++;
+ }
+#endif /* LWIP_DNS */
+}
+
+/** Set a statically allocated struct dhcp to work with.
+ * Using this prevents dhcp_start to allocate it using mem_malloc.
+ *
+ * @param netif the netif for which to set the struct dhcp
+ * @param dhcp (uninitialised) dhcp struct allocated by the application
+ */
+void ICACHE_FLASH_ATTR
+dhcp_set_struct(struct netif *netif, struct dhcp *dhcp)
+{
+ LWIP_ASSERT("netif != NULL", netif != NULL);
+ LWIP_ASSERT("dhcp != NULL", dhcp != NULL);
+ LWIP_ASSERT("netif already has a struct dhcp set", netif->dhcp == NULL);
+
+ /* clear data structure */
+ memset(dhcp, 0, sizeof(struct dhcp));
+ /* dhcp_set_state(&dhcp, DHCP_OFF); */
+ netif->dhcp = dhcp;
+}
+
+/** Removes a struct dhcp from a netif.
+ *
+ * ATTENTION: Only use this when not using dhcp_set_struct() to allocate the
+ * struct dhcp since the memory is passed back to the heap.
+ *
+ * @param netif the netif from which to remove the struct dhcp
+ */
+void ICACHE_FLASH_ATTR dhcp_cleanup(struct netif *netif)
+{
+ LWIP_ASSERT("netif != NULL", netif != NULL);
+
+ if (netif->dhcp != NULL) {
+ mem_free(netif->dhcp);
+ netif->dhcp = NULL;
+ }
+}
+
+/**
+ * Start DHCP negotiation for a network interface.
+ *
+ * If no DHCP client instance was attached to this interface,
+ * a new client is created first. If a DHCP client instance
+ * was already present, it restarts negotiation.
+ *
+ * @param netif The lwIP network interface
+ * @return lwIP error code
+ * - ERR_OK - No error
+ * - ERR_MEM - Out of memory
+ */
+err_t ICACHE_FLASH_ATTR
+dhcp_start(struct netif *netif)
+{
+ struct dhcp *dhcp;
+ err_t result = ERR_OK;
+
+ LWIP_ERROR("netif != NULL", (netif != NULL), return ERR_ARG;);
+ dhcp = netif->dhcp;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_start(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+ /* Remove the flag that says this netif is handled by DHCP,
+ it is set when we succeeded starting. */
+ netif->flags &= ~NETIF_FLAG_DHCP;
+
+ /* check hwtype of the netif */
+ if ((netif->flags & NETIF_FLAG_ETHARP) == 0) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): No ETHARP netif\n"));
+ return ERR_ARG;
+ }
+
+ /* check MTU of the netif */
+ if (netif->mtu < DHCP_MAX_MSG_LEN_MIN_REQUIRED) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): Cannot use this netif with DHCP: MTU is too small\n"));
+ return ERR_MEM;
+ }
+
+ /* no DHCP client attached yet? */
+ if (dhcp == NULL) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): starting new DHCP client\n"));
+ dhcp = (struct dhcp *)mem_malloc(sizeof(struct dhcp));
+ if (dhcp == NULL) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): could not allocate dhcp\n"));
+ return ERR_MEM;
+ }
+ /* store this dhcp client in the netif */
+ netif->dhcp = dhcp;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): allocated dhcp"));
+ /* already has DHCP client attached */
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_start(): restarting DHCP configuration\n"));
+ if (dhcp->pcb != NULL) {
+ udp_remove(dhcp->pcb);
+ }
+ LWIP_ASSERT("pbuf p_out wasn't freed", dhcp->p_out == NULL);
+ LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL );
+ }
+
+ /* clear data structure */
+ memset(dhcp, 0, sizeof(struct dhcp));
+ /* dhcp_set_state(&dhcp, DHCP_OFF); */
+ /* allocate UDP PCB */
+ dhcp->pcb = udp_new();
+ if (dhcp->pcb == NULL) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): could not obtain pcb\n"));
+ return ERR_MEM;
+ }
+ dhcp->pcb->so_options |= SOF_BROADCAST;
+ /* set up local and remote port for the pcb */
+ udp_bind(dhcp->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT);
+ udp_connect(dhcp->pcb, IP_ADDR_ANY, DHCP_SERVER_PORT);
+ /* set up the recv callback and argument */
+ udp_recv(dhcp->pcb, dhcp_recv, netif);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): starting DHCP configuration\n"));
+ /* (re)start the DHCP negotiation */
+ result = dhcp_discover(netif);
+ if (result != ERR_OK) {
+ /* free resources allocated above */
+ dhcp_stop(netif);
+ return ERR_MEM;
+ }
+ /* Set the flag that says this netif is handled by DHCP. */
+ netif->flags |= NETIF_FLAG_DHCP;
+ return result;
+}
+
+/**
+ * Inform a DHCP server of our manual configuration.
+ *
+ * This informs DHCP servers of our fixed IP address configuration
+ * by sending an INFORM message. It does not involve DHCP address
+ * configuration, it is just here to be nice to the network.
+ *
+ * @param netif The lwIP network interface
+ */
+void ICACHE_FLASH_ATTR
+dhcp_inform(struct netif *netif)
+{
+ struct dhcp dhcp;
+ err_t result = ERR_OK;
+ struct udp_pcb *pcb;
+
+ LWIP_ERROR("netif != NULL", (netif != NULL), return;);
+
+ memset(&dhcp, 0, sizeof(struct dhcp));
+ dhcp_set_state(&dhcp, DHCP_INFORM);
+
+ if ((netif->dhcp != NULL) && (netif->dhcp->pcb != NULL)) {
+ /* re-use existing pcb */
+ pcb = netif->dhcp->pcb;
+ } else {
+ pcb = udp_new();
+ if (pcb == NULL) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_inform(): could not obtain pcb"));
+ return;
+ }
+ dhcp.pcb = pcb;
+ dhcp.pcb->so_options |= SOF_BROADCAST;
+ udp_bind(dhcp.pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_inform(): created new udp pcb\n"));
+ }
+ /* create and initialize the DHCP message header */
+ result = dhcp_create_msg(netif, &dhcp, DHCP_INFORM);
+ if (result == ERR_OK) {
+ dhcp_option(&dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ dhcp_option_short(&dhcp, DHCP_MAX_MSG_LEN(netif));
+
+ dhcp_option_trailer(&dhcp);
+
+ pbuf_realloc(dhcp.p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp.options_out_len);
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_inform: INFORMING\n"));
+ udp_sendto_if(pcb, dhcp.p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
+ dhcp_delete_msg(&dhcp);
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_inform: could not allocate DHCP request\n"));
+ }
+
+ if (dhcp.pcb != NULL) {
+ /* otherwise, the existing pcb was used */
+ udp_remove(dhcp.pcb);
+ }
+}
+
+/** Handle a possible change in the network configuration.
+ *
+ * This enters the REBOOTING state to verify that the currently bound
+ * address is still valid.
+ */
+void ICACHE_FLASH_ATTR
+dhcp_network_changed(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ if (!dhcp)
+ return;
+ switch (dhcp->state) {
+ case DHCP_REBINDING:
+ case DHCP_RENEWING:
+ case DHCP_BOUND:
+ case DHCP_REBOOTING:
+ netif_set_down(netif);
+ dhcp->tries = 0;
+ dhcp_reboot(netif);
+ break;
+ case DHCP_OFF:
+ /* stay off */
+ break;
+ default:
+ dhcp->tries = 0;
+#if LWIP_DHCP_AUTOIP_COOP
+ if(dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) {
+ autoip_stop(netif);
+ dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF;
+ }
+#endif /* LWIP_DHCP_AUTOIP_COOP */
+ dhcp_discover(netif);
+ break;
+ }
+}
+
+#if DHCP_DOES_ARP_CHECK
+/**
+ * Match an ARP reply with the offered IP address.
+ *
+ * @param netif the network interface on which the reply was received
+ * @param addr The IP address we received a reply from
+ */
+void ICACHE_FLASH_ATTR dhcp_arp_reply(struct netif *netif, ip_addr_t *addr)
+{
+ LWIP_ERROR("netif != NULL", (netif != NULL), return;);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_arp_reply()\n"));
+ /* is a DHCP client doing an ARP check? */
+ if ((netif->dhcp != NULL) && (netif->dhcp->state == DHCP_CHECKING)) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_arp_reply(): CHECKING, arp reply for 0x%08"X32_F"\n",
+ ip4_addr_get_u32(addr)));
+ /* did a host respond with the address we
+ were offered by the DHCP server? */
+ if (ip_addr_cmp(addr, &netif->dhcp->offered_ip_addr)) {
+ /* we will not accept the offered address */
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
+ ("dhcp_arp_reply(): arp reply matched with offered address, declining\n"));
+ dhcp_decline(netif);
+ }
+ }
+}
+
+/**
+ * Decline an offered lease.
+ *
+ * Tell the DHCP server we do not accept the offered address.
+ * One reason to decline the lease is when we find out the address
+ * is already in use by another host (through ARP).
+ *
+ * @param netif the netif under DHCP control
+ */
+static err_t ICACHE_FLASH_ATTR
+dhcp_decline(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ err_t result = ERR_OK;
+ u16_t msecs;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_decline()\n"));
+ dhcp_set_state(dhcp, DHCP_BACKING_OFF);
+ /* create and initialize the DHCP message header */
+ result = dhcp_create_msg(netif, dhcp, DHCP_DECLINE);
+ if (result == ERR_OK) {
+ dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4);
+ dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr)));
+
+ dhcp_option_trailer(dhcp);
+ /* resize pbuf to reflect true size of options */
+ pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+ /* per section 4.4.4, broadcast DECLINE messages */
+ udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
+ dhcp_delete_msg(dhcp);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_decline: BACKING OFF\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("dhcp_decline: could not allocate DHCP request\n"));
+ }
+ dhcp->tries++;
+ msecs = 10*1000;
+ dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_decline(): set request timeout %"U16_F" msecs\n", msecs));
+ return result;
+}
+#endif /* DHCP_DOES_ARP_CHECK */
+
+
+/**
+ * Start the DHCP process, discover a DHCP server.
+ *
+ * @param netif the netif under DHCP control
+ */
+static err_t ICACHE_FLASH_ATTR
+dhcp_discover(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ err_t result = ERR_OK;
+ u16_t msecs;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover()\n"));
+ ip_addr_set_any(&dhcp->offered_ip_addr);
+ dhcp_set_state(dhcp, DHCP_SELECTING);
+ /* create and initialize the DHCP message header */
+ result = dhcp_create_msg(netif, dhcp, DHCP_DISCOVER);
+ if (result == ERR_OK) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: making request\n"));
+
+ dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif));
+
+ dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, 4/*num options*/);
+ dhcp_option_byte(dhcp, DHCP_OPTION_SUBNET_MASK);
+ dhcp_option_byte(dhcp, DHCP_OPTION_ROUTER);
+ dhcp_option_byte(dhcp, DHCP_OPTION_BROADCAST);
+ dhcp_option_byte(dhcp, DHCP_OPTION_DNS_SERVER);
+
+ dhcp_option_trailer(dhcp);
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: realloc()ing\n"));
+ pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: sendto(DISCOVER, IP_ADDR_BROADCAST, DHCP_SERVER_PORT)\n"));
+ udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: deleting()ing\n"));
+ dhcp_delete_msg(dhcp);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_discover: SELECTING\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_discover: could not allocate DHCP request\n"));
+ }
+ dhcp->tries++;
+#if LWIP_DHCP_AUTOIP_COOP
+ if(dhcp->tries >= LWIP_DHCP_AUTOIP_COOP_TRIES && dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_OFF) {
+ dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_ON;
+ autoip_start(netif);
+ }
+#endif /* LWIP_DHCP_AUTOIP_COOP */
+ msecs = (dhcp->tries < 6 ? 1 << dhcp->tries : 60) * 1000;
+ dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_discover(): set request timeout %"U16_F" msecs\n", msecs));
+ return result;
+}
+
+
+/**
+ * Bind the interface to the offered IP address.
+ *
+ * @param netif network interface to bind to the offered address
+ */
+static void ICACHE_FLASH_ATTR
+dhcp_bind(struct netif *netif)
+{
+ u32_t timeout;
+ struct dhcp *dhcp;
+ ip_addr_t sn_mask, gw_addr;
+ LWIP_ERROR("dhcp_bind: netif != NULL", (netif != NULL), return;);
+ dhcp = netif->dhcp;
+ LWIP_ERROR("dhcp_bind: dhcp != NULL", (dhcp != NULL), return;);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+
+ /* temporary DHCP lease? */
+ if (dhcp->offered_t1_renew != 0xffffffffUL) {
+ /* set renewal period timer */
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t1 renewal timer %"U32_F" secs\n", dhcp->offered_t1_renew));
+ timeout = (dhcp->offered_t1_renew + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS;
+ if(timeout > 0xffff) {
+ timeout = 0xffff;
+ }
+ dhcp->t1_timeout = (u16_t)timeout;
+ if (dhcp->t1_timeout == 0) {
+ dhcp->t1_timeout = 1;
+ }
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t1_renew*1000));
+ }
+ /* set renewal period timer */
+ if (dhcp->offered_t2_rebind != 0xffffffffUL) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t2 rebind timer %"U32_F" secs\n", dhcp->offered_t2_rebind));
+ timeout = (dhcp->offered_t2_rebind + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS;
+ if(timeout > 0xffff) {
+ timeout = 0xffff;
+ }
+ dhcp->t2_timeout = (u16_t)timeout;
+ if (dhcp->t2_timeout == 0) {
+ dhcp->t2_timeout = 1;
+ }
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t2_rebind*1000));
+ }
+
+ /* If we have sub 1 minute lease, t2 and t1 will kick in at the same time. modify by ives at 2014.4.22*/
+ if ((dhcp->t1_timeout >= dhcp->t2_timeout) && (dhcp->t2_timeout > 0)) {
+ dhcp->t1_timeout = 0;
+ }
+
+ if (dhcp->subnet_mask_given) {
+ /* copy offered network mask */
+ ip_addr_copy(sn_mask, dhcp->offered_sn_mask);
+ } else {
+ /* subnet mask not given, choose a safe subnet mask given the network class */
+ u8_t first_octet = ip4_addr1(&dhcp->offered_ip_addr);
+ if (first_octet <= 127) {
+ ip4_addr_set_u32(&sn_mask, PP_HTONL(0xff000000));
+ } else if (first_octet >= 192) {
+ ip4_addr_set_u32(&sn_mask, PP_HTONL(0xffffff00));
+ } else {
+ ip4_addr_set_u32(&sn_mask, PP_HTONL(0xffff0000));
+ }
+ }
+
+ ip_addr_copy(gw_addr, dhcp->offered_gw_addr);
+ /* gateway address not given? */
+ if (ip_addr_isany(&gw_addr)) {
+ /* copy network address */
+ ip_addr_get_network(&gw_addr, &dhcp->offered_ip_addr, &sn_mask);
+ /* use first host address on network as gateway */
+ ip4_addr_set_u32(&gw_addr, ip4_addr_get_u32(&gw_addr) | PP_HTONL(0x00000001));
+ }
+
+#if LWIP_DHCP_AUTOIP_COOP
+ if(dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) {
+ autoip_stop(netif);
+ dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF;
+ }
+#endif /* LWIP_DHCP_AUTOIP_COOP */
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): IP: 0x%08"X32_F"\n",
+ ip4_addr_get_u32(&dhcp->offered_ip_addr)));
+ netif_set_ipaddr(netif, &dhcp->offered_ip_addr);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): SN: 0x%08"X32_F"\n",
+ ip4_addr_get_u32(&sn_mask)));
+ netif_set_netmask(netif, &sn_mask);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): GW: 0x%08"X32_F"\n",
+ ip4_addr_get_u32(&gw_addr)));
+
+ system_station_got_ip_set(&dhcp->offered_ip_addr, &sn_mask, &gw_addr);
+
+ netif_set_gw(netif, &gw_addr);
+ /* bring the interface up */
+ netif_set_up(netif);
+ /* netif is now bound to DHCP leased address */
+ dhcp_set_state(dhcp, DHCP_BOUND);
+}
+
+/**
+ * Renew an existing DHCP lease at the involved DHCP server.
+ *
+ * @param netif network interface which must renew its lease
+ */
+err_t ICACHE_FLASH_ATTR
+dhcp_renew(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ err_t result;
+ u16_t msecs;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_renew()\n"));
+ dhcp_set_state(dhcp, DHCP_RENEWING);
+
+ /* create and initialize the DHCP message header */
+ result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST);
+ if (result == ERR_OK) {
+ dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif));
+
+#if LWIP_NETIF_HOSTNAME
+ if (netif->hostname != NULL) {
+ const char *p = (const char*)netif->hostname;
+ u8_t namelen = (u8_t)strlen(p);
+ if (namelen > 0) {
+ LWIP_ASSERT("DHCP: hostname is too long!", namelen < 255);
+ dhcp_option(dhcp, DHCP_OPTION_HOSTNAME, namelen);
+ while (*p) {
+ dhcp_option_byte(dhcp, *p++);
+ }
+ }
+ }
+#endif /* LWIP_NETIF_HOSTNAME */
+
+#if 0
+ dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4);
+ dhcp_option_long(dhcp, ntohl(dhcp->offered_ip_addr.addr));
+#endif
+
+#if 0
+ dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4);
+ dhcp_option_long(dhcp, ntohl(dhcp->server_ip_addr.addr));
+#endif
+ /* append DHCP message trailer */
+ dhcp_option_trailer(dhcp);
+
+ pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+ udp_sendto_if(dhcp->pcb, dhcp->p_out, &dhcp->server_ip_addr, DHCP_SERVER_PORT, netif);
+ dhcp_delete_msg(dhcp);
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_renew: RENEWING\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_renew: could not allocate DHCP request\n"));
+ }
+ dhcp->tries++;
+ /* back-off on retries, but to a maximum of 20 seconds */
+ msecs = dhcp->tries < 10 ? dhcp->tries * 2000 : 20 * 1000;
+ dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_renew(): set request timeout %"U16_F" msecs\n", msecs));
+ return result;
+}
+
+/**
+ * Rebind with a DHCP server for an existing DHCP lease.
+ *
+ * @param netif network interface which must rebind with a DHCP server
+ */
+static err_t ICACHE_FLASH_ATTR
+dhcp_rebind(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ err_t result;
+ u16_t msecs;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind()\n"));
+ dhcp_set_state(dhcp, DHCP_REBINDING);
+
+ /* create and initialize the DHCP message header */
+ result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST);
+ if (result == ERR_OK) {
+ dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif));
+
+#if LWIP_NETIF_HOSTNAME
+ if (netif->hostname != NULL) {
+ const char *p = (const char*)netif->hostname;
+ u8_t namelen = (u8_t)strlen(p);
+ if (namelen > 0) {
+ LWIP_ASSERT("DHCP: hostname is too long!", namelen < 255);
+ dhcp_option(dhcp, DHCP_OPTION_HOSTNAME, namelen);
+ while (*p) {
+ dhcp_option_byte(dhcp, *p++);
+ }
+ }
+ }
+#endif /* LWIP_NETIF_HOSTNAME */
+
+#if 0
+ dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4);
+ dhcp_option_long(dhcp, ntohl(dhcp->offered_ip_addr.addr));
+
+ dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4);
+ dhcp_option_long(dhcp, ntohl(dhcp->server_ip_addr.addr));
+#endif
+
+ dhcp_option_trailer(dhcp);
+
+ pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+ /* broadcast to server */
+ udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
+ dhcp_delete_msg(dhcp);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind: REBINDING\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_rebind: could not allocate DHCP request\n"));
+ }
+ dhcp->tries++;
+ msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000;
+ dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind(): set request timeout %"U16_F" msecs\n", msecs));
+ return result;
+}
+
+/**
+ * Enter REBOOTING state to verify an existing lease
+ *
+ * @param netif network interface which must reboot
+ */
+static err_t ICACHE_FLASH_ATTR
+dhcp_reboot(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ err_t result;
+ u16_t msecs;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot()\n"));
+ dhcp_set_state(dhcp, DHCP_REBOOTING);
+
+ /* create and initialize the DHCP message header */
+ result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST);
+ if (result == ERR_OK) {
+ dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ dhcp_option_short(dhcp, 576);
+
+ dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4);
+ dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr)));
+
+ dhcp_option_trailer(dhcp);
+
+ pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+ /* broadcast to server */
+ udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
+ dhcp_delete_msg(dhcp);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot: REBOOTING\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_reboot: could not allocate DHCP request\n"));
+ }
+ dhcp->tries++;
+ msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000;
+ dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot(): set request timeout %"U16_F" msecs\n", msecs));
+ return result;
+}
+
+
+/**
+ * Release a DHCP lease.
+ *
+ * @param netif network interface which must release its lease
+ */
+err_t ICACHE_FLASH_ATTR
+dhcp_release(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ err_t result;
+ u16_t msecs;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_release()\n"));
+ if (dhcp == NULL) {
+ return ERR_ARG;
+ }
+
+ /* idle DHCP client */
+ dhcp_set_state(dhcp, DHCP_OFF);
+ /* clean old DHCP offer */
+ ip_addr_set_zero(&dhcp->server_ip_addr);
+ ip_addr_set_zero(&dhcp->offered_ip_addr);
+ ip_addr_set_zero(&dhcp->offered_sn_mask);
+ ip_addr_set_zero(&dhcp->offered_gw_addr);
+#if LWIP_DHCP_BOOTP_FILE
+ ip_addr_set_zero(&dhcp->offered_si_addr);
+#endif /* LWIP_DHCP_BOOTP_FILE */
+ dhcp->offered_t0_lease = dhcp->offered_t1_renew = dhcp->offered_t2_rebind = 0;
+
+ /* create and initialize the DHCP message header */
+ result = dhcp_create_msg(netif, dhcp, DHCP_RELEASE);
+ if (result == ERR_OK) {
+ dhcp_option_trailer(dhcp);
+
+ pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+ udp_sendto_if(dhcp->pcb, dhcp->p_out, &dhcp->server_ip_addr, DHCP_SERVER_PORT, netif);
+ dhcp_delete_msg(dhcp);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_release: RELEASED, DHCP_OFF\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_release: could not allocate DHCP request\n"));
+ }
+ dhcp->tries++;
+ msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000;
+ dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_release(): set request timeout %"U16_F" msecs\n", msecs));
+ /* bring the interface down */
+ netif_set_down(netif);
+ /* remove IP address from interface */
+ netif_set_ipaddr(netif, IP_ADDR_ANY);
+ netif_set_gw(netif, IP_ADDR_ANY);
+ netif_set_netmask(netif, IP_ADDR_ANY);
+
+ return result;
+}
+
+/**
+ * Remove the DHCP client from the interface.
+ *
+ * @param netif The network interface to stop DHCP on
+ */
+void ICACHE_FLASH_ATTR
+dhcp_stop(struct netif *netif)
+{
+ struct dhcp *dhcp;
+ LWIP_ERROR("dhcp_stop: netif != NULL", (netif != NULL), return;);
+ dhcp = netif->dhcp;
+ /* Remove the flag that says this netif is handled by DHCP. */
+ netif->flags &= ~NETIF_FLAG_DHCP;
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_stop()\n"));
+ /* netif is DHCP configured? */
+ if (dhcp != NULL) {
+#if LWIP_DHCP_AUTOIP_COOP
+ if(dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) {
+ autoip_stop(netif);
+ dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF;
+ }
+#endif /* LWIP_DHCP_AUTOIP_COOP */
+
+ if (dhcp->pcb != NULL) {
+ udp_remove(dhcp->pcb);
+ dhcp->pcb = NULL;
+ }
+ LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL);
+ dhcp_set_state(dhcp, DHCP_OFF);
+ }
+}
+
+/*
+ * Set the DHCP state of a DHCP client.
+ *
+ * If the state changed, reset the number of tries.
+ */
+static void ICACHE_FLASH_ATTR
+dhcp_set_state(struct dhcp *dhcp, u8_t new_state)
+{
+ if (new_state != dhcp->state) {
+ dhcp->state = new_state;
+ dhcp->tries = 0;
+ dhcp->request_timeout = 0;
+ }
+}
+
+/*
+ * Concatenate an option type and length field to the outgoing
+ * DHCP message.
+ *
+ */
+static void ICACHE_FLASH_ATTR
+dhcp_option(struct dhcp *dhcp, u8_t option_type, u8_t option_len)
+{
+ LWIP_ASSERT("dhcp_option: dhcp->options_out_len + 2 + option_len <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 2U + option_len <= DHCP_OPTIONS_LEN);
+ dhcp->msg_out->options[dhcp->options_out_len++] = option_type;
+ dhcp->msg_out->options[dhcp->options_out_len++] = option_len;
+}
+/*
+ * Concatenate a single byte to the outgoing DHCP message.
+ *
+ */
+static void ICACHE_FLASH_ATTR
+dhcp_option_byte(struct dhcp *dhcp, u8_t value)
+{
+ LWIP_ASSERT("dhcp_option_byte: dhcp->options_out_len < DHCP_OPTIONS_LEN", dhcp->options_out_len < DHCP_OPTIONS_LEN);
+ dhcp->msg_out->options[dhcp->options_out_len++] = value;
+}
+
+static void ICACHE_FLASH_ATTR
+dhcp_option_short(struct dhcp *dhcp, u16_t value)
+{
+ LWIP_ASSERT("dhcp_option_short: dhcp->options_out_len + 2 <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 2U <= DHCP_OPTIONS_LEN);
+ dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0xff00U) >> 8);
+ dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t) (value & 0x00ffU);
+}
+
+static void ICACHE_FLASH_ATTR
+dhcp_option_long(struct dhcp *dhcp, u32_t value)
+{
+ LWIP_ASSERT("dhcp_option_long: dhcp->options_out_len + 4 <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 4U <= DHCP_OPTIONS_LEN);
+ dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0xff000000UL) >> 24);
+ dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x00ff0000UL) >> 16);
+ dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x0000ff00UL) >> 8);
+ dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x000000ffUL));
+}
+
+/**
+ * Extract the DHCP message and the DHCP options.
+ *
+ * Extract the DHCP message and the DHCP options, each into a contiguous
+ * piece of memory. As a DHCP message is variable sized by its options,
+ * and also allows overriding some fields for options, the easy approach
+ * is to first unfold the options into a conitguous piece of memory, and
+ * use that further on.
+ *
+ */
+static err_t ICACHE_FLASH_ATTR
+dhcp_parse_reply(struct dhcp *dhcp, struct pbuf *p)
+{
+ u8_t *options;
+ u16_t offset;
+ u16_t offset_max;
+ u16_t options_idx;
+ u16_t options_idx_max;
+ struct pbuf *q;
+ int parse_file_as_options = 0;
+ int parse_sname_as_options = 0;
+
+ /* clear received options */
+ dhcp_clear_all_options(dhcp);
+ /* check that beginning of dhcp_msg (up to and including chaddr) is in first pbuf */
+ if (p->len < DHCP_SNAME_OFS) {
+ return ERR_BUF;
+ }
+ dhcp->msg_in = (struct dhcp_msg *)p->payload;
+#if LWIP_DHCP_BOOTP_FILE
+ /* clear boot file name */
+ dhcp->boot_file_name[0] = 0;
+#endif /* LWIP_DHCP_BOOTP_FILE */
+
+ /* parse options */
+
+ /* start with options field */
+ options_idx = DHCP_OPTIONS_OFS;
+ /* parse options to the end of the received packet */
+ options_idx_max = p->tot_len;
+again:
+ q = p;
+ while((q != NULL) && (options_idx >= q->len)) {
+ options_idx -= q->len;
+ options_idx_max -= q->len;
+ q = q->next;
+ }
+ if (q == NULL) {
+ return ERR_BUF;
+ }
+ offset = options_idx;
+ offset_max = options_idx_max;
+ options = (u8_t*)q->payload;
+ /* at least 1 byte to read and no end marker, then at least 3 bytes to read? */
+ while((q != NULL) && (options[offset] != DHCP_OPTION_END) && (offset < offset_max)) {
+ u8_t op = options[offset];
+ u8_t len;
+ u8_t decode_len = 0;
+ int decode_idx = -1;
+ u16_t val_offset = offset + 2;
+ /* len byte might be in the next pbuf */
+ if (offset + 1 < q->len) {
+ len = options[offset + 1];
+ } else {
+ len = (q->next != NULL ? ((u8_t*)q->next->payload)[0] : 0);
+ }
+ /* LWIP_DEBUGF(DHCP_DEBUG, ("msg_offset=%"U16_F", q->len=%"U16_F, msg_offset, q->len)); */
+ decode_len = len;
+ switch(op) {
+ /* case(DHCP_OPTION_END): handled above */
+ case(DHCP_OPTION_PAD):
+ /* special option: no len encoded */
+ decode_len = len = 0;
+ /* will be increased below */
+ offset--;
+ break;
+ case(DHCP_OPTION_SUBNET_MASK):
+ LWIP_ASSERT("len == 4", len == 4);
+ decode_idx = DHCP_OPTION_IDX_SUBNET_MASK;
+ break;
+ case(DHCP_OPTION_ROUTER):
+ decode_len = 4; /* only copy the first given router */
+ LWIP_ASSERT("len >= decode_len", len >= decode_len);
+ decode_idx = DHCP_OPTION_IDX_ROUTER;
+ break;
+ case(DHCP_OPTION_DNS_SERVER):
+ /* special case: there might be more than one server */
+ LWIP_ASSERT("len % 4 == 0", len % 4 == 0);
+ /* limit number of DNS servers */
+ decode_len = LWIP_MIN(len, 4 * DNS_MAX_SERVERS);
+ LWIP_ASSERT("len >= decode_len", len >= decode_len);
+ decode_idx = DHCP_OPTION_IDX_DNS_SERVER;
+ break;
+ case(DHCP_OPTION_LEASE_TIME):
+ LWIP_ASSERT("len == 4", len == 4);
+ decode_idx = DHCP_OPTION_IDX_LEASE_TIME;
+ break;
+ case(DHCP_OPTION_OVERLOAD):
+ LWIP_ASSERT("len == 1", len == 1);
+ decode_idx = DHCP_OPTION_IDX_OVERLOAD;
+ break;
+ case(DHCP_OPTION_MESSAGE_TYPE):
+ LWIP_ASSERT("len == 1", len == 1);
+ decode_idx = DHCP_OPTION_IDX_MSG_TYPE;
+ break;
+ case(DHCP_OPTION_SERVER_ID):
+ LWIP_ASSERT("len == 4", len == 4);
+ decode_idx = DHCP_OPTION_IDX_SERVER_ID;
+ break;
+ case(DHCP_OPTION_T1):
+ LWIP_ASSERT("len == 4", len == 4);
+ decode_idx = DHCP_OPTION_IDX_T1;
+ break;
+ case(DHCP_OPTION_T2):
+ LWIP_ASSERT("len == 4", len == 4);
+ decode_idx = DHCP_OPTION_IDX_T2;
+ break;
+ default:
+ decode_len = 0;
+ LWIP_DEBUGF(DHCP_DEBUG, ("skipping option %"U16_F" in options\n", op));
+ break;
+ }
+ offset += len + 2;
+ if (decode_len > 0) {
+ u32_t value = 0;
+ u16_t copy_len;
+decode_next:
+ LWIP_ASSERT("check decode_idx", decode_idx >= 0 && decode_idx < DHCP_OPTION_IDX_MAX);
+ LWIP_ASSERT("option already decoded", !dhcp_option_given(dhcp, decode_idx));
+ copy_len = LWIP_MIN(decode_len, 4);
+ pbuf_copy_partial(q, &value, copy_len, val_offset);
+ if (decode_len > 4) {
+ /* decode more than one u32_t */
+ LWIP_ASSERT("decode_len % 4 == 0", decode_len % 4 == 0);
+ dhcp_got_option(dhcp, decode_idx);
+ dhcp_set_option_value(dhcp, decode_idx, htonl(value));
+ decode_len -= 4;
+ val_offset += 4;
+ decode_idx++;
+ goto decode_next;
+ } else if (decode_len == 4) {
+ value = ntohl(value);
+ } else {
+ LWIP_ASSERT("invalid decode_len", decode_len == 1);
+ value = ((u8_t*)&value)[0];
+ }
+ dhcp_got_option(dhcp, decode_idx);
+ dhcp_set_option_value(dhcp, decode_idx, value);
+ }
+ if (offset >= q->len) {
+ offset -= q->len;
+ offset_max -= q->len;
+ if ((offset < offset_max) && offset_max) { //modify by ives at 2014.4.22
+ q = q->next;
+ LWIP_ASSERT("next pbuf was null", q);
+ options = (u8_t*)q->payload;
+ } else {
+ /* We've run out of bytes, probably no end marker. Don't proceed. */
+ break;
+ }
+ }
+ }
+ /* is this an overloaded message? */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_OVERLOAD)) {
+ u32_t overload = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_OVERLOAD);
+ dhcp_clear_option(dhcp, DHCP_OPTION_IDX_OVERLOAD);
+ if (overload == DHCP_OVERLOAD_FILE) {
+ parse_file_as_options = 1;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded file field\n"));
+ } else if (overload == DHCP_OVERLOAD_SNAME) {
+ parse_sname_as_options = 1;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded sname field\n"));
+ } else if (overload == DHCP_OVERLOAD_SNAME_FILE) {
+ parse_sname_as_options = 1;
+ parse_file_as_options = 1;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded sname and file field\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("invalid overload option: %d\n", (int)overload));
+ }
+#if LWIP_DHCP_BOOTP_FILE
+ if (!parse_file_as_options) {
+ /* only do this for ACK messages */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_MSG_TYPE) &&
+ (dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_MSG_TYPE) == DHCP_ACK))
+ /* copy bootp file name, don't care for sname (server hostname) */
+ pbuf_copy_partial(p, dhcp->boot_file_name, DHCP_FILE_LEN-1, DHCP_FILE_OFS);
+ /* make sure the string is really NULL-terminated */
+ dhcp->boot_file_name[DHCP_FILE_LEN-1] = 0;
+ }
+#endif /* LWIP_DHCP_BOOTP_FILE */
+ }
+ if (parse_file_as_options) {
+ /* if both are overloaded, parse file first and then sname (RFC 2131 ch. 4.1) */
+ parse_file_as_options = 0;
+ options_idx = DHCP_FILE_OFS;
+ options_idx_max = DHCP_FILE_OFS + DHCP_FILE_LEN;
+ goto again;
+ } else if (parse_sname_as_options) {
+ parse_sname_as_options = 0;
+ options_idx = DHCP_SNAME_OFS;
+ options_idx_max = DHCP_SNAME_OFS + DHCP_SNAME_LEN;
+ goto again;
+ }
+ return ERR_OK;
+}
+
+/**
+ * If an incoming DHCP message is in response to us, then trigger the state machine
+ */
+static void ICACHE_FLASH_ATTR
+dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port)
+{
+ struct netif *netif = (struct netif *)arg;
+ struct dhcp *dhcp = netif->dhcp;
+ struct dhcp_msg *reply_msg = (struct dhcp_msg *)p->payload;
+ u8_t msg_type;
+ u8_t i;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_recv(pbuf = %p) from DHCP server %"U16_F".%"U16_F".%"U16_F".%"U16_F" port %"U16_F"\n", (void*)p,
+ ip4_addr1_16(addr), ip4_addr2_16(addr), ip4_addr3_16(addr), ip4_addr4_16(addr), port));
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("pbuf->len = %"U16_F"\n", p->len));
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("pbuf->tot_len = %"U16_F"\n", p->tot_len));
+ /* prevent warnings about unused arguments */
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_UNUSED_ARG(addr);
+ LWIP_UNUSED_ARG(port);
+
+ LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL);
+
+ if (p->len < DHCP_MIN_REPLY_LEN) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCP reply message or pbuf too short\n"));
+ goto free_pbuf_and_return;
+ }
+
+ if (reply_msg->op != DHCP_BOOTREPLY) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("not a DHCP reply message, but type %"U16_F"\n", (u16_t)reply_msg->op));
+ goto free_pbuf_and_return;
+ }
+ /* iterate through hardware address and match against DHCP message */
+ for (i = 0; i < netif->hwaddr_len; i++) {
+ if (netif->hwaddr[i] != reply_msg->chaddr[i]) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+ ("netif->hwaddr[%"U16_F"]==%02"X16_F" != reply_msg->chaddr[%"U16_F"]==%02"X16_F"\n",
+ (u16_t)i, (u16_t)netif->hwaddr[i], (u16_t)i, (u16_t)reply_msg->chaddr[i]));
+ goto free_pbuf_and_return;
+ }
+ }
+ /* match transaction ID against what we expected */
+ if (ntohl(reply_msg->xid) != dhcp->xid) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+ ("transaction id mismatch reply_msg->xid(%"X32_F")!=dhcp->xid(%"X32_F")\n",ntohl(reply_msg->xid),dhcp->xid));
+ goto free_pbuf_and_return;
+ }
+ /* option fields could be unfold? */
+ if (dhcp_parse_reply(dhcp, p) != ERR_OK) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("problem unfolding DHCP message - too short on memory?\n"));
+ goto free_pbuf_and_return;
+ }
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("searching DHCP_OPTION_MESSAGE_TYPE\n"));
+ /* obtain pointer to DHCP message type */
+ if (!dhcp_option_given(dhcp, DHCP_OPTION_IDX_MSG_TYPE)) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCP_OPTION_MESSAGE_TYPE option not found\n"));
+ goto free_pbuf_and_return;
+ }
+
+ /* read DHCP message type */
+ msg_type = (u8_t)dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_MSG_TYPE);
+ /* message type is DHCP ACK? */
+ if (msg_type == DHCP_ACK) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_ACK received\n"));
+ /* in requesting state? */
+ if (dhcp->state == DHCP_REQUESTING) {
+ dhcp_handle_ack(netif);
+#if DHCP_DOES_ARP_CHECK
+ /* check if the acknowledged lease address is already in use */
+ dhcp_check(netif);
+#else
+ /* bind interface to the acknowledged lease address */
+ dhcp_bind(netif);
+#endif
+ }
+ /* already bound to the given lease address? */
+ else if ((dhcp->state == DHCP_REBOOTING) || (dhcp->state == DHCP_REBINDING) || (dhcp->state == DHCP_RENEWING)) {
+ dhcp_bind(netif);
+ }
+ }
+ /* received a DHCP_NAK in appropriate state? */
+ else if ((msg_type == DHCP_NAK) &&
+ ((dhcp->state == DHCP_REBOOTING) || (dhcp->state == DHCP_REQUESTING) ||
+ (dhcp->state == DHCP_REBINDING) || (dhcp->state == DHCP_RENEWING ))) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_NAK received\n"));
+ dhcp_handle_nak(netif);
+ }
+ /* received a DHCP_OFFER in DHCP_SELECTING state? */
+ else if ((msg_type == DHCP_OFFER) && (dhcp->state == DHCP_SELECTING)) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_OFFER received in DHCP_SELECTING state\n"));
+ dhcp->request_timeout = 0;
+ /* remember offered lease */
+ dhcp_handle_offer(netif);
+ }
+free_pbuf_and_return:
+ dhcp->msg_in = NULL;
+ pbuf_free(p);
+}
+
+/**
+ * Create a DHCP request, fill in common headers
+ *
+ * @param netif the netif under DHCP control
+ * @param dhcp dhcp control struct
+ * @param message_type message type of the request
+ */
+static err_t ICACHE_FLASH_ATTR
+dhcp_create_msg(struct netif *netif, struct dhcp *dhcp, u8_t message_type)
+{
+ u16_t i;
+#ifndef DHCP_GLOBAL_XID
+ /** default global transaction identifier starting value (easy to match
+ * with a packet analyser). We simply increment for each new request.
+ * Predefine DHCP_GLOBAL_XID to a better value or a function call to generate one
+ * at runtime, any supporting function prototypes can be defined in DHCP_GLOBAL_XID_HEADER */
+ static u32_t xid = 0xABCD0000;
+#else
+ static u32_t xid;
+ static u8_t xid_initialised = 0;
+ if (!xid_initialised) {
+ xid = DHCP_GLOBAL_XID;
+ xid_initialised = !xid_initialised;
+ }
+#endif
+ LWIP_ERROR("dhcp_create_msg: netif != NULL", (netif != NULL), return ERR_ARG;);
+ LWIP_ERROR("dhcp_create_msg: dhcp != NULL", (dhcp != NULL), return ERR_VAL;);
+ LWIP_ASSERT("dhcp_create_msg: dhcp->p_out == NULL", dhcp->p_out == NULL);
+ LWIP_ASSERT("dhcp_create_msg: dhcp->msg_out == NULL", dhcp->msg_out == NULL);
+ dhcp->p_out = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcp_msg), PBUF_RAM);
+ if (dhcp->p_out == NULL) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("dhcp_create_msg(): could not allocate pbuf\n"));
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("dhcp_create_msg: check that first pbuf can hold struct dhcp_msg",
+ (dhcp->p_out->len >= sizeof(struct dhcp_msg)));
+
+ /* DHCP_REQUEST should reuse 'xid' from DHCPOFFER modify by ives at 2014.4.22*/
+ if (message_type != DHCP_REQUEST) {
+ /* reuse transaction identifier in retransmissions */
+ if (dhcp->tries == 0) {
+ xid++;
+ }
+ dhcp->xid = xid;
+ }
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE,
+ ("transaction id xid(%"X32_F")\n", xid));
+
+ dhcp->msg_out = (struct dhcp_msg *)dhcp->p_out->payload;
+
+ dhcp->msg_out->op = DHCP_BOOTREQUEST;
+ /* TODO: make link layer independent */
+ dhcp->msg_out->htype = DHCP_HTYPE_ETH;
+ dhcp->msg_out->hlen = netif->hwaddr_len;
+ dhcp->msg_out->hops = 0;
+ dhcp->msg_out->xid = htonl(dhcp->xid);
+ dhcp->msg_out->secs = 0;
+ /* we don't need the broadcast flag since we can receive unicast traffic
+ before being fully configured! */
+ dhcp->msg_out->flags = 0;
+ ip_addr_set_zero(&dhcp->msg_out->ciaddr);
+ /* set ciaddr to netif->ip_addr based on message_type and state */
+ if ((message_type == DHCP_INFORM) || (message_type == DHCP_DECLINE) ||
+ ((message_type == DHCP_REQUEST) && /* DHCP_BOUND not used for sending! */
+ ((dhcp->state==DHCP_RENEWING) || dhcp->state==DHCP_REBINDING))) {
+ ip_addr_copy(dhcp->msg_out->ciaddr, netif->ip_addr);
+ }
+ ip_addr_set_zero(&dhcp->msg_out->yiaddr);
+ ip_addr_set_zero(&dhcp->msg_out->siaddr);
+ ip_addr_set_zero(&dhcp->msg_out->giaddr);
+ for (i = 0; i < DHCP_CHADDR_LEN; i++) {
+ /* copy netif hardware address, pad with zeroes */
+ dhcp->msg_out->chaddr[i] = (i < netif->hwaddr_len && i < NETIF_MAX_HWADDR_LEN) ? netif->hwaddr[i] : 0/* pad byte*/;
+ }
+ for (i = 0; i < DHCP_SNAME_LEN; i++) {
+ dhcp->msg_out->sname[i] = 0;
+ }
+ for (i = 0; i < DHCP_FILE_LEN; i++) {
+ dhcp->msg_out->file[i] = 0;
+ }
+ dhcp->msg_out->cookie = PP_HTONL(DHCP_MAGIC_COOKIE);
+ dhcp->options_out_len = 0;
+ /* fill options field with an incrementing array (for debugging purposes) */
+ for (i = 0; i < DHCP_OPTIONS_LEN; i++) {
+ dhcp->msg_out->options[i] = (u8_t)i; /* for debugging only, no matter if truncated */
+ }
+ /* Add option MESSAGE_TYPE */
+ dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN);
+ dhcp_option_byte(dhcp, message_type);
+ return ERR_OK;
+}
+
+/**
+ * Free previously allocated memory used to send a DHCP request.
+ *
+ * @param dhcp the dhcp struct to free the request from
+ */
+static void ICACHE_FLASH_ATTR
+dhcp_delete_msg(struct dhcp *dhcp)
+{
+ LWIP_ERROR("dhcp_delete_msg: dhcp != NULL", (dhcp != NULL), return;);
+ LWIP_ASSERT("dhcp_delete_msg: dhcp->p_out != NULL", dhcp->p_out != NULL);
+ LWIP_ASSERT("dhcp_delete_msg: dhcp->msg_out != NULL", dhcp->msg_out != NULL);
+ if (dhcp->p_out != NULL) {
+ pbuf_free(dhcp->p_out);
+ }
+ dhcp->p_out = NULL;
+ dhcp->msg_out = NULL;
+}
+
+/**
+ * Add a DHCP message trailer
+ *
+ * Adds the END option to the DHCP message, and if
+ * necessary, up to three padding bytes.
+ *
+ * @param dhcp DHCP state structure
+ */
+static void ICACHE_FLASH_ATTR
+dhcp_option_trailer(struct dhcp *dhcp)
+{
+ LWIP_ERROR("dhcp_option_trailer: dhcp != NULL", (dhcp != NULL), return;);
+ LWIP_ASSERT("dhcp_option_trailer: dhcp->msg_out != NULL\n", dhcp->msg_out != NULL);
+ LWIP_ASSERT("dhcp_option_trailer: dhcp->options_out_len < DHCP_OPTIONS_LEN\n", dhcp->options_out_len < DHCP_OPTIONS_LEN);
+ dhcp->msg_out->options[dhcp->options_out_len++] = DHCP_OPTION_END;
+ /* packet is too small, or not 4 byte aligned? */
+ while (((dhcp->options_out_len < DHCP_MIN_OPTIONS_LEN) || (dhcp->options_out_len & 3)) &&
+ (dhcp->options_out_len < DHCP_OPTIONS_LEN)) {
+ /* LWIP_DEBUGF(DHCP_DEBUG,("dhcp_option_trailer:dhcp->options_out_len=%"U16_F", DHCP_OPTIONS_LEN=%"U16_F, dhcp->options_out_len, DHCP_OPTIONS_LEN)); */
+ LWIP_ASSERT("dhcp_option_trailer: dhcp->options_out_len < DHCP_OPTIONS_LEN\n", dhcp->options_out_len < DHCP_OPTIONS_LEN);
+ /* add a fill/padding byte */
+ dhcp->msg_out->options[dhcp->options_out_len++] = 0;
+ }
+}
+
+#endif /* LWIP_DHCP */
diff --git a/app/lwip/core/dns.c b/app/lwip/core/dns.c
new file mode 100644
index 00000000..ab6821f2
--- /dev/null
+++ b/app/lwip/core/dns.c
@@ -0,0 +1,970 @@
+/**
+ * @file
+ * DNS - host name to IP address resolver.
+ *
+ */
+
+/**
+
+ * This file implements a DNS host name to IP address resolver.
+
+ * Port to lwIP from uIP
+ * by Jim Pettinato April 2007
+
+ * uIP version Copyright (c) 2002-2003, Adam Dunkels.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * DNS.C
+ *
+ * The lwIP DNS resolver functions are used to lookup a host name and
+ * map it to a numerical IP address. It maintains a list of resolved
+ * hostnames that can be queried with the dns_lookup() function.
+ * New hostnames can be resolved using the dns_query() function.
+ *
+ * The lwIP version of the resolver also adds a non-blocking version of
+ * gethostbyname() that will work with a raw API application. This function
+ * checks for an IP address string first and converts it if it is valid.
+ * gethostbyname() then does a dns_lookup() to see if the name is
+ * already in the table. If so, the IP is returned. If not, a query is
+ * issued and the function returns with a ERR_INPROGRESS status. The app
+ * using the dns client must then go into a waiting state.
+ *
+ * Once a hostname has been resolved (or found to be non-existent),
+ * the resolver code calls a specified callback function (which
+ * must be implemented by the module that uses the resolver).
+ */
+
+/*-----------------------------------------------------------------------------
+ * RFC 1035 - Domain names - implementation and specification
+ * RFC 2181 - Clarifications to the DNS Specification
+ *----------------------------------------------------------------------------*/
+
+/** @todo: define good default values (rfc compliance) */
+/** @todo: improve answer parsing, more checkings... */
+/** @todo: check RFC1035 - 7.3. Processing responses */
+
+/*-----------------------------------------------------------------------------
+ * Includes
+ *----------------------------------------------------------------------------*/
+
+#include "lwip/opt.h"
+
+#if LWIP_DNS /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/udp.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/dns.h"
+
+#include
+
+/** DNS server IP address */
+#ifndef DNS_SERVER_ADDRESS
+#define DNS_SERVER_ADDRESS(ipaddr) (ip4_addr_set_u32(ipaddr, ipaddr_addr("208.67.222.222"))) /* resolver1.opendns.com */
+#endif
+
+/** DNS server port address */
+#ifndef DNS_SERVER_PORT
+#define DNS_SERVER_PORT 53
+#endif
+
+/** DNS maximum number of retries when asking for a name, before "timeout". */
+#ifndef DNS_MAX_RETRIES
+#define DNS_MAX_RETRIES 4
+#endif
+
+/** DNS resource record max. TTL (one week as default) */
+#ifndef DNS_MAX_TTL
+#define DNS_MAX_TTL 604800
+#endif
+
+/* DNS protocol flags */
+#define DNS_FLAG1_RESPONSE 0x80
+#define DNS_FLAG1_OPCODE_STATUS 0x10
+#define DNS_FLAG1_OPCODE_INVERSE 0x08
+#define DNS_FLAG1_OPCODE_STANDARD 0x00
+#define DNS_FLAG1_AUTHORATIVE 0x04
+#define DNS_FLAG1_TRUNC 0x02
+#define DNS_FLAG1_RD 0x01
+#define DNS_FLAG2_RA 0x80
+#define DNS_FLAG2_ERR_MASK 0x0f
+#define DNS_FLAG2_ERR_NONE 0x00
+#define DNS_FLAG2_ERR_NAME 0x03
+
+/* DNS protocol states */
+#define DNS_STATE_UNUSED 0
+#define DNS_STATE_NEW 1
+#define DNS_STATE_ASKING 2
+#define DNS_STATE_DONE 3
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+/** DNS message header */
+struct dns_hdr {
+ PACK_STRUCT_FIELD(u16_t id);
+ PACK_STRUCT_FIELD(u8_t flags1);
+ PACK_STRUCT_FIELD(u8_t flags2);
+ PACK_STRUCT_FIELD(u16_t numquestions);
+ PACK_STRUCT_FIELD(u16_t numanswers);
+ PACK_STRUCT_FIELD(u16_t numauthrr);
+ PACK_STRUCT_FIELD(u16_t numextrarr);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+#define SIZEOF_DNS_HDR 12
+
+/** DNS query message structure.
+ No packing needed: only used locally on the stack. */
+struct dns_query {
+ /* DNS query record starts with either a domain name or a pointer
+ to a name already present somewhere in the packet. */
+ u16_t type;
+ u16_t cls;
+};
+#define SIZEOF_DNS_QUERY 4
+
+/** DNS answer message structure.
+ No packing needed: only used locally on the stack. */
+struct dns_answer {
+ /* DNS answer record starts with either a domain name or a pointer
+ to a name already present somewhere in the packet. */
+ u16_t type;
+ u16_t cls;
+ u32_t ttl;
+ u16_t len;
+};
+#define SIZEOF_DNS_ANSWER 10
+
+/** DNS table entry */
+struct dns_table_entry {
+ u8_t state;
+ u8_t numdns;
+ u8_t tmr;
+ u8_t retries;
+ u8_t seqno;
+ u8_t err;
+ u32_t ttl;
+ char name[DNS_MAX_NAME_LENGTH];
+ ip_addr_t ipaddr;
+ /* pointer to callback on DNS query done */
+ dns_found_callback found;
+ void *arg;
+};
+
+#if DNS_LOCAL_HOSTLIST
+
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+/** Local host-list. For hostnames in this list, no
+ * external name resolution is performed */
+static struct local_hostlist_entry *local_hostlist_dynamic;
+#else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+
+/** Defining this allows the local_hostlist_static to be placed in a different
+ * linker section (e.g. FLASH) */
+#ifndef DNS_LOCAL_HOSTLIST_STORAGE_PRE
+#define DNS_LOCAL_HOSTLIST_STORAGE_PRE static
+#endif /* DNS_LOCAL_HOSTLIST_STORAGE_PRE */
+/** Defining this allows the local_hostlist_static to be placed in a different
+ * linker section (e.g. FLASH) */
+#ifndef DNS_LOCAL_HOSTLIST_STORAGE_POST
+#define DNS_LOCAL_HOSTLIST_STORAGE_POST
+#endif /* DNS_LOCAL_HOSTLIST_STORAGE_POST */
+DNS_LOCAL_HOSTLIST_STORAGE_PRE struct local_hostlist_entry local_hostlist_static[]
+ DNS_LOCAL_HOSTLIST_STORAGE_POST = DNS_LOCAL_HOSTLIST_INIT;
+
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+
+static void dns_init_local();
+#endif /* DNS_LOCAL_HOSTLIST */
+
+
+/* forward declarations */
+static void dns_recv(void *s, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port);
+static void dns_check_entries(void);
+
+/*-----------------------------------------------------------------------------
+ * Globales
+ *----------------------------------------------------------------------------*/
+
+/* DNS variables */
+static struct udp_pcb *dns_pcb;
+static u8_t dns_seqno;
+static struct dns_table_entry dns_table[DNS_TABLE_SIZE];
+static ip_addr_t dns_servers[DNS_MAX_SERVERS];
+/** Contiguous buffer for processing responses */
+static u8_t dns_payload_buffer[LWIP_MEM_ALIGN_BUFFER(DNS_MSG_SIZE)];
+static u8_t* dns_payload;
+
+/**
+ * Initialize the resolver: set up the UDP pcb and configure the default server
+ * (DNS_SERVER_ADDRESS).
+ */
+void ICACHE_FLASH_ATTR
+dns_init()
+{
+ ip_addr_t dnsserver;
+
+ dns_payload = (u8_t *)LWIP_MEM_ALIGN(dns_payload_buffer);
+
+ /* initialize default DNS server address */
+ DNS_SERVER_ADDRESS(&dnsserver);
+
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_init: initializing\n"));
+
+ /* if dns client not yet initialized... */
+ if (dns_pcb == NULL) {
+ dns_pcb = udp_new();
+
+ if (dns_pcb != NULL) {
+ /* initialize DNS table not needed (initialized to zero since it is a
+ * global variable) */
+ LWIP_ASSERT("For implicit initialization to work, DNS_STATE_UNUSED needs to be 0",
+ DNS_STATE_UNUSED == 0);
+
+ /* initialize DNS client */
+ udp_bind(dns_pcb, IP_ADDR_ANY, 0);
+ udp_recv(dns_pcb, dns_recv, NULL);
+
+ /* initialize default DNS primary server */
+ dns_setserver(0, &dnsserver);
+ }
+ }
+#if DNS_LOCAL_HOSTLIST
+ dns_init_local();
+#endif
+}
+
+/**
+ * Initialize one of the DNS servers.
+ *
+ * @param numdns the index of the DNS server to set must be < DNS_MAX_SERVERS
+ * @param dnsserver IP address of the DNS server to set
+ */
+void ICACHE_FLASH_ATTR
+dns_setserver(u8_t numdns, ip_addr_t *dnsserver)
+{
+ if ((numdns < DNS_MAX_SERVERS) && (dns_pcb != NULL) &&
+ (dnsserver != NULL) && !ip_addr_isany(dnsserver)) {
+ dns_servers[numdns] = (*dnsserver);
+ }
+}
+
+/**
+ * Obtain one of the currently configured DNS server.
+ *
+ * @param numdns the index of the DNS server
+ * @return IP address of the indexed DNS server or "ip_addr_any" if the DNS
+ * server has not been configured.
+ */
+ip_addr_t ICACHE_FLASH_ATTR
+dns_getserver(u8_t numdns)
+{
+ if (numdns < DNS_MAX_SERVERS) {
+ return dns_servers[numdns];
+ } else {
+ return *IP_ADDR_ANY;
+ }
+}
+
+/**
+ * The DNS resolver client timer - handle retries and timeouts and should
+ * be called every DNS_TMR_INTERVAL milliseconds (every second by default).
+ */
+void
+dns_tmr(void)
+{
+ if (dns_pcb != NULL) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_tmr: dns_check_entries\n"));
+ dns_check_entries();
+ }
+}
+
+#if DNS_LOCAL_HOSTLIST
+static void ICACHE_FLASH_ATTR
+dns_init_local()
+{
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT)
+ int i;
+ struct local_hostlist_entry *entry;
+ /* Dynamic: copy entries from DNS_LOCAL_HOSTLIST_INIT to list */
+ struct local_hostlist_entry local_hostlist_init[] = DNS_LOCAL_HOSTLIST_INIT;
+ size_t namelen;
+ for (i = 0; i < sizeof(local_hostlist_init) / sizeof(struct local_hostlist_entry); i++) {
+ struct local_hostlist_entry *init_entry = &local_hostlist_init[i];
+ LWIP_ASSERT("invalid host name (NULL)", init_entry->name != NULL);
+ namelen = strlen(init_entry->name);
+ LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN);
+ entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST);
+ LWIP_ASSERT("mem-error in dns_init_local", entry != NULL);
+ if (entry != NULL) {
+ entry->name = (char*)entry + sizeof(struct local_hostlist_entry);
+ MEMCPY((char*)entry->name, init_entry->name, namelen);
+ ((char*)entry->name)[namelen] = 0;
+ entry->addr = init_entry->addr;
+ entry->next = local_hostlist_dynamic;
+ local_hostlist_dynamic = entry;
+ }
+ }
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT) */
+}
+
+/**
+ * Scans the local host-list for a hostname.
+ *
+ * @param hostname Hostname to look for in the local host-list
+ * @return The first IP address for the hostname in the local host-list or
+ * IPADDR_NONE if not found.
+ */
+static u32_t ICACHE_FLASH_ATTR
+dns_lookup_local(const char *hostname)
+{
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+ struct local_hostlist_entry *entry = local_hostlist_dynamic;
+ while(entry != NULL) {
+ if(strcmp(entry->name, hostname) == 0) {
+ return ip4_addr_get_u32(&entry->addr);
+ }
+ entry = entry->next;
+ }
+#else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+ int i;
+ for (i = 0; i < sizeof(local_hostlist_static) / sizeof(struct local_hostlist_entry); i++) {
+ if(strcmp(local_hostlist_static[i].name, hostname) == 0) {
+ return ip4_addr_get_u32(&local_hostlist_static[i].addr);
+ }
+ }
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+ return IPADDR_NONE;
+}
+
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+/** Remove all entries from the local host-list for a specific hostname
+ * and/or IP addess
+ *
+ * @param hostname hostname for which entries shall be removed from the local
+ * host-list
+ * @param addr address for which entries shall be removed from the local host-list
+ * @return the number of removed entries
+ */
+int ICACHE_FLASH_ATTR
+dns_local_removehost(const char *hostname, const ip_addr_t *addr)
+{
+ int removed = 0;
+ struct local_hostlist_entry *entry = local_hostlist_dynamic;
+ struct local_hostlist_entry *last_entry = NULL;
+ while (entry != NULL) {
+ if (((hostname == NULL) || !strcmp(entry->name, hostname)) &&
+ ((addr == NULL) || ip_addr_cmp(&entry->addr, addr))) {
+ struct local_hostlist_entry *free_entry;
+ if (last_entry != NULL) {
+ last_entry->next = entry->next;
+ } else {
+ local_hostlist_dynamic = entry->next;
+ }
+ free_entry = entry;
+ entry = entry->next;
+ memp_free(MEMP_LOCALHOSTLIST, free_entry);
+ removed++;
+ } else {
+ last_entry = entry;
+ entry = entry->next;
+ }
+ }
+ return removed;
+}
+
+/**
+ * Add a hostname/IP address pair to the local host-list.
+ * Duplicates are not checked.
+ *
+ * @param hostname hostname of the new entry
+ * @param addr IP address of the new entry
+ * @return ERR_OK if succeeded or ERR_MEM on memory error
+ */
+err_t ICACHE_FLASH_ATTR
+dns_local_addhost(const char *hostname, const ip_addr_t *addr)
+{
+ struct local_hostlist_entry *entry;
+ size_t namelen;
+ LWIP_ASSERT("invalid host name (NULL)", hostname != NULL);
+ namelen = strlen(hostname);
+ LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN);
+ entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST);
+ if (entry == NULL) {
+ return ERR_MEM;
+ }
+ entry->name = (char*)entry + sizeof(struct local_hostlist_entry);
+ MEMCPY((char*)entry->name, hostname, namelen);
+ ((char*)entry->name)[namelen] = 0;
+ ip_addr_copy(entry->addr, *addr);
+ entry->next = local_hostlist_dynamic;
+ local_hostlist_dynamic = entry;
+ return ERR_OK;
+}
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC*/
+#endif /* DNS_LOCAL_HOSTLIST */
+
+/**
+ * Look up a hostname in the array of known hostnames.
+ *
+ * @note This function only looks in the internal array of known
+ * hostnames, it does not send out a query for the hostname if none
+ * was found. The function dns_enqueue() can be used to send a query
+ * for a hostname.
+ *
+ * @param name the hostname to look up
+ * @return the hostname's IP address, as u32_t (instead of ip_addr_t to
+ * better check for failure: != IPADDR_NONE) or IPADDR_NONE if the hostname
+ * was not found in the cached dns_table.
+ */
+static u32_t ICACHE_FLASH_ATTR
+dns_lookup(const char *name)
+{
+ u8_t i;
+#if DNS_LOCAL_HOSTLIST || defined(DNS_LOOKUP_LOCAL_EXTERN)
+ u32_t addr;
+#endif /* DNS_LOCAL_HOSTLIST || defined(DNS_LOOKUP_LOCAL_EXTERN) */
+#if DNS_LOCAL_HOSTLIST
+ if ((addr = dns_lookup_local(name)) != IPADDR_NONE) {
+ return addr;
+ }
+#endif /* DNS_LOCAL_HOSTLIST */
+#ifdef DNS_LOOKUP_LOCAL_EXTERN
+ if((addr = DNS_LOOKUP_LOCAL_EXTERN(name)) != IPADDR_NONE) {
+ return addr;
+ }
+#endif /* DNS_LOOKUP_LOCAL_EXTERN */
+
+ /* Walk through name list, return entry if found. If not, return NULL. */
+ for (i = 0; i < DNS_TABLE_SIZE; ++i) {
+ if ((dns_table[i].state == DNS_STATE_DONE) &&
+ (strcmp(name, dns_table[i].name) == 0)) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_lookup: \"%s\": found = ", name));
+ ip_addr_debug_print(DNS_DEBUG, &(dns_table[i].ipaddr));
+ LWIP_DEBUGF(DNS_DEBUG, ("\n"));
+ return ip4_addr_get_u32(&dns_table[i].ipaddr);
+ }
+ }
+
+ return IPADDR_NONE;
+}
+
+#if DNS_DOES_NAME_CHECK
+/**
+ * Compare the "dotted" name "query" with the encoded name "response"
+ * to make sure an answer from the DNS server matches the current dns_table
+ * entry (otherwise, answers might arrive late for hostname not on the list
+ * any more).
+ *
+ * @param query hostname (not encoded) from the dns_table
+ * @param response encoded hostname in the DNS response
+ * @return 0: names equal; 1: names differ
+ */
+static u8_t ICACHE_FLASH_ATTR
+dns_compare_name(unsigned char *query, unsigned char *response)
+{
+ unsigned char n;
+
+ do {
+ n = *response++;
+ /** @see RFC 1035 - 4.1.4. Message compression */
+ if ((n & 0xc0) == 0xc0) {
+ /* Compressed name */
+ break;
+ } else {
+ /* Not compressed name */
+ while (n > 0) {
+ if ((*query) != (*response)) {
+ return 1;
+ }
+ ++response;
+ ++query;
+ --n;
+ };
+ ++query;
+ }
+ } while (*response != 0);
+
+ return 0;
+}
+#endif /* DNS_DOES_NAME_CHECK */
+
+/**
+ * Walk through a compact encoded DNS name and return the end of the name.
+ *
+ * @param query encoded DNS name in the DNS server response
+ * @return end of the name
+ */
+static unsigned char * ICACHE_FLASH_ATTR
+dns_parse_name(unsigned char *query)
+{
+ unsigned char n;
+
+ do {
+ n = *query++;
+ /** @see RFC 1035 - 4.1.4. Message compression */
+ if ((n & 0xc0) == 0xc0) {
+ /* Compressed name */
+ break;
+ } else {
+ /* Not compressed name */
+ while (n > 0) {
+ ++query;
+ --n;
+ };
+ }
+ } while (*query != 0);
+
+ return query + 1;
+}
+
+/**
+ * Send a DNS query packet.
+ *
+ * @param numdns index of the DNS server in the dns_servers table
+ * @param name hostname to query
+ * @param id index of the hostname in dns_table, used as transaction ID in the
+ * DNS query packet
+ * @return ERR_OK if packet is sent; an err_t indicating the problem otherwise
+ */
+static err_t ICACHE_FLASH_ATTR
+dns_send(u8_t numdns, const char* name, u8_t id)
+{
+ err_t err;
+ struct dns_hdr *hdr;
+ struct dns_query qry;
+ struct pbuf *p;
+ char *query, *nptr;
+ const char *pHostname;
+ u8_t n;
+
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_send: dns_servers[%"U16_F"] \"%s\": request\n",
+ (u16_t)(numdns), name));
+ LWIP_ASSERT("dns server out of array", numdns < DNS_MAX_SERVERS);
+ LWIP_ASSERT("dns server has no IP address set", !ip_addr_isany(&dns_servers[numdns]));
+
+ /* if here, we have either a new query or a retry on a previous query to process */
+ p = pbuf_alloc(PBUF_TRANSPORT, SIZEOF_DNS_HDR + DNS_MAX_NAME_LENGTH +
+ SIZEOF_DNS_QUERY, PBUF_RAM);
+ if (p != NULL) {
+ LWIP_ASSERT("pbuf must be in one piece", p->next == NULL);
+ /* fill dns header */
+ hdr = (struct dns_hdr*)p->payload;
+ memset(hdr, 0, SIZEOF_DNS_HDR);
+ hdr->id = htons(id);
+ hdr->flags1 = DNS_FLAG1_RD;
+ hdr->numquestions = PP_HTONS(1);
+ query = (char*)hdr + SIZEOF_DNS_HDR;
+ pHostname = name;
+ --pHostname;
+
+ /* convert hostname into suitable query format. */
+ do {
+ ++pHostname;
+ nptr = query;
+ ++query;
+ for(n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) {
+ *query = *pHostname;
+ ++query;
+ ++n;
+ }
+ *nptr = n;
+ } while(*pHostname != 0);
+ *query++='\0';
+
+ /* fill dns query */
+ qry.type = PP_HTONS(DNS_RRTYPE_A);
+ qry.cls = PP_HTONS(DNS_RRCLASS_IN);
+ SMEMCPY(query, &qry, SIZEOF_DNS_QUERY);
+
+ /* resize pbuf to the exact dns query */
+ pbuf_realloc(p, (u16_t)((query + SIZEOF_DNS_QUERY) - ((char*)(p->payload))));
+
+ /* connect to the server for faster receiving */
+ udp_connect(dns_pcb, &dns_servers[numdns], DNS_SERVER_PORT);
+ /* send dns packet */
+ err = udp_sendto(dns_pcb, p, &dns_servers[numdns], DNS_SERVER_PORT);
+
+ /* free pbuf */
+ pbuf_free(p);
+ } else {
+ err = ERR_MEM;
+ }
+
+ return err;
+}
+
+/**
+ * dns_check_entry() - see if pEntry has not yet been queried and, if so, sends out a query.
+ * Check an entry in the dns_table:
+ * - send out query for new entries
+ * - retry old pending entries on timeout (also with different servers)
+ * - remove completed entries from the table if their TTL has expired
+ *
+ * @param i index of the dns_table entry to check
+ */
+static void ICACHE_FLASH_ATTR
+dns_check_entry(u8_t i)
+{
+ err_t err;
+ struct dns_table_entry *pEntry = &dns_table[i];
+
+ LWIP_ASSERT("array index out of bounds", i < DNS_TABLE_SIZE);
+
+ switch(pEntry->state) {
+
+ case DNS_STATE_NEW: {
+ /* initialize new entry */
+ pEntry->state = DNS_STATE_ASKING;
+ pEntry->numdns = 0;
+ pEntry->tmr = 1;
+ pEntry->retries = 0;
+
+ /* send DNS packet for this entry */
+ err = dns_send(pEntry->numdns, pEntry->name, i);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING,
+ ("dns_send returned error: %s\n", lwip_strerr(err)));
+ }
+ break;
+ }
+
+ case DNS_STATE_ASKING: {
+ if (--pEntry->tmr == 0) {
+ if (++pEntry->retries == DNS_MAX_RETRIES) {
+ if ((pEntry->numdns+1numdns+1])) {
+ /* change of server */
+ pEntry->numdns++;
+ pEntry->tmr = 1;
+ pEntry->retries = 0;
+ break;
+ } else {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": timeout\n", pEntry->name));
+ /* call specified callback function if provided */
+ if (pEntry->found)
+ (*pEntry->found)(pEntry->name, NULL, pEntry->arg);
+ /* flush this entry */
+ pEntry->state = DNS_STATE_UNUSED;
+ pEntry->found = NULL;
+ break;
+ }
+ }
+
+ /* wait longer for the next retry */
+ pEntry->tmr = pEntry->retries;
+
+ /* send DNS packet for this entry */
+ err = dns_send(pEntry->numdns, pEntry->name, i);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING,
+ ("dns_send returned error: %s\n", lwip_strerr(err)));
+ }
+ }
+ break;
+ }
+
+ case DNS_STATE_DONE: {
+ /* if the time to live is nul */
+ if (--pEntry->ttl == 0) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": flush\n", pEntry->name));
+ /* flush this entry */
+ pEntry->state = DNS_STATE_UNUSED;
+ pEntry->found = NULL;
+ }
+ break;
+ }
+ case DNS_STATE_UNUSED:
+ /* nothing to do */
+ break;
+ default:
+ LWIP_ASSERT("unknown dns_table entry state:", 0);
+ break;
+ }
+}
+
+/**
+ * Call dns_check_entry for each entry in dns_table - check all entries.
+ */
+static void ICACHE_FLASH_ATTR
+dns_check_entries(void)
+{
+ u8_t i;
+
+ for (i = 0; i < DNS_TABLE_SIZE; ++i) {
+ dns_check_entry(i);
+ }
+}
+
+/**
+ * Receive input function for DNS response packets arriving for the dns UDP pcb.
+ *
+ * @params see udp.h
+ */
+static void ICACHE_FLASH_ATTR
+dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port)
+{
+ u16_t i;
+ char *pHostname;
+ struct dns_hdr *hdr;
+ struct dns_answer ans;
+ struct dns_table_entry *pEntry;
+ u16_t nquestions, nanswers;
+
+ LWIP_UNUSED_ARG(arg);
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_UNUSED_ARG(addr);
+ LWIP_UNUSED_ARG(port);
+
+ /* is the dns message too big ? */
+ if (p->tot_len > DNS_MSG_SIZE) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too big\n"));
+ /* free pbuf and return */
+ goto memerr;
+ }
+
+ /* is the dns message big enough ? */
+ if (p->tot_len < (SIZEOF_DNS_HDR + SIZEOF_DNS_QUERY + SIZEOF_DNS_ANSWER)) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too small\n"));
+ /* free pbuf and return */
+ goto memerr;
+ }
+
+ /* copy dns payload inside static buffer for processing */
+ if (pbuf_copy_partial(p, dns_payload, p->tot_len, 0) == p->tot_len) {
+ /* The ID in the DNS header should be our entry into the name table. */
+ hdr = (struct dns_hdr*)dns_payload;
+ i = htons(hdr->id);
+ if (i < DNS_TABLE_SIZE) {
+ pEntry = &dns_table[i];
+ if(pEntry->state == DNS_STATE_ASKING) {
+ /* This entry is now completed. */
+ pEntry->state = DNS_STATE_DONE;
+ pEntry->err = hdr->flags2 & DNS_FLAG2_ERR_MASK;
+
+ /* We only care about the question(s) and the answers. The authrr
+ and the extrarr are simply discarded. */
+ nquestions = htons(hdr->numquestions);
+ nanswers = htons(hdr->numanswers);
+
+ /* Check for error. If so, call callback to inform. */
+ if (((hdr->flags1 & DNS_FLAG1_RESPONSE) == 0) || (pEntry->err != 0) || (nquestions != 1)) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in flags\n", pEntry->name));
+ /* call callback to indicate error, clean up memory and return */
+ goto responseerr;
+ }
+
+#if DNS_DOES_NAME_CHECK
+ /* Check if the name in the "question" part match with the name in the entry. */
+ if (dns_compare_name((unsigned char *)(pEntry->name), (unsigned char *)dns_payload + SIZEOF_DNS_HDR) != 0) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", pEntry->name));
+ /* call callback to indicate error, clean up memory and return */
+ goto responseerr;
+ }
+#endif /* DNS_DOES_NAME_CHECK */
+
+ /* Skip the name in the "question" part */
+ pHostname = (char *) dns_parse_name((unsigned char *)dns_payload + SIZEOF_DNS_HDR) + SIZEOF_DNS_QUERY;
+
+ while (nanswers > 0) {
+ /* skip answer resource record's host name */
+ pHostname = (char *) dns_parse_name((unsigned char *)pHostname);
+
+ /* Check for IP address type and Internet class. Others are discarded. */
+ SMEMCPY(&ans, pHostname, SIZEOF_DNS_ANSWER);
+ if((ans.type == PP_HTONS(DNS_RRTYPE_A)) && (ans.cls == PP_HTONS(DNS_RRCLASS_IN)) &&
+ (ans.len == PP_HTONS(sizeof(ip_addr_t))) ) {
+ /* read the answer resource record's TTL, and maximize it if needed */
+ pEntry->ttl = ntohl(ans.ttl);
+ if (pEntry->ttl > DNS_MAX_TTL) {
+ pEntry->ttl = DNS_MAX_TTL;
+ }
+ /* read the IP address after answer resource record's header */
+ SMEMCPY(&(pEntry->ipaddr), (pHostname+SIZEOF_DNS_ANSWER), sizeof(ip_addr_t));
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response = ", pEntry->name));
+ ip_addr_debug_print(DNS_DEBUG, (&(pEntry->ipaddr)));
+ LWIP_DEBUGF(DNS_DEBUG, ("\n"));
+ /* call specified callback function if provided */
+ if (pEntry->found) {
+ (*pEntry->found)(pEntry->name, &pEntry->ipaddr, pEntry->arg);
+ }
+ /* deallocate memory and return */
+ goto memerr;
+ } else {
+ pHostname = pHostname + SIZEOF_DNS_ANSWER + htons(ans.len);
+ }
+ --nanswers;
+ }
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in response\n", pEntry->name));
+ /* call callback to indicate error, clean up memory and return */
+ goto responseerr;
+ }
+ }
+ }
+
+ /* deallocate memory and return */
+ goto memerr;
+
+responseerr:
+ /* ERROR: call specified callback function with NULL as name to indicate an error */
+ if (pEntry->found) {
+ (*pEntry->found)(pEntry->name, NULL, pEntry->arg);
+ }
+ /* flush this entry */
+ pEntry->state = DNS_STATE_UNUSED;
+ pEntry->found = NULL;
+
+memerr:
+ /* free pbuf */
+ pbuf_free(p);
+ return;
+}
+
+/**
+ * Queues a new hostname to resolve and sends out a DNS query for that hostname
+ *
+ * @param name the hostname that is to be queried
+ * @param found a callback founction to be called on success, failure or timeout
+ * @param callback_arg argument to pass to the callback function
+ * @return @return a err_t return code.
+ */
+static err_t ICACHE_FLASH_ATTR
+dns_enqueue(const char *name, dns_found_callback found, void *callback_arg)
+{
+ u8_t i;
+ u8_t lseq, lseqi;
+ struct dns_table_entry *pEntry = NULL;
+ size_t namelen;
+
+ /* search an unused entry, or the oldest one */
+ lseq = lseqi = 0;
+ for (i = 0; i < DNS_TABLE_SIZE; ++i) {
+ pEntry = &dns_table[i];
+ /* is it an unused entry ? */
+ if (pEntry->state == DNS_STATE_UNUSED)
+ break;
+
+ /* check if this is the oldest completed entry */
+ if (pEntry->state == DNS_STATE_DONE) {
+ if ((dns_seqno - pEntry->seqno) > lseq) {
+ lseq = dns_seqno - pEntry->seqno;
+ lseqi = i;
+ }
+ }
+ }
+
+ /* if we don't have found an unused entry, use the oldest completed one */
+ if (i == DNS_TABLE_SIZE) {
+ if ((lseqi >= DNS_TABLE_SIZE) || (dns_table[lseqi].state != DNS_STATE_DONE)) {
+ /* no entry can't be used now, table is full */
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": DNS entries table is full\n", name));
+ return ERR_MEM;
+ } else {
+ /* use the oldest completed one */
+ i = lseqi;
+ pEntry = &dns_table[i];
+ }
+ }
+
+ /* use this entry */
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS entry %"U16_F"\n", name, (u16_t)(i)));
+
+ /* fill the entry */
+ pEntry->state = DNS_STATE_NEW;
+ pEntry->seqno = dns_seqno++;
+ pEntry->found = found;
+ pEntry->arg = callback_arg;
+ namelen = LWIP_MIN(strlen(name), DNS_MAX_NAME_LENGTH-1);
+ MEMCPY(pEntry->name, name, namelen);
+ pEntry->name[namelen] = 0;
+
+ /* force to send query without waiting timer */
+ dns_check_entry(i);
+
+ /* dns query is enqueued */
+ return ERR_INPROGRESS;
+}
+
+/**
+ * Resolve a hostname (string) into an IP address.
+ * NON-BLOCKING callback version for use with raw API!!!
+ *
+ * Returns immediately with one of err_t return codes:
+ * - ERR_OK if hostname is a valid IP address string or the host
+ * name is already in the local names table.
+ * - ERR_INPROGRESS enqueue a request to be sent to the DNS server
+ * for resolution if no errors are present.
+ * - ERR_ARG: dns client not initialized or invalid hostname
+ *
+ * @param hostname the hostname that is to be queried
+ * @param addr pointer to a ip_addr_t where to store the address if it is already
+ * cached in the dns_table (only valid if ERR_OK is returned!)
+ * @param found a callback function to be called on success, failure or timeout (only if
+ * ERR_INPROGRESS is returned!)
+ * @param callback_arg argument to pass to the callback function
+ * @return a err_t return code.
+ */
+err_t ICACHE_FLASH_ATTR
+dns_gethostbyname(const char *hostname, ip_addr_t *addr, dns_found_callback found,
+ void *callback_arg)
+{
+ u32_t ipaddr;
+ /* not initialized or no valid server yet, or invalid addr pointer
+ * or invalid hostname or invalid hostname length */
+ if ((dns_pcb == NULL) || (addr == NULL) ||
+ (!hostname) || (!hostname[0]) ||
+ (strlen(hostname) >= DNS_MAX_NAME_LENGTH)) {
+ return ERR_ARG;
+ }
+
+#if LWIP_HAVE_LOOPIF
+ if (strcmp(hostname, "localhost")==0) {
+ ip_addr_set_loopback(addr);
+ return ERR_OK;
+ }
+#endif /* LWIP_HAVE_LOOPIF */
+
+ /* host name already in octet notation? set ip addr and return ERR_OK */
+ ipaddr = ipaddr_addr(hostname);
+ if (ipaddr == IPADDR_NONE) {
+ /* already have this address cached? */
+// ipaddr = dns_lookup(hostname);
+ }
+ if (ipaddr != IPADDR_NONE) {
+ ip4_addr_set_u32(addr, ipaddr);
+ return ERR_OK;
+ }
+
+ /* queue query with specified callback */
+ return dns_enqueue(hostname, found, callback_arg);
+}
+
+#endif /* LWIP_DNS */
diff --git a/app/lwip/core/init.c b/app/lwip/core/init.c
new file mode 100644
index 00000000..3bd91755
--- /dev/null
+++ b/app/lwip/core/init.c
@@ -0,0 +1,321 @@
+/**
+ * @file
+ * Modules initialization
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/init.h"
+#include "lwip/stats.h"
+#include "lwip/sys.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/pbuf.h"
+#include "lwip/netif.h"
+#include "lwip/sockets.h"
+#include "lwip/ip.h"
+#include "lwip/raw.h"
+#include "lwip/udp.h"
+#include "lwip/tcp_impl.h"
+#include "lwip/snmp_msg.h"
+#include "lwip/autoip.h"
+#include "lwip/igmp.h"
+#include "lwip/dns.h"
+#include "lwip/timers.h"
+#include "netif/etharp.h"
+
+/* Compile-time sanity checks for configuration errors.
+ * These can be done independently of LWIP_DEBUG, without penalty.
+ */
+#ifndef BYTE_ORDER
+ #error "BYTE_ORDER is not defined, you have to define it in your cc.h"
+#endif
+#if (!IP_SOF_BROADCAST && IP_SOF_BROADCAST_RECV)
+ #error "If you want to use broadcast filter per pcb on recv operations, you have to define IP_SOF_BROADCAST=1 in your lwipopts.h"
+#endif
+#if (!LWIP_ARP && ARP_QUEUEING)
+ #error "If you want to use ARP Queueing, you have to define LWIP_ARP=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_UDPLITE)
+ #error "If you want to use UDP Lite, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_SNMP)
+ #error "If you want to use SNMP, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_DHCP)
+ #error "If you want to use DHCP, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_IGMP)
+ #error "If you want to use IGMP, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_SNMP)
+ #error "If you want to use SNMP, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_DNS)
+ #error "If you want to use DNS, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+#if (LWIP_ARP && ARP_QUEUEING && (MEMP_NUM_ARP_QUEUE<=0))
+ #error "If you want to use ARP Queueing, you have to define MEMP_NUM_ARP_QUEUE>=1 in your lwipopts.h"
+#endif
+#if (LWIP_RAW && (MEMP_NUM_RAW_PCB<=0))
+ #error "If you want to use RAW, you have to define MEMP_NUM_RAW_PCB>=1 in your lwipopts.h"
+#endif
+#if (LWIP_UDP && (MEMP_NUM_UDP_PCB<=0))
+ #error "If you want to use UDP, you have to define MEMP_NUM_UDP_PCB>=1 in your lwipopts.h"
+#endif
+//#if (LWIP_TCP && (MEMP_NUM_TCP_PCB<=0))
+// #error "If you want to use TCP, you have to define MEMP_NUM_TCP_PCB>=1 in your lwipopts.h"
+//#endif
+#if (LWIP_TCP && (TCP_WND > 0xffff))
+ #error "If you want to use TCP, TCP_WND must fit in an u16_t, so, you have to reduce it in your lwipopts.h"
+#endif
+#if (LWIP_TCP && (TCP_SND_QUEUELEN > 0xffff))
+ #error "If you want to use TCP, TCP_SND_QUEUELEN must fit in an u16_t, so, you have to reduce it in your lwipopts.h"
+#endif
+#if (LWIP_TCP && (TCP_SND_QUEUELEN < 2))
+ #error "TCP_SND_QUEUELEN must be at least 2 for no-copy TCP writes to work"
+#endif
+#if (LWIP_TCP && ((TCP_MAXRTX > 12) || (TCP_SYNMAXRTX > 12)))
+ #error "If you want to use TCP, TCP_MAXRTX and TCP_SYNMAXRTX must less or equal to 12 (due to tcp_backoff table), so, you have to reduce them in your lwipopts.h"
+#endif
+#if (LWIP_TCP && TCP_LISTEN_BACKLOG && (TCP_DEFAULT_LISTEN_BACKLOG < 0) || (TCP_DEFAULT_LISTEN_BACKLOG > 0xff))
+ #error "If you want to use TCP backlog, TCP_DEFAULT_LISTEN_BACKLOG must fit into an u8_t"
+#endif
+#if (LWIP_IGMP && (MEMP_NUM_IGMP_GROUP<=1))
+ #error "If you want to use IGMP, you have to define MEMP_NUM_IGMP_GROUP>1 in your lwipopts.h"
+#endif
+#if (LWIP_NETIF_API && (NO_SYS==1))
+ #error "If you want to use NETIF API, you have to define NO_SYS=0 in your lwipopts.h"
+#endif
+#if ((LWIP_SOCKET || LWIP_NETCONN) && (NO_SYS==1))
+ #error "If you want to use Sequential API, you have to define NO_SYS=0 in your lwipopts.h"
+#endif
+#if ((LWIP_NETCONN || LWIP_SOCKET) && (MEMP_NUM_TCPIP_MSG_API<=0))
+ #error "If you want to use Sequential API, you have to define MEMP_NUM_TCPIP_MSG_API>=1 in your lwipopts.h"
+#endif
+#if (!LWIP_NETCONN && LWIP_SOCKET)
+ #error "If you want to use Socket API, you have to define LWIP_NETCONN=1 in your lwipopts.h"
+#endif
+#if (((!LWIP_DHCP) || (!LWIP_AUTOIP)) && LWIP_DHCP_AUTOIP_COOP)
+ #error "If you want to use DHCP/AUTOIP cooperation mode, you have to define LWIP_DHCP=1 and LWIP_AUTOIP=1 in your lwipopts.h"
+#endif
+#if (((!LWIP_DHCP) || (!LWIP_ARP)) && DHCP_DOES_ARP_CHECK)
+ #error "If you want to use DHCP ARP checking, you have to define LWIP_DHCP=1 and LWIP_ARP=1 in your lwipopts.h"
+#endif
+#if (!LWIP_ARP && LWIP_AUTOIP)
+ #error "If you want to use AUTOIP, you have to define LWIP_ARP=1 in your lwipopts.h"
+#endif
+#if (LWIP_SNMP && (SNMP_CONCURRENT_REQUESTS<=0))
+ #error "If you want to use SNMP, you have to define SNMP_CONCURRENT_REQUESTS>=1 in your lwipopts.h"
+#endif
+#if (LWIP_SNMP && (SNMP_TRAP_DESTINATIONS<=0))
+ #error "If you want to use SNMP, you have to define SNMP_TRAP_DESTINATIONS>=1 in your lwipopts.h"
+#endif
+#if (LWIP_TCP && ((LWIP_EVENT_API && LWIP_CALLBACK_API) || (!LWIP_EVENT_API && !LWIP_CALLBACK_API)))
+ #error "One and exactly one of LWIP_EVENT_API and LWIP_CALLBACK_API has to be enabled in your lwipopts.h"
+#endif
+/* There must be sufficient timeouts, taking into account requirements of the subsystems. */
+#if LWIP_TIMERS && (MEMP_NUM_SYS_TIMEOUT < (LWIP_TCP + IP_REASSEMBLY + LWIP_ARP + (2*LWIP_DHCP) + LWIP_AUTOIP + LWIP_IGMP + LWIP_DNS + PPP_SUPPORT))
+ #error "MEMP_NUM_SYS_TIMEOUT is too low to accomodate all required timeouts"
+#endif
+#if (IP_REASSEMBLY && (MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS))
+ #error "MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS doesn't make sense since each struct ip_reassdata must hold 2 pbufs at least!"
+#endif
+#if (MEM_LIBC_MALLOC && MEM_USE_POOLS)
+ #error "MEM_LIBC_MALLOC and MEM_USE_POOLS may not both be simultaneously enabled in your lwipopts.h"
+#endif
+#if (MEM_USE_POOLS && !MEMP_USE_CUSTOM_POOLS)
+ #error "MEM_USE_POOLS requires custom pools (MEMP_USE_CUSTOM_POOLS) to be enabled in your lwipopts.h"
+#endif
+#if (PBUF_POOL_BUFSIZE <= MEM_ALIGNMENT)
+ #error "PBUF_POOL_BUFSIZE must be greater than MEM_ALIGNMENT or the offset may take the full first pbuf"
+#endif
+#if (TCP_QUEUE_OOSEQ && !LWIP_TCP)
+ #error "TCP_QUEUE_OOSEQ requires LWIP_TCP"
+#endif
+#if (DNS_LOCAL_HOSTLIST && !DNS_LOCAL_HOSTLIST_IS_DYNAMIC && !(defined(DNS_LOCAL_HOSTLIST_INIT)))
+ #error "you have to define define DNS_LOCAL_HOSTLIST_INIT {{'host1', 0x123}, {'host2', 0x234}} to initialize DNS_LOCAL_HOSTLIST"
+#endif
+#if PPP_SUPPORT && !PPPOS_SUPPORT & !PPPOE_SUPPORT
+ #error "PPP_SUPPORT needs either PPPOS_SUPPORT or PPPOE_SUPPORT turned on"
+#endif
+#if !LWIP_ETHERNET && (LWIP_ARP || PPPOE_SUPPORT)
+ #error "LWIP_ETHERNET needs to be turned on for LWIP_ARP or PPPOE_SUPPORT"
+#endif
+#if LWIP_IGMP && !defined(LWIP_RAND)
+ #error "When using IGMP, LWIP_RAND() needs to be defined to a random-function returning an u32_t random value"
+#endif
+#if LWIP_TCPIP_CORE_LOCKING_INPUT && !LWIP_TCPIP_CORE_LOCKING
+ #error "When using LWIP_TCPIP_CORE_LOCKING_INPUT, LWIP_TCPIP_CORE_LOCKING must be enabled, too"
+#endif
+#if LWIP_TCP && LWIP_NETIF_TX_SINGLE_PBUF && !TCP_OVERSIZE
+ #error "LWIP_NETIF_TX_SINGLE_PBUF needs TCP_OVERSIZE enabled to create single-pbuf TCP packets"
+#endif
+#if IP_FRAG && IP_FRAG_USES_STATIC_BUF && LWIP_NETIF_TX_SINGLE_PBUF
+ #error "LWIP_NETIF_TX_SINGLE_PBUF does not work with IP_FRAG_USES_STATIC_BUF==1 as that creates pbuf queues"
+#endif
+
+
+/* Compile-time checks for deprecated options.
+ */
+#ifdef MEMP_NUM_TCPIP_MSG
+ #error "MEMP_NUM_TCPIP_MSG option is deprecated. Remove it from your lwipopts.h."
+#endif
+#ifdef MEMP_NUM_API_MSG
+ #error "MEMP_NUM_API_MSG option is deprecated. Remove it from your lwipopts.h."
+#endif
+#ifdef TCP_REXMIT_DEBUG
+ #error "TCP_REXMIT_DEBUG option is deprecated. Remove it from your lwipopts.h."
+#endif
+#ifdef RAW_STATS
+ #error "RAW_STATS option is deprecated. Remove it from your lwipopts.h."
+#endif
+#ifdef ETHARP_QUEUE_FIRST
+ #error "ETHARP_QUEUE_FIRST option is deprecated. Remove it from your lwipopts.h."
+#endif
+#ifdef ETHARP_ALWAYS_INSERT
+ #error "ETHARP_ALWAYS_INSERT option is deprecated. Remove it from your lwipopts.h."
+#endif
+
+#ifdef LWIP_DEBUG
+static void ICACHE_FLASH_ATTR
+lwip_sanity_check(void)
+{
+ /* Warnings */
+#if LWIP_NETCONN
+ if (MEMP_NUM_NETCONN > (MEMP_NUM_TCP_PCB+MEMP_NUM_TCP_PCB_LISTEN+MEMP_NUM_UDP_PCB+MEMP_NUM_RAW_PCB))
+ LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: MEMP_NUM_NETCONN should be less than the sum of MEMP_NUM_{TCP,RAW,UDP}_PCB+MEMP_NUM_TCP_PCB_LISTEN\n"));
+#endif /* LWIP_NETCONN */
+#if LWIP_TCP
+ if (MEMP_NUM_TCP_SEG < TCP_SND_QUEUELEN)
+ LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: MEMP_NUM_TCP_SEG should be at least as big as TCP_SND_QUEUELEN\n"));
+ if (TCP_SND_BUF < 2 * TCP_MSS)
+ LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_SND_BUF must be at least as much as (2 * TCP_MSS) for things to work smoothly\n"));
+ if (TCP_SND_QUEUELEN < (2 * (TCP_SND_BUF/TCP_MSS)))
+ LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_SND_QUEUELEN must be at least as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work\n"));
+ if (TCP_SNDLOWAT >= TCP_SND_BUF)
+ LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_SNDLOWAT must be less than TCP_SND_BUF.\n"));
+ if (TCP_SNDQUEUELOWAT >= TCP_SND_QUEUELEN)
+ LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_SNDQUEUELOWAT must be less than TCP_SND_QUEUELEN.\n"));
+ if (TCP_WND > (PBUF_POOL_SIZE*PBUF_POOL_BUFSIZE))
+ LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_WND is larger than space provided by PBUF_POOL_SIZE*PBUF_POOL_BUFSIZE\n"));
+ if (TCP_WND < TCP_MSS)
+ LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_WND is smaller than MSS\n"));
+#endif /* LWIP_TCP */
+#if LWIP_SOCKET
+ /* Check that the SO_* socket options and SOF_* lwIP-internal flags match */
+ if (SO_ACCEPTCONN != SOF_ACCEPTCONN)
+ LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: SO_ACCEPTCONN != SOF_ACCEPTCONN\n"));
+ if (SO_REUSEADDR != SOF_REUSEADDR)
+ LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: SO_REUSEADDR != SOF_REUSEADDR\n"));
+ if (SO_KEEPALIVE != SOF_KEEPALIVE)
+ LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: SO_KEEPALIVE != SOF_KEEPALIVE\n"));
+ if (SO_BROADCAST != SOF_BROADCAST)
+ LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: SO_BROADCAST != SOF_BROADCAST\n"));
+ if (SO_LINGER != SOF_LINGER)
+ LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: SO_LINGER != SOF_LINGER\n"));
+#endif /* LWIP_SOCKET */
+}
+#else /* LWIP_DEBUG */
+#define lwip_sanity_check()
+#endif /* LWIP_DEBUG */
+
+/**
+ * Perform Sanity check of user-configurable values, and initialize all modules.
+ */
+void
+lwip_init(void)
+{
+ /* Sanity check user-configurable values */
+ lwip_sanity_check();
+
+ /* Modules initialization */
+ stats_init();
+#if !NO_SYS
+ sys_init();
+#endif /* !NO_SYS */
+#if 0
+ mem_init(&_bss_end);
+#endif
+ memp_init();
+
+ pbuf_init();
+
+ netif_init();
+
+#if LWIP_SOCKET
+ lwip_socket_init();
+#endif /* LWIP_SOCKET */
+ ip_init();
+
+#if LWIP_ARP
+ etharp_init();
+
+#endif /* LWIP_ARP */
+#if LWIP_RAW
+ raw_init();
+
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+ udp_init();
+
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ MEMP_NUM_TCP_PCB = 5;
+ tcp_init();
+
+#endif /* LWIP_TCP */
+#if LWIP_SNMP
+ snmp_init();
+
+#endif /* LWIP_SNMP */
+#if LWIP_AUTOIP
+ autoip_init();
+
+#endif /* LWIP_AUTOIP */
+#if LWIP_IGMP
+ igmp_init();
+
+#endif /* LWIP_IGMP */
+#if LWIP_DNS
+ dns_init();
+
+#endif /* LWIP_DNS */
+
+#if LWIP_TIMERS
+ sys_timeouts_init();
+#endif /* LWIP_TIMERS */
+}
diff --git a/app/lwip/core/ipv4/Makefile b/app/lwip/core/ipv4/Makefile
new file mode 100644
index 00000000..660968be
--- /dev/null
+++ b/app/lwip/core/ipv4/Makefile
@@ -0,0 +1,46 @@
+
+#############################################################
+# Required variables for each makefile
+# Discard this section from all parent makefiles
+# Expected variables (with automatic defaults):
+# CSRCS (all "C" files in the dir)
+# SUBDIRS (all subdirs with a Makefile)
+# GEN_LIBS - list of libs to be generated ()
+# GEN_IMAGES - list of images to be generated ()
+# COMPONENTS_xxx - a list of libs/objs in the form
+# subdir/lib to be extracted and rolled up into
+# a generated lib/image xxx.a ()
+#
+ifndef PDIR
+
+GEN_LIBS = liblwipipv4.a
+
+endif
+
+
+#############################################################
+# Configuration i.e. compile options etc.
+# Target specific stuff (defines etc.) goes in here!
+# Generally values applying to a tree are captured in the
+# makefile at its root level - these are then overridden
+# for a subtree within the makefile rooted therein
+#
+#DEFINES +=
+
+#############################################################
+# Recursion Magic - Don't touch this!!
+#
+# Each subtree potentially has an include directory
+# corresponding to the common APIs applicable to modules
+# rooted at that subtree. Accordingly, the INCLUDE PATH
+# of a module can only contain the include directories up
+# its parent path, and not its siblings
+#
+# Required for each makefile to inherit from the parent
+#
+
+INCLUDES := $(INCLUDES) -I $(PDIR)include
+INCLUDES += -I ./
+PDIR := ../$(PDIR)
+sinclude $(PDIR)Makefile
+
diff --git a/app/lwip/core/ipv4/autoip.c b/app/lwip/core/ipv4/autoip.c
new file mode 100644
index 00000000..92bb4591
--- /dev/null
+++ b/app/lwip/core/ipv4/autoip.c
@@ -0,0 +1,536 @@
+/**
+ * @file
+ * AutoIP Automatic LinkLocal IP Configuration
+ *
+ */
+
+/*
+ *
+ * Copyright (c) 2007 Dominik Spies
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Dominik Spies
+ *
+ * This is a AutoIP implementation for the lwIP TCP/IP stack. It aims to conform
+ * with RFC 3927.
+ *
+ *
+ * Please coordinate changes and requests with Dominik Spies
+ *
+ */
+
+/*******************************************************************************
+ * USAGE:
+ *
+ * define LWIP_AUTOIP 1 in your lwipopts.h
+ *
+ * If you don't use tcpip.c (so, don't call, you don't call tcpip_init):
+ * - First, call autoip_init().
+ * - call autoip_tmr() all AUTOIP_TMR_INTERVAL msces,
+ * that should be defined in autoip.h.
+ * I recommend a value of 100. The value must divide 1000 with a remainder almost 0.
+ * Possible values are 1000, 500, 333, 250, 200, 166, 142, 125, 111, 100 ....
+ *
+ * Without DHCP:
+ * - Call autoip_start() after netif_add().
+ *
+ * With DHCP:
+ * - define LWIP_DHCP_AUTOIP_COOP 1 in your lwipopts.h.
+ * - Configure your DHCP Client.
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_AUTOIP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/mem.h"
+#include "lwip/udp.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/autoip.h"
+#include "netif/etharp.h"
+
+#include
+#include
+
+/* 169.254.0.0 */
+#define AUTOIP_NET 0xA9FE0000
+/* 169.254.1.0 */
+#define AUTOIP_RANGE_START (AUTOIP_NET | 0x0100)
+/* 169.254.254.255 */
+#define AUTOIP_RANGE_END (AUTOIP_NET | 0xFEFF)
+
+
+/** Pseudo random macro based on netif informations.
+ * You could use "rand()" from the C Library if you define LWIP_AUTOIP_RAND in lwipopts.h */
+#ifndef LWIP_AUTOIP_RAND
+#define LWIP_AUTOIP_RAND(netif) ( (((u32_t)((netif->hwaddr[5]) & 0xff) << 24) | \
+ ((u32_t)((netif->hwaddr[3]) & 0xff) << 16) | \
+ ((u32_t)((netif->hwaddr[2]) & 0xff) << 8) | \
+ ((u32_t)((netif->hwaddr[4]) & 0xff))) + \
+ (netif->autoip?netif->autoip->tried_llipaddr:0))
+#endif /* LWIP_AUTOIP_RAND */
+
+/**
+ * Macro that generates the initial IP address to be tried by AUTOIP.
+ * If you want to override this, define it to something else in lwipopts.h.
+ */
+#ifndef LWIP_AUTOIP_CREATE_SEED_ADDR
+#define LWIP_AUTOIP_CREATE_SEED_ADDR(netif) \
+ htonl(AUTOIP_RANGE_START + ((u32_t)(((u8_t)(netif->hwaddr[4])) | \
+ ((u32_t)((u8_t)(netif->hwaddr[5]))) << 8)))
+#endif /* LWIP_AUTOIP_CREATE_SEED_ADDR */
+
+/* static functions */
+static void autoip_handle_arp_conflict(struct netif *netif);
+
+/* creates a pseudo random LL IP-Address for a network interface */
+static void autoip_create_addr(struct netif *netif, ip_addr_t *ipaddr);
+
+/* sends an ARP probe */
+static err_t autoip_arp_probe(struct netif *netif);
+
+/* sends an ARP announce */
+static err_t autoip_arp_announce(struct netif *netif);
+
+/* configure interface for use with current LL IP-Address */
+static err_t autoip_bind(struct netif *netif);
+
+/* start sending probes for llipaddr */
+static void autoip_start_probing(struct netif *netif);
+
+/**
+ * Initialize this module
+ */
+void
+autoip_init(void)
+{
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_init()\n"));
+}
+
+/** Set a statically allocated struct autoip to work with.
+ * Using this prevents autoip_start to allocate it using mem_malloc.
+ *
+ * @param netif the netif for which to set the struct autoip
+ * @param dhcp (uninitialised) dhcp struct allocated by the application
+ */
+void
+autoip_set_struct(struct netif *netif, struct autoip *autoip)
+{
+ LWIP_ASSERT("netif != NULL", netif != NULL);
+ LWIP_ASSERT("autoip != NULL", autoip != NULL);
+ LWIP_ASSERT("netif already has a struct autoip set", netif->autoip == NULL);
+
+ /* clear data structure */
+ memset(autoip, 0, sizeof(struct autoip));
+ /* autoip->state = AUTOIP_STATE_OFF; */
+ netif->autoip = autoip;
+}
+
+/** Restart AutoIP client and check the next address (conflict detected)
+ *
+ * @param netif The netif under AutoIP control
+ */
+static void
+autoip_restart(struct netif *netif)
+{
+ netif->autoip->tried_llipaddr++;
+ autoip_start(netif);
+}
+
+/**
+ * Handle a IP address conflict after an ARP conflict detection
+ */
+static void
+autoip_handle_arp_conflict(struct netif *netif)
+{
+ /* Somehow detect if we are defending or retreating */
+ unsigned char defend = 1; /* tbd */
+
+ if(defend) {
+ if(netif->autoip->lastconflict > 0) {
+ /* retreat, there was a conflicting ARP in the last
+ * DEFEND_INTERVAL seconds
+ */
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_handle_arp_conflict(): we are defending, but in DEFEND_INTERVAL, retreating\n"));
+
+ /* TODO: close all TCP sessions */
+ autoip_restart(netif);
+ } else {
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_handle_arp_conflict(): we are defend, send ARP Announce\n"));
+ autoip_arp_announce(netif);
+ netif->autoip->lastconflict = DEFEND_INTERVAL * AUTOIP_TICKS_PER_SECOND;
+ }
+ } else {
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_handle_arp_conflict(): we do not defend, retreating\n"));
+ /* TODO: close all TCP sessions */
+ autoip_restart(netif);
+ }
+}
+
+/**
+ * Create an IP-Address out of range 169.254.1.0 to 169.254.254.255
+ *
+ * @param netif network interface on which create the IP-Address
+ * @param ipaddr ip address to initialize
+ */
+static void
+autoip_create_addr(struct netif *netif, ip_addr_t *ipaddr)
+{
+ /* Here we create an IP-Address out of range 169.254.1.0 to 169.254.254.255
+ * compliant to RFC 3927 Section 2.1
+ * We have 254 * 256 possibilities */
+
+ u32_t addr = ntohl(LWIP_AUTOIP_CREATE_SEED_ADDR(netif));
+ addr += netif->autoip->tried_llipaddr;
+ addr = AUTOIP_NET | (addr & 0xffff);
+ /* Now, 169.254.0.0 <= addr <= 169.254.255.255 */
+
+ if (addr < AUTOIP_RANGE_START) {
+ addr += AUTOIP_RANGE_END - AUTOIP_RANGE_START + 1;
+ }
+ if (addr > AUTOIP_RANGE_END) {
+ addr -= AUTOIP_RANGE_END - AUTOIP_RANGE_START + 1;
+ }
+ LWIP_ASSERT("AUTOIP address not in range", (addr >= AUTOIP_RANGE_START) &&
+ (addr <= AUTOIP_RANGE_END));
+ ip4_addr_set_u32(ipaddr, htonl(addr));
+
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_create_addr(): tried_llipaddr=%"U16_F", %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ (u16_t)(netif->autoip->tried_llipaddr), ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr),
+ ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr)));
+}
+
+/**
+ * Sends an ARP probe from a network interface
+ *
+ * @param netif network interface used to send the probe
+ */
+static err_t
+autoip_arp_probe(struct netif *netif)
+{
+ return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, ðbroadcast,
+ (struct eth_addr *)netif->hwaddr, IP_ADDR_ANY, ðzero,
+ &netif->autoip->llipaddr, ARP_REQUEST);
+}
+
+/**
+ * Sends an ARP announce from a network interface
+ *
+ * @param netif network interface used to send the announce
+ */
+static err_t
+autoip_arp_announce(struct netif *netif)
+{
+ return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, ðbroadcast,
+ (struct eth_addr *)netif->hwaddr, &netif->autoip->llipaddr, ðzero,
+ &netif->autoip->llipaddr, ARP_REQUEST);
+}
+
+/**
+ * Configure interface for use with current LL IP-Address
+ *
+ * @param netif network interface to configure with current LL IP-Address
+ */
+static err_t
+autoip_bind(struct netif *netif)
+{
+ struct autoip *autoip = netif->autoip;
+ ip_addr_t sn_mask, gw_addr;
+
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
+ ("autoip_bind(netif=%p) %c%c%"U16_F" %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num,
+ ip4_addr1_16(&autoip->llipaddr), ip4_addr2_16(&autoip->llipaddr),
+ ip4_addr3_16(&autoip->llipaddr), ip4_addr4_16(&autoip->llipaddr)));
+
+ IP4_ADDR(&sn_mask, 255, 255, 0, 0);
+ IP4_ADDR(&gw_addr, 0, 0, 0, 0);
+
+ netif_set_ipaddr(netif, &autoip->llipaddr);
+ netif_set_netmask(netif, &sn_mask);
+ netif_set_gw(netif, &gw_addr);
+
+ /* bring the interface up */
+ netif_set_up(netif);
+
+ return ERR_OK;
+}
+
+/**
+ * Start AutoIP client
+ *
+ * @param netif network interface on which start the AutoIP client
+ */
+err_t
+autoip_start(struct netif *netif)
+{
+ struct autoip *autoip = netif->autoip;
+ err_t result = ERR_OK;
+
+ if(netif_is_up(netif)) {
+ netif_set_down(netif);
+ }
+
+ /* Set IP-Address, Netmask and Gateway to 0 to make sure that
+ * ARP Packets are formed correctly
+ */
+ ip_addr_set_zero(&netif->ip_addr);
+ ip_addr_set_zero(&netif->netmask);
+ ip_addr_set_zero(&netif->gw);
+
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_start(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0],
+ netif->name[1], (u16_t)netif->num));
+ if(autoip == NULL) {
+ /* no AutoIP client attached yet? */
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
+ ("autoip_start(): starting new AUTOIP client\n"));
+ autoip = (struct autoip *)mem_malloc(sizeof(struct autoip));
+ if(autoip == NULL) {
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
+ ("autoip_start(): could not allocate autoip\n"));
+ return ERR_MEM;
+ }
+ memset(autoip, 0, sizeof(struct autoip));
+ /* store this AutoIP client in the netif */
+ netif->autoip = autoip;
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_start(): allocated autoip"));
+ } else {
+ autoip->state = AUTOIP_STATE_OFF;
+ autoip->ttw = 0;
+ autoip->sent_num = 0;
+ ip_addr_set_zero(&autoip->llipaddr);
+ autoip->lastconflict = 0;
+ }
+
+ autoip_create_addr(netif, &(autoip->llipaddr));
+ autoip_start_probing(netif);
+
+ return result;
+}
+
+static void
+autoip_start_probing(struct netif *netif)
+{
+ struct autoip *autoip = netif->autoip;
+
+ autoip->state = AUTOIP_STATE_PROBING;
+ autoip->sent_num = 0;
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_start_probing(): changing state to PROBING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(&netif->autoip->llipaddr), ip4_addr2_16(&netif->autoip->llipaddr),
+ ip4_addr3_16(&netif->autoip->llipaddr), ip4_addr4_16(&netif->autoip->llipaddr)));
+
+ /* time to wait to first probe, this is randomly
+ * choosen out of 0 to PROBE_WAIT seconds.
+ * compliant to RFC 3927 Section 2.2.1
+ */
+ autoip->ttw = (u16_t)(LWIP_AUTOIP_RAND(netif) % (PROBE_WAIT * AUTOIP_TICKS_PER_SECOND));
+
+ /*
+ * if we tried more then MAX_CONFLICTS we must limit our rate for
+ * accquiring and probing address
+ * compliant to RFC 3927 Section 2.2.1
+ */
+ if(autoip->tried_llipaddr > MAX_CONFLICTS) {
+ autoip->ttw = RATE_LIMIT_INTERVAL * AUTOIP_TICKS_PER_SECOND;
+ }
+}
+
+/**
+ * Handle a possible change in the network configuration.
+ *
+ * If there is an AutoIP address configured, take the interface down
+ * and begin probing with the same address.
+ */
+void
+autoip_network_changed(struct netif *netif)
+{
+ if (netif->autoip && netif->autoip->state != AUTOIP_STATE_OFF) {
+ netif_set_down(netif);
+ autoip_start_probing(netif);
+ }
+}
+
+/**
+ * Stop AutoIP client
+ *
+ * @param netif network interface on which stop the AutoIP client
+ */
+err_t
+autoip_stop(struct netif *netif)
+{
+ netif->autoip->state = AUTOIP_STATE_OFF;
+ netif_set_down(netif);
+ return ERR_OK;
+}
+
+/**
+ * Has to be called in loop every AUTOIP_TMR_INTERVAL milliseconds
+ */
+void
+autoip_tmr()
+{
+ struct netif *netif = netif_list;
+ /* loop through netif's */
+ while (netif != NULL) {
+ /* only act on AutoIP configured interfaces */
+ if (netif->autoip != NULL) {
+ if(netif->autoip->lastconflict > 0) {
+ netif->autoip->lastconflict--;
+ }
+
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
+ ("autoip_tmr() AutoIP-State: %"U16_F", ttw=%"U16_F"\n",
+ (u16_t)(netif->autoip->state), netif->autoip->ttw));
+
+ switch(netif->autoip->state) {
+ case AUTOIP_STATE_PROBING:
+ if(netif->autoip->ttw > 0) {
+ netif->autoip->ttw--;
+ } else {
+ if(netif->autoip->sent_num >= PROBE_NUM) {
+ netif->autoip->state = AUTOIP_STATE_ANNOUNCING;
+ netif->autoip->sent_num = 0;
+ netif->autoip->ttw = ANNOUNCE_WAIT * AUTOIP_TICKS_PER_SECOND;
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_tmr(): changing state to ANNOUNCING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(&netif->autoip->llipaddr), ip4_addr2_16(&netif->autoip->llipaddr),
+ ip4_addr3_16(&netif->autoip->llipaddr), ip4_addr4_16(&netif->autoip->llipaddr)));
+ } else {
+ autoip_arp_probe(netif);
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
+ ("autoip_tmr() PROBING Sent Probe\n"));
+ netif->autoip->sent_num++;
+ /* calculate time to wait to next probe */
+ netif->autoip->ttw = (u16_t)((LWIP_AUTOIP_RAND(netif) %
+ ((PROBE_MAX - PROBE_MIN) * AUTOIP_TICKS_PER_SECOND) ) +
+ PROBE_MIN * AUTOIP_TICKS_PER_SECOND);
+ }
+ }
+ break;
+
+ case AUTOIP_STATE_ANNOUNCING:
+ if(netif->autoip->ttw > 0) {
+ netif->autoip->ttw--;
+ } else {
+ if(netif->autoip->sent_num == 0) {
+ /* We are here the first time, so we waited ANNOUNCE_WAIT seconds
+ * Now we can bind to an IP address and use it.
+ *
+ * autoip_bind calls netif_set_up. This triggers a gratuitous ARP
+ * which counts as an announcement.
+ */
+ autoip_bind(netif);
+ } else {
+ autoip_arp_announce(netif);
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
+ ("autoip_tmr() ANNOUNCING Sent Announce\n"));
+ }
+ netif->autoip->ttw = ANNOUNCE_INTERVAL * AUTOIP_TICKS_PER_SECOND;
+ netif->autoip->sent_num++;
+
+ if(netif->autoip->sent_num >= ANNOUNCE_NUM) {
+ netif->autoip->state = AUTOIP_STATE_BOUND;
+ netif->autoip->sent_num = 0;
+ netif->autoip->ttw = 0;
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_tmr(): changing state to BOUND: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(&netif->autoip->llipaddr), ip4_addr2_16(&netif->autoip->llipaddr),
+ ip4_addr3_16(&netif->autoip->llipaddr), ip4_addr4_16(&netif->autoip->llipaddr)));
+ }
+ }
+ break;
+ }
+ }
+ /* proceed to next network interface */
+ netif = netif->next;
+ }
+}
+
+/**
+ * Handles every incoming ARP Packet, called by etharp_arp_input.
+ *
+ * @param netif network interface to use for autoip processing
+ * @param hdr Incoming ARP packet
+ */
+void
+autoip_arp_reply(struct netif *netif, struct etharp_hdr *hdr)
+{
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_arp_reply()\n"));
+ if ((netif->autoip != NULL) && (netif->autoip->state != AUTOIP_STATE_OFF)) {
+ /* when ip.src == llipaddr && hw.src != netif->hwaddr
+ *
+ * when probing ip.dst == llipaddr && hw.src != netif->hwaddr
+ * we have a conflict and must solve it
+ */
+ ip_addr_t sipaddr, dipaddr;
+ struct eth_addr netifaddr;
+ ETHADDR16_COPY(netifaddr.addr, netif->hwaddr);
+
+ /* Copy struct ip_addr2 to aligned ip_addr, to support compilers without
+ * structure packing (not using structure copy which breaks strict-aliasing rules).
+ */
+ IPADDR2_COPY(&sipaddr, &hdr->sipaddr);
+ IPADDR2_COPY(&dipaddr, &hdr->dipaddr);
+
+ if ((netif->autoip->state == AUTOIP_STATE_PROBING) ||
+ ((netif->autoip->state == AUTOIP_STATE_ANNOUNCING) &&
+ (netif->autoip->sent_num == 0))) {
+ /* RFC 3927 Section 2.2.1:
+ * from beginning to after ANNOUNCE_WAIT
+ * seconds we have a conflict if
+ * ip.src == llipaddr OR
+ * ip.dst == llipaddr && hw.src != own hwaddr
+ */
+ if ((ip_addr_cmp(&sipaddr, &netif->autoip->llipaddr)) ||
+ (ip_addr_cmp(&dipaddr, &netif->autoip->llipaddr) &&
+ !eth_addr_cmp(&netifaddr, &hdr->shwaddr))) {
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
+ ("autoip_arp_reply(): Probe Conflict detected\n"));
+ autoip_restart(netif);
+ }
+ } else {
+ /* RFC 3927 Section 2.5:
+ * in any state we have a conflict if
+ * ip.src == llipaddr && hw.src != own hwaddr
+ */
+ if (ip_addr_cmp(&sipaddr, &netif->autoip->llipaddr) &&
+ !eth_addr_cmp(&netifaddr, &hdr->shwaddr)) {
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
+ ("autoip_arp_reply(): Conflicting ARP-Packet detected\n"));
+ autoip_handle_arp_conflict(netif);
+ }
+ }
+ }
+}
+
+#endif /* LWIP_AUTOIP */
diff --git a/app/lwip/core/ipv4/icmp.c b/app/lwip/core/ipv4/icmp.c
new file mode 100644
index 00000000..2eb3b02c
--- /dev/null
+++ b/app/lwip/core/ipv4/icmp.c
@@ -0,0 +1,338 @@
+/**
+ * @file
+ * ICMP - Internet Control Message Protocol
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ *
+ */
+
+/* Some ICMP messages should be passed to the transport protocols. This
+ is not implemented. */
+
+#include "lwip/opt.h"
+
+#if LWIP_ICMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/icmp.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/ip.h"
+#include "lwip/def.h"
+#include "lwipopts.h"
+#include "lwip/stats.h"
+#include "lwip/snmp.h"
+
+#include
+
+/** Small optimization: set to 0 if incoming PBUF_POOL pbuf always can be
+ * used to modify and send a response packet (and to 1 if this is not the case,
+ * e.g. when link header is stripped of when receiving) */
+#ifndef LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
+#define LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN 1
+#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */
+
+/* The amount of data from the original packet to return in a dest-unreachable */
+#define ICMP_DEST_UNREACH_DATASIZE 8
+
+static void icmp_send_response(struct pbuf *p, u8_t type, u8_t code);
+
+/**
+ * Processes ICMP input packets, called from ip_input().
+ *
+ * Currently only processes icmp echo requests and sends
+ * out the echo response.
+ *
+ * @param p the icmp echo request packet, p->payload pointing to the ip header
+ * @param inp the netif on which this packet was received
+ */
+void
+icmp_input(struct pbuf *p, struct netif *inp)
+{
+ u8_t type;
+#ifdef LWIP_DEBUG
+ u8_t code;
+#endif /* LWIP_DEBUG */
+ struct icmp_echo_hdr *iecho;
+ struct ip_hdr *iphdr;
+ s16_t hlen;
+
+ ICMP_STATS_INC(icmp.recv);
+ snmp_inc_icmpinmsgs();
+
+
+ iphdr = (struct ip_hdr *)p->payload;
+ hlen = IPH_HL(iphdr) * 4;
+ if (pbuf_header(p, -hlen) || (p->tot_len < sizeof(u16_t)*2)) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: short ICMP (%"U16_F" bytes) received\n", p->tot_len));
+ goto lenerr;
+ }
+
+ type = *((u8_t *)p->payload);
+#ifdef LWIP_DEBUG
+ code = *(((u8_t *)p->payload)+1);
+#endif /* LWIP_DEBUG */
+ switch (type) {
+ case ICMP_ER:
+ /* This is OK, echo reply might have been parsed by a raw PCB
+ (as obviously, an echo request has been sent, too). */
+ break;
+ case ICMP_ECHO:
+#if !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING
+ {
+ int accepted = 1;
+#if !LWIP_MULTICAST_PING
+ /* multicast destination address? */
+ if (ip_addr_ismulticast(¤t_iphdr_dest)) {
+ accepted = 0;
+ }
+#endif /* LWIP_MULTICAST_PING */
+#if !LWIP_BROADCAST_PING
+ /* broadcast destination address? */
+ if (ip_addr_isbroadcast(¤t_iphdr_dest, inp)) {
+ accepted = 0;
+ }
+#endif /* LWIP_BROADCAST_PING */
+ /* broadcast or multicast destination address not acceptd? */
+ if (!accepted) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: Not echoing to multicast or broadcast pings\n"));
+ ICMP_STATS_INC(icmp.err);
+ pbuf_free(p);
+ return;
+ }
+ }
+#endif /* !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING */
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ping\n"));
+ if (p->tot_len < sizeof(struct icmp_echo_hdr)) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: bad ICMP echo received\n"));
+ goto lenerr;
+ }
+ if (inet_chksum_pbuf(p) != 0) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo\n"));
+ pbuf_free(p);
+ ICMP_STATS_INC(icmp.chkerr);
+ snmp_inc_icmpinerrors();
+ return;
+ }
+#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
+ if (pbuf_header(p, (PBUF_IP_HLEN + PBUF_LINK_HLEN))) {
+ /* p is not big enough to contain link headers
+ * allocate a new one and copy p into it
+ */
+ struct pbuf *r;
+ /* switch p->payload to ip header */
+ if (pbuf_header(p, hlen)) {
+ LWIP_ASSERT("icmp_input: moving p->payload to ip header failed\n", 0);
+ goto memerr;
+ }
+ /* allocate new packet buffer with space for link headers */
+ r = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM);
+ if (r == NULL) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: allocating new pbuf failed\n"));
+ goto memerr;
+ }
+ LWIP_ASSERT("check that first pbuf can hold struct the ICMP header",
+ (r->len >= hlen + sizeof(struct icmp_echo_hdr)));
+ /* copy the whole packet including ip header */
+ if (pbuf_copy(r, p) != ERR_OK) {
+ LWIP_ASSERT("icmp_input: copying to new pbuf failed\n", 0);
+ goto memerr;
+ }
+ iphdr = (struct ip_hdr *)r->payload;
+ /* switch r->payload back to icmp header */
+ if (pbuf_header(r, -hlen)) {
+ LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0);
+ goto memerr;
+ }
+ /* free the original p */
+ pbuf_free(p);
+ /* we now have an identical copy of p that has room for link headers */
+ p = r;
+ } else {
+ /* restore p->payload to point to icmp header */
+ if (pbuf_header(p, -(s16_t)(PBUF_IP_HLEN + PBUF_LINK_HLEN))) {
+ LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0);
+ goto memerr;
+ }
+ }
+#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */
+ /* At this point, all checks are OK. */
+ /* We generate an answer by switching the dest and src ip addresses,
+ * setting the icmp type to ECHO_RESPONSE and updating the checksum. */
+ iecho = (struct icmp_echo_hdr *)p->payload;
+ ip_addr_copy(iphdr->src, *ip_current_dest_addr());
+ ip_addr_copy(iphdr->dest, *ip_current_src_addr());
+ ICMPH_TYPE_SET(iecho, ICMP_ER);
+ /* adjust the checksum */
+ if (iecho->chksum >= PP_HTONS(0xffff - (ICMP_ECHO << 8))) {
+ iecho->chksum += PP_HTONS(ICMP_ECHO << 8) + 1;
+ } else {
+ iecho->chksum += PP_HTONS(ICMP_ECHO << 8);
+ }
+
+ /* Set the correct TTL and recalculate the header checksum. */
+ IPH_TTL_SET(iphdr, ICMP_TTL);
+ IPH_CHKSUM_SET(iphdr, 0);
+#if CHECKSUM_GEN_IP
+ IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
+#endif /* CHECKSUM_GEN_IP */
+
+ ICMP_STATS_INC(icmp.xmit);
+ /* increase number of messages attempted to send */
+ snmp_inc_icmpoutmsgs();
+ /* increase number of echo replies attempted to send */
+ snmp_inc_icmpoutechoreps();
+
+ if(pbuf_header(p, hlen)) {
+ LWIP_ASSERT("Can't move over header in packet", 0);
+ } else {
+ err_t ret;
+ /* send an ICMP packet, src addr is the dest addr of the curren packet */
+ ret = ip_output_if(p, ip_current_dest_addr(), IP_HDRINCL,
+ ICMP_TTL, 0, IP_PROTO_ICMP, inp);
+ if (ret != ERR_OK) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ip_output_if returned an error: %c.\n", ret));
+ }
+ }
+ break;
+ default:
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ICMP type %"S16_F" code %"S16_F" not supported.\n",
+ (s16_t)type, (s16_t)code));
+ ICMP_STATS_INC(icmp.proterr);
+ ICMP_STATS_INC(icmp.drop);
+ }
+ pbuf_free(p);
+ return;
+lenerr:
+ pbuf_free(p);
+ ICMP_STATS_INC(icmp.lenerr);
+ snmp_inc_icmpinerrors();
+ return;
+#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
+memerr:
+ pbuf_free(p);
+ ICMP_STATS_INC(icmp.err);
+ snmp_inc_icmpinerrors();
+ return;
+#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */
+}
+
+/**
+ * Send an icmp 'destination unreachable' packet, called from ip_input() if
+ * the transport layer protocol is unknown and from udp_input() if the local
+ * port is not bound.
+ *
+ * @param p the input packet for which the 'unreachable' should be sent,
+ * p->payload pointing to the IP header
+ * @param t type of the 'unreachable' packet
+ */
+void
+icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t)
+{
+ icmp_send_response(p, ICMP_DUR, t);
+}
+
+#if IP_FORWARD || IP_REASSEMBLY
+/**
+ * Send a 'time exceeded' packet, called from ip_forward() if TTL is 0.
+ *
+ * @param p the input packet for which the 'time exceeded' should be sent,
+ * p->payload pointing to the IP header
+ * @param t type of the 'time exceeded' packet
+ */
+void
+icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t)
+{
+ icmp_send_response(p, ICMP_TE, t);
+}
+
+#endif /* IP_FORWARD || IP_REASSEMBLY */
+
+/**
+ * Send an icmp packet in response to an incoming packet.
+ *
+ * @param p the input packet for which the 'unreachable' should be sent,
+ * p->payload pointing to the IP header
+ * @param type Type of the ICMP header
+ * @param code Code of the ICMP header
+ */
+static void ICACHE_FLASH_ATTR
+icmp_send_response(struct pbuf *p, u8_t type, u8_t code)
+{
+ struct pbuf *q;
+ struct ip_hdr *iphdr;
+ /* we can use the echo header here */
+ struct icmp_echo_hdr *icmphdr;
+ ip_addr_t iphdr_src;
+
+ /* ICMP header + IP header + 8 bytes of data */
+ //Ϊpbufռ䣬pbufԤIPײ̫ײռ䣬pbuf
+ //=ײ+ݳ(IPײ+8)
+ q = pbuf_alloc(PBUF_IP, sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE,
+ PBUF_RAM);
+ if (q == NULL) {//ʧܣ
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMP packet.\n"));
+ return;
+ }
+ LWIP_ASSERT("check that first pbuf can hold icmp message",
+ (q->len >= (sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE)));
+
+ iphdr = (struct ip_hdr *)p->payload;//ָIPݰײ
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded from "));
+ ip_addr_debug_print(ICMP_DEBUG, &(iphdr->src));
+ LWIP_DEBUGF(ICMP_DEBUG, (" to "));
+ ip_addr_debug_print(ICMP_DEBUG, &(iphdr->dest));
+ LWIP_DEBUGF(ICMP_DEBUG, ("\n"));
+
+ icmphdr = (struct icmp_echo_hdr *)q->payload;//ָײ
+ icmphdr->type = type;//дֶ
+ icmphdr->code = code;//дֶ
+ icmphdr->id = 0;//ĿIJɴݱʱ
+ icmphdr->seqno = 0;//ģײʣ4ֽڶΪ0
+
+ /* copy fields from original packet IPݱIPײ+8ֽݿ*/
+ SMEMCPY((u8_t *)q->payload + sizeof(struct icmp_echo_hdr), (u8_t *)p->payload,
+ IP_HLEN + ICMP_DEST_UNREACH_DATASIZE);
+
+ /* calculate checksum */
+ icmphdr->chksum = 0;//Уֶ0
+ icmphdr->chksum = inet_chksum(icmphdr, q->len);//дУ
+ ICMP_STATS_INC(icmp.xmit);
+ /* increase number of messages attempted to send */
+ snmp_inc_icmpoutmsgs();
+ /* increase number of destination unreachable messages attempted to send */
+ snmp_inc_icmpouttimeexcds();
+ ip_addr_copy(iphdr_src, iphdr->src);
+ ip_output(q, NULL, &iphdr_src, ICMP_TTL, 0, IP_PROTO_ICMP);//IP㺯ICMP
+ pbuf_free(q);
+}
+
+#endif /* LWIP_ICMP */
diff --git a/app/lwip/core/ipv4/igmp.c b/app/lwip/core/ipv4/igmp.c
new file mode 100644
index 00000000..b4cca192
--- /dev/null
+++ b/app/lwip/core/ipv4/igmp.c
@@ -0,0 +1,817 @@
+/**
+ * @file
+ * IGMP - Internet Group Management Protocol
+ *
+ */
+
+/*
+ * Copyright (c) 2002 CITEL Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of CITEL Technologies Ltd nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CITEL TECHNOLOGIES AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL CITEL TECHNOLOGIES OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This file is a contribution to the lwIP TCP/IP stack.
+ * The Swedish Institute of Computer Science and Adam Dunkels
+ * are specifically granted permission to redistribute this
+ * source code.
+*/
+
+/*-------------------------------------------------------------
+Note 1)
+Although the rfc requires V1 AND V2 capability
+we will only support v2 since now V1 is very old (August 1989)
+V1 can be added if required
+
+a debug print and statistic have been implemented to
+show this up.
+-------------------------------------------------------------
+-------------------------------------------------------------
+Note 2)
+A query for a specific group address (as opposed to ALLHOSTS)
+has now been implemented as I am unsure if it is required
+
+a debug print and statistic have been implemented to
+show this up.
+-------------------------------------------------------------
+-------------------------------------------------------------
+Note 3)
+The router alert rfc 2113 is implemented in outgoing packets
+but not checked rigorously incoming
+-------------------------------------------------------------
+Steve Reynolds
+------------------------------------------------------------*/
+
+/*-----------------------------------------------------------------------------
+ * RFC 988 - Host extensions for IP multicasting - V0
+ * RFC 1054 - Host extensions for IP multicasting -
+ * RFC 1112 - Host extensions for IP multicasting - V1
+ * RFC 2236 - Internet Group Management Protocol, Version 2 - V2 <- this code is based on this RFC (it's the "de facto" standard)
+ * RFC 3376 - Internet Group Management Protocol, Version 3 - V3
+ * RFC 4604 - Using Internet Group Management Protocol Version 3... - V3+
+ * RFC 2113 - IP Router Alert Option -
+ *----------------------------------------------------------------------------*/
+
+/*-----------------------------------------------------------------------------
+ * Includes
+ *----------------------------------------------------------------------------*/
+
+#include "lwip/opt.h"
+
+#if LWIP_IGMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/igmp.h"
+#include "lwip/debug.h"
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/ip.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/netif.h"
+#include "lwip/icmp.h"
+#include "lwip/udp.h"
+#include "lwip/tcp.h"
+#include "lwip/stats.h"
+
+#include "string.h"
+
+/*
+ * IGMP constants
+ */
+#define IGMP_TTL 1
+#define IGMP_MINLEN 8
+#define ROUTER_ALERT 0x9404
+#define ROUTER_ALERTLEN 4
+
+/*
+ * IGMP message types, including version number.
+ */
+#define IGMP_MEMB_QUERY 0x11 /* Membership query */
+#define IGMP_V1_MEMB_REPORT 0x12 /* Ver. 1 membership report */
+#define IGMP_V2_MEMB_REPORT 0x16 /* Ver. 2 membership report */
+#define IGMP_LEAVE_GROUP 0x17 /* Leave-group message */
+
+/* Group membership states */
+#define IGMP_GROUP_NON_MEMBER 0
+#define IGMP_GROUP_DELAYING_MEMBER 1
+#define IGMP_GROUP_IDLE_MEMBER 2
+
+/**
+ * IGMP packet format.
+ */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct igmp_msg {
+ PACK_STRUCT_FIELD(u8_t igmp_msgtype);
+ PACK_STRUCT_FIELD(u8_t igmp_maxresp);
+ PACK_STRUCT_FIELD(u16_t igmp_checksum);
+ PACK_STRUCT_FIELD(ip_addr_p_t igmp_group_address);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+
+static struct igmp_group *igmp_lookup_group(struct netif *ifp, ip_addr_t *addr)ICACHE_FLASH_ATTR;
+static err_t igmp_remove_group(struct igmp_group *group)ICACHE_FLASH_ATTR;
+static void igmp_timeout( struct igmp_group *group)ICACHE_FLASH_ATTR;
+static void igmp_start_timer(struct igmp_group *group, u8_t max_time)ICACHE_FLASH_ATTR;
+static void igmp_stop_timer(struct igmp_group *group)ICACHE_FLASH_ATTR;
+static void igmp_delaying_member(struct igmp_group *group, u8_t maxresp)ICACHE_FLASH_ATTR;
+static err_t igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif)ICACHE_FLASH_ATTR;
+static void igmp_send(struct igmp_group *group, u8_t type)ICACHE_FLASH_ATTR;
+
+
+static struct igmp_group* igmp_group_list;
+static ip_addr_t allsystems;
+static ip_addr_t allrouters;
+
+
+/**
+ * Initialize the IGMP module
+ */
+void
+igmp_init(void)
+{
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_init: initializing\n"));
+
+ IP4_ADDR(&allsystems, 224, 0, 0, 1);
+ IP4_ADDR(&allrouters, 224, 0, 0, 2);
+}
+
+#ifdef LWIP_DEBUG
+/**
+ * Dump global IGMP groups list
+ */
+void
+igmp_dump_group_list()
+{
+ struct igmp_group *group = igmp_group_list;
+
+ while (group != NULL) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_dump_group_list: [%"U32_F"] ", (u32_t)(group->group_state)));
+ ip_addr_debug_print(IGMP_DEBUG, &group->group_address);
+ LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->netif));
+ group = group->next;
+ }
+ LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
+}
+#else
+#define igmp_dump_group_list()
+#endif /* LWIP_DEBUG */
+
+/**
+ * Start IGMP processing on interface
+ *
+ * @param netif network interface on which start IGMP processing
+ */
+err_t
+igmp_start(struct netif *netif)
+{
+ struct igmp_group* group;
+
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: starting IGMP processing on if %p\n", netif));
+
+ group = igmp_lookup_group(netif, &allsystems);
+
+ if (group != NULL) {
+ group->group_state = IGMP_GROUP_IDLE_MEMBER;
+ group->use++;
+
+ /* Allow the igmp messages at the MAC level */
+ if (netif->igmp_mac_filter != NULL) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: igmp_mac_filter(ADD "));
+ ip_addr_debug_print(IGMP_DEBUG, &allsystems);
+ LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
+ netif->igmp_mac_filter(netif, &allsystems, IGMP_ADD_MAC_FILTER);
+ }
+
+ return ERR_OK;
+ }
+
+ return ERR_MEM;
+}
+
+/**
+ * Stop IGMP processing on interface
+ *
+ * @param netif network interface on which stop IGMP processing
+ */
+err_t
+igmp_stop(struct netif *netif)
+{
+ struct igmp_group *group = igmp_group_list;
+ struct igmp_group *prev = NULL;
+ struct igmp_group *next;
+
+ /* look for groups joined on this interface further down the list */
+ while (group != NULL) {
+ next = group->next;
+ /* is it a group joined on this interface? */
+ if (group->netif == netif) {
+ /* is it the first group of the list? */
+ if (group == igmp_group_list) {
+ igmp_group_list = next;
+ }
+ /* is there a "previous" group defined? */
+ if (prev != NULL) {
+ prev->next = next;
+ }
+ /* disable the group at the MAC level */
+ if (netif->igmp_mac_filter != NULL) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_stop: igmp_mac_filter(DEL "));
+ ip_addr_debug_print(IGMP_DEBUG, &group->group_address);
+ LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
+ netif->igmp_mac_filter(netif, &(group->group_address), IGMP_DEL_MAC_FILTER);
+ }
+ /* free group */
+ memp_free(MEMP_IGMP_GROUP, group);
+ } else {
+ /* change the "previous" */
+ prev = group;
+ }
+ /* move to "next" */
+ group = next;
+ }
+ return ERR_OK;
+}
+
+/**
+ * Report IGMP memberships for this interface
+ *
+ * @param netif network interface on which report IGMP memberships
+ */
+void
+igmp_report_groups(struct netif *netif)
+{
+ struct igmp_group *group = igmp_group_list;
+
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_report_groups: sending IGMP reports on if %p\n", netif));
+
+ while (group != NULL) {
+ if (group->netif == netif) {
+ igmp_delaying_member(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
+ }
+ group = group->next;
+ }
+}
+
+/**
+ * Search for a group in the global igmp_group_list
+ *
+ * @param ifp the network interface for which to look
+ * @param addr the group ip address to search for
+ * @return a struct igmp_group* if the group has been found,
+ * NULL if the group wasn't found.
+ */
+struct igmp_group *
+igmp_lookfor_group(struct netif *ifp, ip_addr_t *addr)
+{
+ struct igmp_group *group = igmp_group_list;
+
+ while (group != NULL) {
+ if ((group->netif == ifp) && (ip_addr_cmp(&(group->group_address), addr))) {
+ return group;
+ }
+ group = group->next;
+ }
+
+ /* to be clearer, we return NULL here instead of
+ * 'group' (which is also NULL at this point).
+ */
+ return NULL;
+}
+
+/**
+ * Search for a specific igmp group and create a new one if not found-
+ *
+ * @param ifp the network interface for which to look
+ * @param addr the group ip address to search
+ * @return a struct igmp_group*,
+ * NULL on memory error.
+ */
+struct igmp_group *
+igmp_lookup_group(struct netif *ifp, ip_addr_t *addr)
+{
+ struct igmp_group *group = igmp_group_list;
+
+ /* Search if the group already exists */
+ group = igmp_lookfor_group(ifp, addr);
+ if (group != NULL) {
+ /* Group already exists. */
+ return group;
+ }
+
+ /* Group doesn't exist yet, create a new one */
+ group = (struct igmp_group *)memp_malloc(MEMP_IGMP_GROUP);
+ if (group != NULL) {
+ group->netif = ifp;
+ ip_addr_set(&(group->group_address), addr);
+ group->timer = 0; /* Not running */
+ group->group_state = IGMP_GROUP_NON_MEMBER;
+ group->last_reporter_flag = 0;
+ group->use = 0;
+ group->next = igmp_group_list;
+
+ igmp_group_list = group;
+ }
+
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_lookup_group: %sallocated a new group with address ", (group?"":"impossible to ")));
+ ip_addr_debug_print(IGMP_DEBUG, addr);
+ LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", ifp));
+
+ return group;
+}
+
+/**
+ * Remove a group in the global igmp_group_list
+ *
+ * @param group the group to remove from the global igmp_group_list
+ * @return ERR_OK if group was removed from the list, an err_t otherwise
+ */
+static err_t
+igmp_remove_group(struct igmp_group *group)
+{
+ err_t err = ERR_OK;
+
+ /* Is it the first group? */
+ if (igmp_group_list == group) {
+ igmp_group_list = group->next;
+ } else {
+ /* look for group further down the list */
+ struct igmp_group *tmpGroup;
+ for (tmpGroup = igmp_group_list; tmpGroup != NULL; tmpGroup = tmpGroup->next) {
+ if (tmpGroup->next == group) {
+ tmpGroup->next = group->next;
+ break;
+ }
+ }
+ /* Group not found in the global igmp_group_list */
+ if (tmpGroup == NULL)
+ err = ERR_ARG;
+ }
+ /* free group */
+ memp_free(MEMP_IGMP_GROUP, group);
+
+ return err;
+}
+
+/**
+ * Called from ip_input() if a new IGMP packet is received.
+ *
+ * @param p received igmp packet, p->payload pointing to the ip header
+ * @param inp network interface on which the packet was received
+ * @param dest destination ip address of the igmp packet
+ */
+void
+igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest)
+{
+ struct ip_hdr * iphdr;
+ struct igmp_msg* igmp;
+ struct igmp_group* group;
+ struct igmp_group* groupref;
+
+ IGMP_STATS_INC(igmp.recv);
+
+ /* Note that the length CAN be greater than 8 but only 8 are used - All are included in the checksum */
+ iphdr = (struct ip_hdr *)p->payload;
+ if (pbuf_header(p, -(s16_t)(IPH_HL(iphdr) * 4)) || (p->len < IGMP_MINLEN)) {
+ pbuf_free(p);
+ IGMP_STATS_INC(igmp.lenerr);
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: length error\n"));
+ return;
+ }
+
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: message from "));
+ ip_addr_debug_print(IGMP_DEBUG, &(iphdr->src));
+ LWIP_DEBUGF(IGMP_DEBUG, (" to address "));
+ ip_addr_debug_print(IGMP_DEBUG, &(iphdr->dest));
+ LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", inp));
+
+ /* Now calculate and check the checksum */
+ igmp = (struct igmp_msg *)p->payload;
+ if (inet_chksum(igmp, p->len)) {
+ pbuf_free(p);
+ IGMP_STATS_INC(igmp.chkerr);
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: checksum error\n"));
+ return;
+ }
+
+ /* Packet is ok so find an existing group */
+ group = igmp_lookfor_group(inp, dest); /* use the destination IP address of incoming packet */
+
+ /* If group can be found or create... */
+ if (!group) {
+ pbuf_free(p);
+ IGMP_STATS_INC(igmp.drop);
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP frame not for us\n"));
+ return;
+ }
+
+ /* NOW ACT ON THE INCOMING MESSAGE TYPE... */
+ switch (igmp->igmp_msgtype) {
+ case IGMP_MEMB_QUERY: {
+ /* IGMP_MEMB_QUERY to the "all systems" address ? */
+ if ((ip_addr_cmp(dest, &allsystems)) && ip_addr_isany(&igmp->igmp_group_address)) {
+ /* THIS IS THE GENERAL QUERY */
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: General IGMP_MEMB_QUERY on \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
+
+ if (igmp->igmp_maxresp == 0) {
+ IGMP_STATS_INC(igmp.rx_v1);
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: got an all hosts query with time== 0 - this is V1 and not implemented - treat as v2\n"));
+ igmp->igmp_maxresp = IGMP_V1_DELAYING_MEMBER_TMR;
+ } else {
+ IGMP_STATS_INC(igmp.rx_general);
+ }
+
+ groupref = igmp_group_list;
+ while (groupref) {
+ /* Do not send messages on the all systems group address! */
+ if ((groupref->netif == inp) && (!(ip_addr_cmp(&(groupref->group_address), &allsystems)))) {
+ igmp_delaying_member(groupref, igmp->igmp_maxresp);
+ }
+ groupref = groupref->next;
+ }
+ } else {
+ /* IGMP_MEMB_QUERY to a specific group ? */
+ if (!ip_addr_isany(&igmp->igmp_group_address)) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_MEMB_QUERY to a specific group "));
+ ip_addr_debug_print(IGMP_DEBUG, &igmp->igmp_group_address);
+ if (ip_addr_cmp(dest, &allsystems)) {
+ ip_addr_t groupaddr;
+ LWIP_DEBUGF(IGMP_DEBUG, (" using \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
+ /* we first need to re-look for the group since we used dest last time */
+ ip_addr_copy(groupaddr, igmp->igmp_group_address);
+ group = igmp_lookfor_group(inp, &groupaddr);
+ } else {
+ LWIP_DEBUGF(IGMP_DEBUG, (" with the group address as destination [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
+ }
+
+ if (group != NULL) {
+ IGMP_STATS_INC(igmp.rx_group);
+ igmp_delaying_member(group, igmp->igmp_maxresp);
+ } else {
+ IGMP_STATS_INC(igmp.drop);
+ }
+ } else {
+ IGMP_STATS_INC(igmp.proterr);
+ }
+ }
+ break;
+ }
+ case IGMP_V2_MEMB_REPORT: {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_V2_MEMB_REPORT\n"));
+ IGMP_STATS_INC(igmp.rx_report);
+ if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) {
+ /* This is on a specific group we have already looked up */
+ group->timer = 0; /* stopped */
+ group->group_state = IGMP_GROUP_IDLE_MEMBER;
+ group->last_reporter_flag = 0;
+ }
+ break;
+ }
+ default: {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: unexpected msg %d in state %d on group %p on if %p\n",
+ igmp->igmp_msgtype, group->group_state, &group, group->netif));
+ IGMP_STATS_INC(igmp.proterr);
+ break;
+ }
+ }
+
+ pbuf_free(p);
+ return;
+}
+
+/**
+ * Join a group on one network interface.
+ *
+ * @param ifaddr ip address of the network interface which should join a new group
+ * @param groupaddr the ip address of the group which to join
+ * @return ERR_OK if group was joined on the netif(s), an err_t otherwise
+ */
+err_t
+igmp_joingroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr)
+{
+ err_t err = ERR_VAL; /* no matching interface */
+ struct igmp_group *group;
+ struct netif *netif;
+
+ /* make sure it is multicast address */
+ LWIP_ERROR("igmp_joingroup: attempt to join non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;);
+ LWIP_ERROR("igmp_joingroup: attempt to join allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
+
+ /* loop through netif's */
+ netif = netif_list;
+ while (netif != NULL) {
+ /* Should we join this interface ? */
+ if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) {
+ /* find group or create a new one if not found */
+ group = igmp_lookup_group(netif, groupaddr);
+
+ if (group != NULL) {
+ /* This should create a new group, check the state to make sure */
+ if (group->group_state != IGMP_GROUP_NON_MEMBER) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to group not in state IGMP_GROUP_NON_MEMBER\n"));
+ } else {
+ /* OK - it was new group */
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to new group: "));
+ ip_addr_debug_print(IGMP_DEBUG, groupaddr);
+ LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
+
+ /* If first use of the group, allow the group at the MAC level */
+ if ((group->use==0) && (netif->igmp_mac_filter != NULL)) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: igmp_mac_filter(ADD "));
+ ip_addr_debug_print(IGMP_DEBUG, groupaddr);
+ LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
+ netif->igmp_mac_filter(netif, groupaddr, IGMP_ADD_MAC_FILTER);
+ }
+
+ IGMP_STATS_INC(igmp.tx_join);
+ igmp_send(group, IGMP_V2_MEMB_REPORT);
+
+ igmp_start_timer(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
+
+ /* Need to work out where this timer comes from */
+ group->group_state = IGMP_GROUP_DELAYING_MEMBER;
+ }
+ /* Increment group use */
+ group->use++;
+ /* Join on this interface */
+ err = ERR_OK;
+ } else {
+ /* Return an error even if some network interfaces are joined */
+ /** @todo undo any other netif already joined */
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: Not enought memory to join to group\n"));
+ return ERR_MEM;
+ }
+ }
+ /* proceed to next network interface */
+ netif = netif->next;
+ }
+
+ return err;
+}
+
+/**
+ * Leave a group on one network interface.
+ *
+ * @param ifaddr ip address of the network interface which should leave a group
+ * @param groupaddr the ip address of the group which to leave
+ * @return ERR_OK if group was left on the netif(s), an err_t otherwise
+ */
+err_t
+igmp_leavegroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr)
+{
+ err_t err = ERR_VAL; /* no matching interface */
+ struct igmp_group *group;
+ struct netif *netif;
+
+ /* make sure it is multicast address */
+ LWIP_ERROR("igmp_leavegroup: attempt to leave non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;);
+ LWIP_ERROR("igmp_leavegroup: attempt to leave allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
+
+ /* loop through netif's */
+ netif = netif_list;
+ while (netif != NULL) {
+ /* Should we leave this interface ? */
+ if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) {
+ /* find group */
+ group = igmp_lookfor_group(netif, groupaddr);
+
+ if (group != NULL) {
+ /* Only send a leave if the flag is set according to the state diagram */
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: Leaving group: "));
+ ip_addr_debug_print(IGMP_DEBUG, groupaddr);
+ LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
+
+ /* If there is no other use of the group */
+ if (group->use <= 1) {
+ /* If we are the last reporter for this group */
+ if (group->last_reporter_flag) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: sending leaving group\n"));
+ IGMP_STATS_INC(igmp.tx_leave);
+ igmp_send(group, IGMP_LEAVE_GROUP);
+ }
+
+ /* Disable the group at the MAC level */
+ if (netif->igmp_mac_filter != NULL) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: igmp_mac_filter(DEL "));
+ ip_addr_debug_print(IGMP_DEBUG, groupaddr);
+ LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
+ netif->igmp_mac_filter(netif, groupaddr, IGMP_DEL_MAC_FILTER);
+ }
+
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: remove group: "));
+ ip_addr_debug_print(IGMP_DEBUG, groupaddr);
+ LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
+
+ /* Free the group */
+ igmp_remove_group(group);
+ } else {
+ /* Decrement group use */
+ group->use--;
+ }
+ /* Leave on this interface */
+ err = ERR_OK;
+ } else {
+ /* It's not a fatal error on "leavegroup" */
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: not member of group\n"));
+ }
+ }
+ /* proceed to next network interface */
+ netif = netif->next;
+ }
+
+ return err;
+}
+
+/**
+ * The igmp timer function (both for NO_SYS=1 and =0)
+ * Should be called every IGMP_TMR_INTERVAL milliseconds (100 ms is default).
+ */
+void
+igmp_tmr(void)
+{
+ struct igmp_group *group = igmp_group_list;
+
+ while (group != NULL) {
+ if (group->timer > 0) {
+ group->timer--;
+ if (group->timer == 0) {
+ igmp_timeout(group);
+ }
+ }
+ group = group->next;
+ }
+}
+
+/**
+ * Called if a timeout for one group is reached.
+ * Sends a report for this group.
+ *
+ * @param group an igmp_group for which a timeout is reached
+ */
+static void
+igmp_timeout(struct igmp_group *group)
+{
+ /* If the state is IGMP_GROUP_DELAYING_MEMBER then we send a report for this group */
+ if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_timeout: report membership for group with address "));
+ ip_addr_debug_print(IGMP_DEBUG, &(group->group_address));
+ LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->netif));
+
+ IGMP_STATS_INC(igmp.tx_report);
+ igmp_send(group, IGMP_V2_MEMB_REPORT);
+ }
+}
+
+/**
+ * Start a timer for an igmp group
+ *
+ * @param group the igmp_group for which to start a timer
+ * @param max_time the time in multiples of IGMP_TMR_INTERVAL (decrease with
+ * every call to igmp_tmr())
+ */
+static void
+igmp_start_timer(struct igmp_group *group, u8_t max_time)
+{
+ /* ensure the input value is > 0 */
+ if (max_time == 0) {
+ max_time = 1;
+ }
+ /* ensure the random value is > 0 */
+ group->timer = (LWIP_RAND() % (max_time - 1)) + 1;
+}
+
+/**
+ * Stop a timer for an igmp_group
+ *
+ * @param group the igmp_group for which to stop the timer
+ */
+static void
+igmp_stop_timer(struct igmp_group *group)
+{
+ group->timer = 0;
+}
+
+/**
+ * Delaying membership report for a group if necessary
+ *
+ * @param group the igmp_group for which "delaying" membership report
+ * @param maxresp query delay
+ */
+static void
+igmp_delaying_member(struct igmp_group *group, u8_t maxresp)
+{
+ if ((group->group_state == IGMP_GROUP_IDLE_MEMBER) ||
+ ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) &&
+ ((group->timer == 0) || (maxresp < group->timer)))) {
+ igmp_start_timer(group, maxresp);
+ group->group_state = IGMP_GROUP_DELAYING_MEMBER;
+ }
+}
+
+
+/**
+ * Sends an IP packet on a network interface. This function constructs the IP header
+ * and calculates the IP header checksum. If the source IP address is NULL,
+ * the IP address of the outgoing network interface is filled in as source address.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+ protocol header; if dest == IP_HDRINCL, p already includes an IP
+ header and p->payload points to that IP header)
+ * @param src the source IP address to send from (if src == IP_ADDR_ANY, the
+ * IP address of the netif used to send is used as source address)
+ * @param dest the destination IP address to send the packet to
+ * @param ttl the TTL value to be set in the IP header
+ * @param proto the PROTOCOL to be set in the IP header
+ * @param netif the netif on which to send this packet
+ * @return ERR_OK if the packet was sent OK
+ * ERR_BUF if p doesn't have enough space for IP/LINK headers
+ * returns errors returned by netif->output
+ */
+static err_t
+igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif)
+{
+ /* This is the "router alert" option */
+ u16_t ra[2];
+ ra[0] = PP_HTONS(ROUTER_ALERT);
+ ra[1] = 0x0000; /* Router shall examine packet */
+ IGMP_STATS_INC(igmp.xmit);
+ return ip_output_if_opt(p, src, dest, IGMP_TTL, 0, IP_PROTO_IGMP, netif, ra, ROUTER_ALERTLEN);
+}
+
+/**
+ * Send an igmp packet to a specific group.
+ *
+ * @param group the group to which to send the packet
+ * @param type the type of igmp packet to send
+ */
+static void
+igmp_send(struct igmp_group *group, u8_t type)
+{
+ struct pbuf* p = NULL;
+ struct igmp_msg* igmp = NULL;
+ ip_addr_t src = *IP_ADDR_ANY;
+ ip_addr_t* dest = NULL;
+
+ /* IP header + "router alert" option + IGMP header */
+ p = pbuf_alloc(PBUF_TRANSPORT, IGMP_MINLEN, PBUF_RAM);
+
+ if (p) {
+ igmp = (struct igmp_msg *)p->payload;
+ LWIP_ASSERT("igmp_send: check that first pbuf can hold struct igmp_msg",
+ (p->len >= sizeof(struct igmp_msg)));
+ ip_addr_copy(src, group->netif->ip_addr);
+
+ if (type == IGMP_V2_MEMB_REPORT) {
+ dest = &(group->group_address);
+ ip_addr_copy(igmp->igmp_group_address, group->group_address);
+ group->last_reporter_flag = 1; /* Remember we were the last to report */
+ } else {
+ if (type == IGMP_LEAVE_GROUP) {
+ dest = &allrouters;
+ ip_addr_copy(igmp->igmp_group_address, group->group_address);
+ }
+ }
+
+ if ((type == IGMP_V2_MEMB_REPORT) || (type == IGMP_LEAVE_GROUP)) {
+ igmp->igmp_msgtype = type;
+ igmp->igmp_maxresp = 0;
+ igmp->igmp_checksum = 0;
+ igmp->igmp_checksum = inet_chksum(igmp, IGMP_MINLEN);
+
+ igmp_ip_output_if(p, &src, dest, group->netif);
+ }
+
+ pbuf_free(p);
+ } else {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_send: not enough memory for igmp_send\n"));
+ IGMP_STATS_INC(igmp.memerr);
+ }
+}
+
+#endif /* LWIP_IGMP */
diff --git a/app/lwip/core/ipv4/inet.c b/app/lwip/core/ipv4/inet.c
new file mode 100644
index 00000000..e283a576
--- /dev/null
+++ b/app/lwip/core/ipv4/inet.c
@@ -0,0 +1,42 @@
+/**
+ * @file
+ * Functions common to all TCP/IPv4 modules, such as the byte order functions.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/inet.h"
+
diff --git a/app/lwip/core/ipv4/inet_chksum.c b/app/lwip/core/ipv4/inet_chksum.c
new file mode 100644
index 00000000..bfcc40d5
--- /dev/null
+++ b/app/lwip/core/ipv4/inet_chksum.c
@@ -0,0 +1,450 @@
+/**
+ * @file
+ * Incluse internet checksum functions.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/inet_chksum.h"
+#include "lwip/def.h"
+
+#include
+#include
+
+/* These are some reference implementations of the checksum algorithm, with the
+ * aim of being simple, correct and fully portable. Checksumming is the
+ * first thing you would want to optimize for your platform. If you create
+ * your own version, link it in and in your cc.h put:
+ *
+ * #define LWIP_CHKSUM
+ *
+ * Or you can select from the implementations below by defining
+ * LWIP_CHKSUM_ALGORITHM to 1, 2 or 3.
+ */
+
+#ifndef LWIP_CHKSUM
+# define LWIP_CHKSUM lwip_standard_chksum
+# ifndef LWIP_CHKSUM_ALGORITHM
+# define LWIP_CHKSUM_ALGORITHM 2
+# endif
+#endif
+/* If none set: */
+#ifndef LWIP_CHKSUM_ALGORITHM
+# define LWIP_CHKSUM_ALGORITHM 0
+#endif
+
+#if (LWIP_CHKSUM_ALGORITHM == 1) /* Version #1 */
+/**
+ * lwip checksum
+ *
+ * @param dataptr points to start of data to be summed at any boundary
+ * @param len length of data to be summed
+ * @return host order (!) lwip checksum (non-inverted Internet sum)
+ *
+ * @note accumulator size limits summable length to 64k
+ * @note host endianess is irrelevant (p3 RFC1071)
+ */
+static u16_t ICACHE_FLASH_ATTR
+lwip_standard_chksum(void *dataptr, u16_t len)
+{
+ u32_t acc;
+ u16_t src;
+ u8_t *octetptr;
+
+ acc = 0;
+ /* dataptr may be at odd or even addresses */
+ octetptr = (u8_t*)dataptr;
+ while (len > 1) {
+ /* declare first octet as most significant
+ thus assume network order, ignoring host order */
+ src = (*octetptr) << 8;
+ octetptr++;
+ /* declare second octet as least significant */
+ src |= (*octetptr);
+ octetptr++;
+ acc += src;
+ len -= 2;
+ }
+ if (len > 0) {
+ /* accumulate remaining octet */
+ src = (*octetptr) << 8;
+ acc += src;
+ }
+ /* add deferred carry bits */
+ acc = (acc >> 16) + (acc & 0x0000ffffUL);
+ if ((acc & 0xffff0000UL) != 0) {
+ acc = (acc >> 16) + (acc & 0x0000ffffUL);
+ }
+ /* This maybe a little confusing: reorder sum using htons()
+ instead of ntohs() since it has a little less call overhead.
+ The caller must invert bits for Internet sum ! */
+ return htons((u16_t)acc);
+}
+#endif
+
+#if (LWIP_CHKSUM_ALGORITHM == 2) /* Alternative version #2 */
+/*
+ * Curt McDowell
+ * Broadcom Corp.
+ * csm@broadcom.com
+ *
+ * IP checksum two bytes at a time with support for
+ * unaligned buffer.
+ * Works for len up to and including 0x20000.
+ * by Curt McDowell, Broadcom Corp. 12/08/2005
+ *
+ * @param dataptr points to start of data to be summed at any boundary
+ * @param len length of data to be summed
+ * @return host order (!) lwip checksum (non-inverted Internet sum)
+ */
+
+static u16_t ICACHE_FLASH_ATTR
+lwip_standard_chksum(void *dataptr, int len)
+{
+ u8_t *pb = (u8_t *)dataptr;
+ u16_t *ps, t = 0;
+ u32_t sum = 0;
+ int odd = ((mem_ptr_t)pb & 1);
+
+ /* Get aligned to u16_t */
+ if (odd && len > 0) {
+ ((u8_t *)&t)[1] = *pb++;
+ len--;
+ }
+
+ /* Add the bulk of the data */
+ ps = (u16_t *)(void *)pb;
+ while (len > 1) {
+ sum += *ps++;
+ len -= 2;
+ }
+
+ /* Consume left-over byte, if any */
+ if (len > 0) {
+ ((u8_t *)&t)[0] = *(u8_t *)ps;
+ }
+
+ /* Add end bytes */
+ sum += t;
+
+ /* Fold 32-bit sum to 16 bits
+ calling this twice is propably faster than if statements... */
+ sum = FOLD_U32T(sum);
+ sum = FOLD_U32T(sum);
+
+ /* Swap if alignment was odd */
+ if (odd) {
+ sum = SWAP_BYTES_IN_WORD(sum);
+ }
+
+ return (u16_t)sum;
+}
+#endif
+
+#if (LWIP_CHKSUM_ALGORITHM == 3) /* Alternative version #3 */
+/**
+ * An optimized checksum routine. Basically, it uses loop-unrolling on
+ * the checksum loop, treating the head and tail bytes specially, whereas
+ * the inner loop acts on 8 bytes at a time.
+ *
+ * @arg start of buffer to be checksummed. May be an odd byte address.
+ * @len number of bytes in the buffer to be checksummed.
+ * @return host order (!) lwip checksum (non-inverted Internet sum)
+ *
+ * by Curt McDowell, Broadcom Corp. December 8th, 2005
+ */
+
+static u16_t ICACHE_FLASH_ATTR
+lwip_standard_chksum(void *dataptr, int len)
+{
+ u8_t *pb = (u8_t *)dataptr;
+ u16_t *ps, t = 0;
+ u32_t *pl;
+ u32_t sum = 0, tmp;
+ /* starts at odd byte address? */
+ int odd = ((mem_ptr_t)pb & 1);
+
+ if (odd && len > 0) {
+ ((u8_t *)&t)[1] = *pb++;
+ len--;
+ }
+
+ ps = (u16_t *)pb;
+
+ if (((mem_ptr_t)ps & 3) && len > 1) {
+ sum += *ps++;
+ len -= 2;
+ }
+
+ pl = (u32_t *)ps;
+
+ while (len > 7) {
+ tmp = sum + *pl++; /* ping */
+ if (tmp < sum) {
+ tmp++; /* add back carry */
+ }
+
+ sum = tmp + *pl++; /* pong */
+ if (sum < tmp) {
+ sum++; /* add back carry */
+ }
+
+ len -= 8;
+ }
+
+ /* make room in upper bits */
+ sum = FOLD_U32T(sum);
+
+ ps = (u16_t *)pl;
+
+ /* 16-bit aligned word remaining? */
+ while (len > 1) {
+ sum += *ps++;
+ len -= 2;
+ }
+
+ /* dangling tail byte remaining? */
+ if (len > 0) { /* include odd byte */
+ ((u8_t *)&t)[0] = *(u8_t *)ps;
+ }
+
+ sum += t; /* add end bytes */
+
+ /* Fold 32-bit sum to 16 bits
+ calling this twice is propably faster than if statements... */
+ sum = FOLD_U32T(sum);
+ sum = FOLD_U32T(sum);
+
+ if (odd) {
+ sum = SWAP_BYTES_IN_WORD(sum);
+ }
+
+ return (u16_t)sum;
+}
+#endif
+
+/* inet_chksum_pseudo:
+ *
+ * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain.
+ * IP addresses are expected to be in network byte order.
+ *
+ * @param p chain of pbufs over that a checksum should be calculated (ip data part)
+ * @param src source ip address (used for checksum of pseudo header)
+ * @param dst destination ip address (used for checksum of pseudo header)
+ * @param proto ip protocol (used for checksum of pseudo header)
+ * @param proto_len length of the ip data part (used for checksum of pseudo header)
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+inet_chksum_pseudo(struct pbuf *p,
+ ip_addr_t *src, ip_addr_t *dest,
+ u8_t proto, u16_t proto_len)
+{
+ u32_t acc;
+ u32_t addr;
+ struct pbuf *q;
+ u8_t swapped;
+
+ acc = 0;
+ swapped = 0;
+ /* iterate through all pbuf in chain */
+ for(q = p; q != NULL; q = q->next) {
+ LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n",
+ (void *)q, (void *)q->next));
+ acc += LWIP_CHKSUM(q->payload, q->len);
+ /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/
+ /* just executing this next line is probably faster that the if statement needed
+ to check whether we really need to execute it, and does no harm */
+ acc = FOLD_U32T(acc);
+ if (q->len % 2 != 0) {
+ swapped = 1 - swapped;
+ acc = SWAP_BYTES_IN_WORD(acc);
+ }
+ /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/
+ }
+
+ if (swapped) {
+ acc = SWAP_BYTES_IN_WORD(acc);
+ }
+ addr = ip4_addr_get_u32(src);
+ acc += (addr & 0xffffUL);
+ acc += ((addr >> 16) & 0xffffUL);
+ addr = ip4_addr_get_u32(dest);
+ acc += (addr & 0xffffUL);
+ acc += ((addr >> 16) & 0xffffUL);
+ acc += (u32_t)htons((u16_t)proto);
+ acc += (u32_t)htons(proto_len);
+
+ /* Fold 32-bit sum to 16 bits
+ calling this twice is propably faster than if statements... */
+ acc = FOLD_U32T(acc);
+ acc = FOLD_U32T(acc);
+ LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc));
+ return (u16_t)~(acc & 0xffffUL);
+}
+
+/* inet_chksum_pseudo:
+ *
+ * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain.
+ * IP addresses are expected to be in network byte order.
+ *
+ * @param p chain of pbufs over that a checksum should be calculated (ip data part)
+ * @param src source ip address (used for checksum of pseudo header)
+ * @param dst destination ip address (used for checksum of pseudo header)
+ * @param proto ip protocol (used for checksum of pseudo header)
+ * @param proto_len length of the ip data part (used for checksum of pseudo header)
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+inet_chksum_pseudo_partial(struct pbuf *p,
+ ip_addr_t *src, ip_addr_t *dest,
+ u8_t proto, u16_t proto_len, u16_t chksum_len)
+{
+ u32_t acc;
+ u32_t addr;
+ struct pbuf *q;
+ u8_t swapped;
+ u16_t chklen;
+
+ acc = 0;
+ swapped = 0;
+ /* iterate through all pbuf in chain */
+ for(q = p; (q != NULL) && (chksum_len > 0); q = q->next) {
+ LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n",
+ (void *)q, (void *)q->next));
+ chklen = q->len;
+ if (chklen > chksum_len) {
+ chklen = chksum_len;
+ }
+ acc += LWIP_CHKSUM(q->payload, chklen);
+ chksum_len -= chklen;
+ LWIP_ASSERT("delete me", chksum_len < 0x7fff);
+ /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/
+ /* fold the upper bit down */
+ acc = FOLD_U32T(acc);
+ if (q->len % 2 != 0) {
+ swapped = 1 - swapped;
+ acc = SWAP_BYTES_IN_WORD(acc);
+ }
+ /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/
+ }
+
+ if (swapped) {
+ acc = SWAP_BYTES_IN_WORD(acc);
+ }
+ addr = ip4_addr_get_u32(src);
+ acc += (addr & 0xffffUL);
+ acc += ((addr >> 16) & 0xffffUL);
+ addr = ip4_addr_get_u32(dest);
+ acc += (addr & 0xffffUL);
+ acc += ((addr >> 16) & 0xffffUL);
+ acc += (u32_t)htons((u16_t)proto);
+ acc += (u32_t)htons(proto_len);
+
+ /* Fold 32-bit sum to 16 bits
+ calling this twice is propably faster than if statements... */
+ acc = FOLD_U32T(acc);
+ acc = FOLD_U32T(acc);
+ LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc));
+ return (u16_t)~(acc & 0xffffUL);
+}
+
+/* inet_chksum:
+ *
+ * Calculates the Internet checksum over a portion of memory. Used primarily for IP
+ * and ICMP.
+ *
+ * @param dataptr start of the buffer to calculate the checksum (no alignment needed)
+ * @param len length of the buffer to calculate the checksum
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+
+u16_t
+inet_chksum(void *dataptr, u16_t len)
+{
+ return ~LWIP_CHKSUM(dataptr, len);
+}
+
+/**
+ * Calculate a checksum over a chain of pbufs (without pseudo-header, much like
+ * inet_chksum only pbufs are used).
+ *
+ * @param p pbuf chain over that the checksum should be calculated
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+inet_chksum_pbuf(struct pbuf *p)
+{
+ u32_t acc;
+ struct pbuf *q;
+ u8_t swapped;
+
+ acc = 0;
+ swapped = 0;
+ for(q = p; q != NULL; q = q->next) {
+ acc += LWIP_CHKSUM(q->payload, q->len);
+ acc = FOLD_U32T(acc);
+ if (q->len % 2 != 0) {
+ swapped = 1 - swapped;
+ acc = SWAP_BYTES_IN_WORD(acc);
+ }
+ }
+
+ if (swapped) {
+ acc = SWAP_BYTES_IN_WORD(acc);
+ }
+ return (u16_t)~(acc & 0xffffUL);
+}
+
+/* These are some implementations for LWIP_CHKSUM_COPY, which copies data
+ * like MEMCPY but generates a checksum at the same time. Since this is a
+ * performance-sensitive function, you might want to create your own version
+ * in assembly targeted at your hardware by defining it in lwipopts.h:
+ * #define LWIP_CHKSUM_COPY(dst, src, len) your_chksum_copy(dst, src, len)
+ */
+
+#if (LWIP_CHKSUM_COPY_ALGORITHM == 1) /* Version #1 */
+/** Safe but slow: first call MEMCPY, then call LWIP_CHKSUM.
+ * For architectures with big caches, data might still be in cache when
+ * generating the checksum after copying.
+ */
+u16_t
+lwip_chksum_copy(void *dst, const void *src, u16_t len)
+{
+ MEMCPY(dst, src, len);
+ return LWIP_CHKSUM(dst, len);
+}
+#endif /* (LWIP_CHKSUM_COPY_ALGORITHM == 1) */
diff --git a/app/lwip/core/ipv4/ip.c b/app/lwip/core/ipv4/ip.c
new file mode 100644
index 00000000..6046e342
--- /dev/null
+++ b/app/lwip/core/ipv4/ip.c
@@ -0,0 +1,904 @@
+/**
+ * @file
+ * This is the IPv4 layer implementation for incoming and outgoing IP traffic.
+ *
+ * @see ip_frag.c
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ *
+ */
+
+#include "lwip/opt.h"
+#include "lwip/ip.h"
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/ip_frag.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/netif.h"
+#include "lwip/icmp.h"
+#include "lwip/igmp.h"
+#include "lwip/raw.h"
+#include "lwip/udp.h"
+#include "lwip/tcp_impl.h"
+#include "lwip/snmp.h"
+#include "lwip/dhcp.h"
+#include "lwip/autoip.h"
+#include "lwip/stats.h"
+#include "arch/perf.h"
+
+#include
+
+/** Set this to 0 in the rare case of wanting to call an extra function to
+ * generate the IP checksum (in contrast to calculating it on-the-fly). */
+#ifndef LWIP_INLINE_IP_CHKSUM
+#define LWIP_INLINE_IP_CHKSUM 1
+#endif
+#if LWIP_INLINE_IP_CHKSUM && CHECKSUM_GEN_IP
+#define CHECKSUM_GEN_IP_INLINE 1
+#else
+#define CHECKSUM_GEN_IP_INLINE 0
+#endif
+
+#if LWIP_DHCP || defined(LWIP_IP_ACCEPT_UDP_PORT)
+#define IP_ACCEPT_LINK_LAYER_ADDRESSING 1
+
+/** Some defines for DHCP to let link-layer-addressed packets through while the
+ * netif is down.
+ * To use this in your own application/protocol, define LWIP_IP_ACCEPT_UDP_PORT
+ * to return 1 if the port is accepted and 0 if the port is not accepted.
+ */
+#if LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT)
+/* accept DHCP client port and custom port */
+#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) (((port) == PP_NTOHS(DHCP_CLIENT_PORT)) \
+ || (LWIP_IP_ACCEPT_UDP_PORT(port)))
+#elif defined(LWIP_IP_ACCEPT_UDP_PORT) /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */
+/* accept custom port only */
+#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) (LWIP_IP_ACCEPT_UDP_PORT(dst_port))
+#else /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */
+/* accept DHCP client port only */
+#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) ((port) == PP_NTOHS(DHCP_CLIENT_PORT))
+#endif /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */
+
+#else /* LWIP_DHCP */
+#define IP_ACCEPT_LINK_LAYER_ADDRESSING 0
+#endif /* LWIP_DHCP */
+
+/**
+ * The interface that provided the packet for the current callback
+ * invocation.
+ */
+struct netif *current_netif;
+
+/**
+ * Header of the input packet currently being processed.
+ */
+const struct ip_hdr *current_header;
+/** Source IP address of current_header */
+ip_addr_t current_iphdr_src;
+/** Destination IP address of current_header */
+ip_addr_t current_iphdr_dest;
+
+/** The IP header ID of the next outgoing IP packet */
+static u16_t ip_id;
+
+/**
+ * Finds the appropriate network interface for a given IP address. It
+ * searches the list of network interfaces linearly. A match is found
+ * if the masked IP address of the network interface equals the masked
+ * IP address given to the function.
+ *
+ * @param dest the destination IP address for which to find the route
+ * @return the netif on which to send to reach dest
+ */
+struct netif *
+ip_route(ip_addr_t *dest)
+{
+ struct netif *netif;
+
+ /* iterate through netifs */
+ for(netif = netif_list; netif != NULL; netif = netif->next) {
+ /* network mask matches? */
+ if (netif_is_up(netif)) {
+ if (ip_addr_netcmp(dest, &(netif->ip_addr), &(netif->netmask))) {
+ /* return netif on which to forward IP packet */
+ return netif;
+ }
+ if (!ip_addr_isbroadcast(dest, netif) && netif != netif_default) {
+ return netif;
+ }
+ }
+ }
+ if ((netif_default == NULL) || (!netif_is_up(netif_default))) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_route: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest)));
+ IP_STATS_INC(ip.rterr);
+ snmp_inc_ipoutnoroutes();
+ return NULL;
+ }
+ /* no matching netif found, use default netif */
+ return netif_default;
+}
+
+/**
+ * Finds the appropriate network interface for a source IP address. It
+ * searches the list of network interfaces linearly. A match is found
+ * if the masked IP address of the network interface equals the masked
+ * IP address given to the function.
+ *
+ * @param source the sourcination IP address for which to find the route
+ * @return the netif on which to send to reach source
+ */
+
+struct netif *ICACHE_FLASH_ATTR
+ip_router(ip_addr_t *dest, ip_addr_t *source){
+ struct netif *netif;
+ /* iterate through netifs */
+ for(netif = netif_list; netif != NULL; netif = netif->next) {
+ /* network mask matches? */
+
+ if (netif_is_up(netif)) {
+ if (ip_addr_netcmp(dest, &(netif->ip_addr), &(netif->netmask))) {
+ /* return netif on which to forward IP packet */
+ return netif;
+ }
+ }
+
+ if (netif_is_up(netif)) {
+ if (ip_addr_netcmp(source, &(netif->ip_addr), &(netif->netmask))) {
+ /* return netif on which to forward IP packet */
+ return netif;
+ }
+ }
+ }
+
+ if ((netif_default == NULL) || (!netif_is_up(netif_default))) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_route: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest)));
+ IP_STATS_INC(ip.rterr);
+ snmp_inc_ipoutnoroutes();
+ return NULL;
+ }
+ /* no matching netif found, use default netif */
+ os_printf("ip_router %d %p\n", __LINE__, netif_default);
+ return netif_default;
+}
+
+#if IP_FORWARD
+/**
+ * Forwards an IP packet. It finds an appropriate route for the
+ * packet, decrements the TTL value of the packet, adjusts the
+ * checksum and outputs the packet on the appropriate interface.
+ *
+ * @param p the packet to forward (p->payload points to IP header)
+ * @param iphdr the IP header of the input packet
+ * @param inp the netif on which this packet was received
+ */
+static void ICACHE_FLASH_ATTR
+ip_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp)
+{
+ struct netif *netif;
+
+ PERF_START;
+
+ /* RFC3927 2.7: do not forward link-local addresses */
+ if (ip_addr_islinklocal(¤t_iphdr_dest)) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip_forward: not forwarding LLA %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(¤t_iphdr_dest), ip4_addr2_16(¤t_iphdr_dest),
+ ip4_addr3_16(¤t_iphdr_dest), ip4_addr4_16(¤t_iphdr_dest)));
+ goto return_noroute;
+ }
+
+ /* Find network interface where to forward this IP packet to. */
+ netif = ip_route(¤t_iphdr_dest);
+ if (netif == NULL) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip_forward: no forwarding route for %"U16_F".%"U16_F".%"U16_F".%"U16_F" found\n",
+ ip4_addr1_16(¤t_iphdr_dest), ip4_addr2_16(¤t_iphdr_dest),
+ ip4_addr3_16(¤t_iphdr_dest), ip4_addr4_16(¤t_iphdr_dest)));
+ goto return_noroute;
+ }
+ /* Do not forward packets onto the same network interface on which
+ * they arrived. */
+ if (netif == inp) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip_forward: not bouncing packets back on incoming interface.\n"));
+ goto return_noroute;
+ }
+
+ /* decrement TTL */
+ IPH_TTL_SET(iphdr, IPH_TTL(iphdr) - 1);
+ /* send ICMP if TTL == 0 */
+ if (IPH_TTL(iphdr) == 0) {
+ snmp_inc_ipinhdrerrors();
+#if LWIP_ICMP
+ /* Don't send ICMP messages in response to ICMP messages */
+ if (IPH_PROTO(iphdr) != IP_PROTO_ICMP) {
+ icmp_time_exceeded(p, ICMP_TE_TTL);
+ }
+#endif /* LWIP_ICMP */
+ return;
+ }
+
+ /* Incrementally update the IP checksum. */
+ if (IPH_CHKSUM(iphdr) >= PP_HTONS(0xffff - 0x100)) {
+ IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + PP_HTONS(0x100) + 1);
+ } else {
+ IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + PP_HTONS(0x100));
+ }
+
+ LWIP_DEBUGF(IP_DEBUG, ("ip_forward: forwarding packet to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(¤t_iphdr_dest), ip4_addr2_16(¤t_iphdr_dest),
+ ip4_addr3_16(¤t_iphdr_dest), ip4_addr4_16(¤t_iphdr_dest)));
+
+ IP_STATS_INC(ip.fw);
+ IP_STATS_INC(ip.xmit);
+ snmp_inc_ipforwdatagrams();
+
+ PERF_STOP("ip_forward");
+ /* transmit pbuf on chosen interface */
+ netif->output(netif, p, ¤t_iphdr_dest);
+ return;
+return_noroute:
+ snmp_inc_ipoutnoroutes();
+}
+#endif /* IP_FORWARD */
+
+/**
+ * This function is called by the network interface device driver when
+ * an IP packet is received. The function does the basic checks of the
+ * IP header such as packet size being at least larger than the header
+ * size etc. If the packet was not destined for us, the packet is
+ * forwarded (using ip_forward). The IP checksum is always checked.
+ *
+ * Finally, the packet is sent to the upper layer protocol input function.
+ *
+ * @param p the received IP packet (p->payload points to IP header)
+ * @param inp the netif on which this packet was received
+ * @return ERR_OK if the packet was processed (could return ERR_* if it wasn't
+ * processed, but currently always returns ERR_OK)
+ */
+err_t
+ip_input(struct pbuf *p, struct netif *inp)
+{
+ struct ip_hdr *iphdr;
+ struct netif *netif;
+ u16_t iphdr_hlen;
+ u16_t iphdr_len;
+#if IP_ACCEPT_LINK_LAYER_ADDRESSING
+ int check_ip_src=1;
+#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */
+
+ IP_STATS_INC(ip.recv);
+ snmp_inc_ipinreceives();
+
+ /* identify the IP header */
+ iphdr = (struct ip_hdr *)p->payload;
+ if (IPH_V(iphdr) != 4) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_WARNING, ("IP packet dropped due to bad version number %"U16_F"\n", IPH_V(iphdr)));
+ ip_debug_print(p);
+ pbuf_free(p);
+ IP_STATS_INC(ip.err);
+ IP_STATS_INC(ip.drop);
+ snmp_inc_ipinhdrerrors();
+ return ERR_OK;
+ }
+
+ /* obtain IP header length in number of 32-bit words */
+ iphdr_hlen = IPH_HL(iphdr);
+ /* calculate IP header length in bytes */
+ iphdr_hlen *= 4;
+ /* obtain ip length in bytes */
+ iphdr_len = ntohs(IPH_LEN(iphdr));
+
+ /* header length exceeds first pbuf length, or ip length exceeds total pbuf length? */
+ if ((iphdr_hlen > p->len) || (iphdr_len > p->tot_len)) {
+ if (iphdr_hlen > p->len) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("IP header (len %"U16_F") does not fit in first pbuf (len %"U16_F"), IP packet dropped.\n",
+ iphdr_hlen, p->len));
+ }
+ if (iphdr_len > p->tot_len) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("IP (len %"U16_F") is longer than pbuf (len %"U16_F"), IP packet dropped.\n",
+ iphdr_len, p->tot_len));
+ }
+ /* free (drop) packet pbufs */
+ pbuf_free(p);
+ IP_STATS_INC(ip.lenerr);
+ IP_STATS_INC(ip.drop);
+ snmp_inc_ipindiscards();
+ return ERR_OK;
+ }
+
+ /* verify checksum */
+#if CHECKSUM_CHECK_IP
+ if (inet_chksum(iphdr, iphdr_hlen) != 0) {
+
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("Checksum (0x%"X16_F") failed, IP packet dropped.\n", inet_chksum(iphdr, iphdr_hlen)));
+ ip_debug_print(p);
+ pbuf_free(p);
+ IP_STATS_INC(ip.chkerr);
+ IP_STATS_INC(ip.drop);
+ snmp_inc_ipinhdrerrors();
+ return ERR_OK;
+ }
+#endif
+
+ /* Trim pbuf. This should have been done at the netif layer,
+ * but we'll do it anyway just to be sure that its done. */
+ pbuf_realloc(p, iphdr_len);
+
+ /* copy IP addresses to aligned ip_addr_t */
+ ip_addr_copy(current_iphdr_dest, iphdr->dest);
+ ip_addr_copy(current_iphdr_src, iphdr->src);
+
+ /* match packet against an interface, i.e. is this packet for us? */
+#if LWIP_IGMP
+ if (ip_addr_ismulticast(¤t_iphdr_dest)) {
+ if ((inp->flags & NETIF_FLAG_IGMP) && (igmp_lookfor_group(inp, ¤t_iphdr_dest))) {
+ netif = inp;
+ } else {
+ netif = NULL;
+ }
+ } else
+#endif /* LWIP_IGMP */
+ {
+ /* start trying with inp. if that's not acceptable, start walking the
+ list of configured netifs.
+ 'first' is used as a boolean to mark whether we started walking the list */
+ int first = 1;
+ netif = inp;
+ do {
+ LWIP_DEBUGF(IP_DEBUG, ("ip_input: iphdr->dest 0x%"X32_F" netif->ip_addr 0x%"X32_F" (0x%"X32_F", 0x%"X32_F", 0x%"X32_F")\n",
+ ip4_addr_get_u32(&iphdr->dest), ip4_addr_get_u32(&netif->ip_addr),
+ ip4_addr_get_u32(&iphdr->dest) & ip4_addr_get_u32(&netif->netmask),
+ ip4_addr_get_u32(&netif->ip_addr) & ip4_addr_get_u32(&netif->netmask),
+ ip4_addr_get_u32(&iphdr->dest) & ~ip4_addr_get_u32(&netif->netmask)));
+
+ /* interface is up and configured? */
+ if ((netif_is_up(netif)) && (!ip_addr_isany(&(netif->ip_addr)))) {
+ /* unicast to this interface address? */
+ if (ip_addr_cmp(¤t_iphdr_dest, &(netif->ip_addr)) ||
+ /* or broadcast on this interface network address? */
+ ip_addr_isbroadcast(¤t_iphdr_dest, netif)) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip_input: packet accepted on interface %c%c\n",
+ netif->name[0], netif->name[1]));
+ /* break out of for loop */
+ break;
+ }
+#if LWIP_AUTOIP
+ /* connections to link-local addresses must persist after changing
+ the netif's address (RFC3927 ch. 1.9) */
+ if ((netif->autoip != NULL) &&
+ ip_addr_cmp(¤t_iphdr_dest, &(netif->autoip->llipaddr))) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip_input: LLA packet accepted on interface %c%c\n",
+ netif->name[0], netif->name[1]));
+ /* break out of for loop */
+ break;
+ }
+#endif /* LWIP_AUTOIP */
+ }
+ if (first) {
+ first = 0;
+ netif = netif_list;
+ } else {
+ netif = netif->next;
+ }
+ if (netif == inp) {
+ netif = netif->next;
+ }
+ } while(netif != NULL);
+ }
+
+#if IP_ACCEPT_LINK_LAYER_ADDRESSING
+ /* Pass DHCP messages regardless of destination address. DHCP traffic is addressed
+ * using link layer addressing (such as Ethernet MAC) so we must not filter on IP.
+ * According to RFC 1542 section 3.1.1, referred by RFC 2131).
+ *
+ * If you want to accept private broadcast communication while a netif is down,
+ * define LWIP_IP_ACCEPT_UDP_PORT(dst_port), e.g.:
+ *
+ * #define LWIP_IP_ACCEPT_UDP_PORT(dst_port) ((dst_port) == PP_NTOHS(12345))
+ */
+ if (netif == NULL) {
+ /* remote port is DHCP server? */
+ if (IPH_PROTO(iphdr) == IP_PROTO_UDP) {
+ struct udp_hdr *udphdr = (struct udp_hdr *)((u8_t *)iphdr + iphdr_hlen);
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: UDP packet to DHCP client port %"U16_F"\n",
+ ntohs(udphdr->dest)));
+ if (IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(udphdr->dest)) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: DHCP packet accepted.\n"));
+ netif = inp;
+ check_ip_src = 0;
+ }
+ }
+ }
+#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */
+
+ /* broadcast or multicast packet source address? Compliant with RFC 1122: 3.2.1.3 */
+#if IP_ACCEPT_LINK_LAYER_ADDRESSING
+ /* DHCP servers need 0.0.0.0 to be allowed as source address (RFC 1.1.2.2: 3.2.1.3/a) */
+ if (check_ip_src && !ip_addr_isany(¤t_iphdr_src))
+#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */
+ { if ((ip_addr_isbroadcast(¤t_iphdr_src, inp)) ||
+ (ip_addr_ismulticast(¤t_iphdr_src))) {
+ /* packet source is not valid */
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("ip_input: packet source is not valid.\n"));
+ /* free (drop) packet pbufs */
+ pbuf_free(p);
+ IP_STATS_INC(ip.drop);
+ snmp_inc_ipinaddrerrors();
+ snmp_inc_ipindiscards();
+ return ERR_OK;
+ }
+ }
+
+ /* packet not for us? */
+ if (netif == NULL) {
+ /* packet not for us, route or discard */
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: packet not for us.\n"));
+#if IP_FORWARD
+ /* non-broadcast packet? */
+ if (!ip_addr_isbroadcast(¤t_iphdr_dest, inp)) {
+ /* try to forward IP packet on (other) interfaces */
+ ip_forward(p, iphdr, inp);
+ } else
+#endif /* IP_FORWARD */
+ {
+ snmp_inc_ipinaddrerrors();
+ snmp_inc_ipindiscards();
+ }
+ pbuf_free(p);
+ return ERR_OK;
+ }
+ /* packet consists of multiple fragments? */
+ if ((IPH_OFFSET(iphdr) & PP_HTONS(IP_OFFMASK | IP_MF)) != 0) {
+#if IP_REASSEMBLY /* packet fragment reassembly code present? */
+ LWIP_DEBUGF(IP_DEBUG, ("IP packet is a fragment (id=0x%04"X16_F" tot_len=%"U16_F" len=%"U16_F" MF=%"U16_F" offset=%"U16_F"), calling ip_reass()\n",
+ ntohs(IPH_ID(iphdr)), p->tot_len, ntohs(IPH_LEN(iphdr)), !!(IPH_OFFSET(iphdr) & PP_HTONS(IP_MF)), (ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK)*8));
+ /* reassemble the packet*/
+ p = ip_reass(p);
+ /* packet not fully reassembled yet? */
+ if (p == NULL) {
+ return ERR_OK;
+ }
+ iphdr = (struct ip_hdr *)p->payload;
+#else /* IP_REASSEMBLY == 0, no packet fragment reassembly code present */
+ pbuf_free(p);
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IP packet dropped since it was fragmented (0x%"X16_F") (while IP_REASSEMBLY == 0).\n",
+ ntohs(IPH_OFFSET(iphdr))));
+ IP_STATS_INC(ip.opterr);
+ IP_STATS_INC(ip.drop);
+ /* unsupported protocol feature */
+ snmp_inc_ipinunknownprotos();
+ return ERR_OK;
+#endif /* IP_REASSEMBLY */
+ }
+
+#if IP_OPTIONS_ALLOWED == 0 /* no support for IP options in the IP header? */
+
+#if LWIP_IGMP
+ /* there is an extra "router alert" option in IGMP messages which we allow for but do not police */
+ if((iphdr_hlen > IP_HLEN) && (IPH_PROTO(iphdr) != IP_PROTO_IGMP)) {
+#else
+ if (iphdr_hlen > IP_HLEN) {
+#endif /* LWIP_IGMP */
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IP packet dropped since there were IP options (while IP_OPTIONS_ALLOWED == 0).\n"));
+ pbuf_free(p);
+ IP_STATS_INC(ip.opterr);
+ IP_STATS_INC(ip.drop);
+ /* unsupported protocol feature */
+ snmp_inc_ipinunknownprotos();
+ return ERR_OK;
+ }
+#endif /* IP_OPTIONS_ALLOWED == 0 */
+
+ /* send to upper layers */
+ LWIP_DEBUGF(IP_DEBUG, ("ip_input: \n"));
+ ip_debug_print(p);
+ LWIP_DEBUGF(IP_DEBUG, ("ip_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len));
+
+ current_netif = inp;
+ current_header = iphdr;
+
+#if LWIP_RAW
+ /* raw input did not eat the packet? */
+ if (raw_input(p, inp) == 0)
+#endif /* LWIP_RAW */
+ {
+
+ switch (IPH_PROTO(iphdr)) {
+#if LWIP_UDP
+ case IP_PROTO_UDP:
+#if LWIP_UDPLITE
+ case IP_PROTO_UDPLITE:
+#endif /* LWIP_UDPLITE */
+ snmp_inc_ipindelivers();
+ udp_input(p, inp);
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case IP_PROTO_TCP:
+ snmp_inc_ipindelivers();
+ tcp_input(p, inp);
+ break;
+#endif /* LWIP_TCP */
+#if LWIP_ICMP
+ case IP_PROTO_ICMP:
+ snmp_inc_ipindelivers();
+ icmp_input(p, inp);
+ break;
+#endif /* LWIP_ICMP */
+#if LWIP_IGMP
+ case IP_PROTO_IGMP:
+ igmp_input(p, inp, ¤t_iphdr_dest);
+ break;
+#endif /* LWIP_IGMP */
+ default:
+#if LWIP_ICMP
+ /* send ICMP destination protocol unreachable unless is was a broadcast */
+ if (!ip_addr_isbroadcast(¤t_iphdr_dest, inp) &&
+ !ip_addr_ismulticast(¤t_iphdr_dest)) {
+ p->payload = iphdr;
+ icmp_dest_unreach(p, ICMP_DUR_PROTO);
+ }
+#endif /* LWIP_ICMP */
+ pbuf_free(p);
+
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("Unsupported transport protocol %"U16_F"\n", IPH_PROTO(iphdr)));
+
+ IP_STATS_INC(ip.proterr);
+ IP_STATS_INC(ip.drop);
+ snmp_inc_ipinunknownprotos();
+ }
+ }
+
+ current_netif = NULL;
+ current_header = NULL;
+ ip_addr_set_any(¤t_iphdr_src);
+ ip_addr_set_any(¤t_iphdr_dest);
+
+ return ERR_OK;
+}
+
+/**
+ * Sends an IP packet on a network interface. This function constructs
+ * the IP header and calculates the IP header checksum. If the source
+ * IP address is NULL, the IP address of the outgoing network
+ * interface is filled in as source address.
+ * If the destination IP address is IP_HDRINCL, p is assumed to already
+ * include an IP header and p->payload points to it instead of the data.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+ protocol header; if dest == IP_HDRINCL, p already includes an IP
+ header and p->payload points to that IP header)
+ * @param src the source IP address to send from (if src == IP_ADDR_ANY, the
+ * IP address of the netif used to send is used as source address)
+ * @param dest the destination IP address to send the packet to
+ * @param ttl the TTL value to be set in the IP header
+ * @param tos the TOS value to be set in the IP header
+ * @param proto the PROTOCOL to be set in the IP header
+ * @param netif the netif on which to send this packet
+ * @return ERR_OK if the packet was sent OK
+ * ERR_BUF if p doesn't have enough space for IP/LINK headers
+ * returns errors returned by netif->output
+ *
+ * @note ip_id: RFC791 "some host may be able to simply use
+ * unique identifiers independent of destination"
+ */
+err_t
+ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
+ u8_t ttl, u8_t tos,
+ u8_t proto, struct netif *netif)
+{
+#if IP_OPTIONS_SEND
+ return ip_output_if_opt(p, src, dest, ttl, tos, proto, netif, NULL, 0);
+}
+
+/**
+ * Same as ip_output_if() but with the possibility to include IP options:
+ *
+ * @ param ip_options pointer to the IP options, copied into the IP header
+ * @ param optlen length of ip_options
+ */
+err_t ip_output_if_opt(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
+ u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options,
+ u16_t optlen)
+{
+#endif /* IP_OPTIONS_SEND */
+ struct ip_hdr *iphdr;
+ ip_addr_t dest_addr;
+#if CHECKSUM_GEN_IP_INLINE
+ u32_t chk_sum = 0;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+
+ /* pbufs passed to IP must have a ref-count of 1 as their payload pointer
+ gets altered as the packet is passed down the stack */
+ LWIP_ASSERT("p->ref == 1", p->ref == 1);
+
+ snmp_inc_ipoutrequests();
+
+ /* Should the IP header be generated or is it already included in p? */
+ if (dest != IP_HDRINCL) {
+ u16_t ip_hlen = IP_HLEN;
+#if IP_OPTIONS_SEND
+ u16_t optlen_aligned = 0;
+ if (optlen != 0) {
+#if CHECKSUM_GEN_IP_INLINE
+ int i;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+ /* round up to a multiple of 4 */
+ optlen_aligned = ((optlen + 3) & ~3);
+ ip_hlen += optlen_aligned;
+ /* First write in the IP options */
+ if (pbuf_header(p, optlen_aligned)) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_output_if_opt: not enough room for IP options in pbuf\n"));
+ IP_STATS_INC(ip.err);
+ snmp_inc_ipoutdiscards();
+ return ERR_BUF;
+ }
+ MEMCPY(p->payload, ip_options, optlen);
+ if (optlen < optlen_aligned) {
+ /* zero the remaining bytes */
+ memset(((char*)p->payload) + optlen, 0, optlen_aligned - optlen);
+ }
+#if CHECKSUM_GEN_IP_INLINE
+ for (i = 0; i < optlen_aligned/2; i++) {
+ chk_sum += ((u16_t*)p->payload)[i];
+ }
+#endif /* CHECKSUM_GEN_IP_INLINE */
+ }
+#endif /* IP_OPTIONS_SEND */
+ /* generate IP header */
+ if (pbuf_header(p, IP_HLEN)) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_output: not enough room for IP header in pbuf\n"));
+
+ IP_STATS_INC(ip.err);
+ snmp_inc_ipoutdiscards();
+ return ERR_BUF;
+ }
+
+ iphdr = (struct ip_hdr *)p->payload;
+ LWIP_ASSERT("check that first pbuf can hold struct ip_hdr",
+ (p->len >= sizeof(struct ip_hdr)));
+
+ IPH_TTL_SET(iphdr, ttl);
+ IPH_PROTO_SET(iphdr, proto);
+#if CHECKSUM_GEN_IP_INLINE
+ chk_sum += LWIP_MAKE_U16(proto, ttl);
+#endif /* CHECKSUM_GEN_IP_INLINE */
+
+ /* dest cannot be NULL here */
+ ip_addr_copy(iphdr->dest, *dest);
+#if CHECKSUM_GEN_IP_INLINE
+ chk_sum += ip4_addr_get_u32(&iphdr->dest) & 0xFFFF;
+ chk_sum += ip4_addr_get_u32(&iphdr->dest) >> 16;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+
+ IPH_VHLTOS_SET(iphdr, 4, ip_hlen / 4, tos);
+#if CHECKSUM_GEN_IP_INLINE
+ chk_sum += iphdr->_v_hl_tos;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+ IPH_LEN_SET(iphdr, htons(p->tot_len));
+#if CHECKSUM_GEN_IP_INLINE
+ chk_sum += iphdr->_len;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+ IPH_OFFSET_SET(iphdr, 0);
+ IPH_ID_SET(iphdr, htons(ip_id));
+#if CHECKSUM_GEN_IP_INLINE
+ chk_sum += iphdr->_id;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+ ++ip_id;
+
+ if (ip_addr_isany(src)) {
+ ip_addr_copy(iphdr->src, netif->ip_addr);
+ } else {
+ /* src cannot be NULL here */
+ ip_addr_copy(iphdr->src, *src);
+ }
+
+#if CHECKSUM_GEN_IP_INLINE
+ chk_sum += ip4_addr_get_u32(&iphdr->src) & 0xFFFF;
+ chk_sum += ip4_addr_get_u32(&iphdr->src) >> 16;
+ chk_sum = (chk_sum >> 16) + (chk_sum & 0xFFFF);
+ chk_sum = (chk_sum >> 16) + chk_sum;
+ chk_sum = ~chk_sum;
+ iphdr->_chksum = chk_sum; /* network order */
+#else /* CHECKSUM_GEN_IP_INLINE */
+ IPH_CHKSUM_SET(iphdr, 0);
+#if CHECKSUM_GEN_IP
+ IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, ip_hlen));
+#endif
+#endif /* CHECKSUM_GEN_IP_INLINE */
+ } else {
+ /* IP header already included in p */
+ iphdr = (struct ip_hdr *)p->payload;
+ ip_addr_copy(dest_addr, iphdr->dest);
+ dest = &dest_addr;
+ }
+
+ IP_STATS_INC(ip.xmit);
+
+ LWIP_DEBUGF(IP_DEBUG, ("ip_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], netif->num));
+ ip_debug_print(p);
+
+#if ENABLE_LOOPBACK
+ if (ip_addr_cmp(dest, &netif->ip_addr)) {
+ /* Packet to self, enqueue it for loopback */
+ LWIP_DEBUGF(IP_DEBUG, ("netif_loop_output()"));
+ return netif_loop_output(netif, p, dest);
+ }
+#if LWIP_IGMP
+ if ((p->flags & PBUF_FLAG_MCASTLOOP) != 0) {
+ netif_loop_output(netif, p, dest);
+ }
+#endif /* LWIP_IGMP */
+#endif /* ENABLE_LOOPBACK */
+#if IP_FRAG
+ /* don't fragment if interface has mtu set to 0 [loopif] */
+ if (netif->mtu && (p->tot_len > netif->mtu)) {
+ return ip_frag(p, netif, dest);
+ }
+#endif /* IP_FRAG */
+
+ LWIP_DEBUGF(IP_DEBUG, ("netif->output()\n"));
+ return netif->output(netif, p, dest);
+}
+
+/**
+ * Simple interface to ip_output_if. It finds the outgoing network
+ * interface and calls upon ip_output_if to do the actual work.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+ protocol header; if dest == IP_HDRINCL, p already includes an IP
+ header and p->payload points to that IP header)
+ * @param src the source IP address to send from (if src == IP_ADDR_ANY, the
+ * IP address of the netif used to send is used as source address)
+ * @param dest the destination IP address to send the packet to
+ * @param ttl the TTL value to be set in the IP header
+ * @param tos the TOS value to be set in the IP header
+ * @param proto the PROTOCOL to be set in the IP header
+ *
+ * @return ERR_RTE if no route is found
+ * see ip_output_if() for more return values
+ */
+err_t
+ip_output(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
+ u8_t ttl, u8_t tos, u8_t proto)
+{
+ struct netif *netif;
+
+ /* pbufs passed to IP must have a ref-count of 1 as their payload pointer
+ gets altered as the packet is passed down the stack */
+ LWIP_ASSERT("p->ref == 1", p->ref == 1);
+
+ if ((netif = ip_route(dest)) == NULL) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip_output: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest)));
+ IP_STATS_INC(ip.rterr);
+ return ERR_RTE;
+ }
+
+ return ip_output_if(p, src, dest, ttl, tos, proto, netif);
+}
+
+#if LWIP_NETIF_HWADDRHINT
+/** Like ip_output, but takes and addr_hint pointer that is passed on to netif->addr_hint
+ * before calling ip_output_if.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+ protocol header; if dest == IP_HDRINCL, p already includes an IP
+ header and p->payload points to that IP header)
+ * @param src the source IP address to send from (if src == IP_ADDR_ANY, the
+ * IP address of the netif used to send is used as source address)
+ * @param dest the destination IP address to send the packet to
+ * @param ttl the TTL value to be set in the IP header
+ * @param tos the TOS value to be set in the IP header
+ * @param proto the PROTOCOL to be set in the IP header
+ * @param addr_hint address hint pointer set to netif->addr_hint before
+ * calling ip_output_if()
+ *
+ * @return ERR_RTE if no route is found
+ * see ip_output_if() for more return values
+ */
+err_t
+ip_output_hinted(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
+ u8_t ttl, u8_t tos, u8_t proto, u8_t *addr_hint)
+{
+ struct netif *netif;
+ err_t err;
+
+ /* pbufs passed to IP must have a ref-count of 1 as their payload pointer
+ gets altered as the packet is passed down the stack */
+ LWIP_ASSERT("p->ref == 1", p->ref == 1);
+
+ if ((netif = ip_route(dest)) == NULL) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip_output: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest)));
+ IP_STATS_INC(ip.rterr);
+ return ERR_RTE;
+ }
+
+ netif->addr_hint = addr_hint;
+ err = ip_output_if(p, src, dest, ttl, tos, proto, netif);
+ netif->addr_hint = NULL;
+
+ return err;
+}
+#endif /* LWIP_NETIF_HWADDRHINT*/
+
+#if IP_DEBUG
+/* Print an IP header by using LWIP_DEBUGF
+ * @param p an IP packet, p->payload pointing to the IP header
+ */
+void
+ip_debug_print(struct pbuf *p)
+{
+ struct ip_hdr *iphdr = (struct ip_hdr *)p->payload;
+ u8_t *payload;
+
+ payload = (u8_t *)iphdr + IP_HLEN;
+
+ LWIP_DEBUGF(IP_DEBUG, ("IP header:\n"));
+ LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP_DEBUG, ("|%2"S16_F" |%2"S16_F" | 0x%02"X16_F" | %5"U16_F" | (v, hl, tos, len)\n",
+ IPH_V(iphdr),
+ IPH_HL(iphdr),
+ IPH_TOS(iphdr),
+ ntohs(IPH_LEN(iphdr))));
+ LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP_DEBUG, ("| %5"U16_F" |%"U16_F"%"U16_F"%"U16_F"| %4"U16_F" | (id, flags, offset)\n",
+ ntohs(IPH_ID(iphdr)),
+ ntohs(IPH_OFFSET(iphdr)) >> 15 & 1,
+ ntohs(IPH_OFFSET(iphdr)) >> 14 & 1,
+ ntohs(IPH_OFFSET(iphdr)) >> 13 & 1,
+ ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK));
+ LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | 0x%04"X16_F" | (ttl, proto, chksum)\n",
+ IPH_TTL(iphdr),
+ IPH_PROTO(iphdr),
+ ntohs(IPH_CHKSUM(iphdr))));
+ LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | %3"U16_F" | %3"U16_F" | (src)\n",
+ ip4_addr1_16(&iphdr->src),
+ ip4_addr2_16(&iphdr->src),
+ ip4_addr3_16(&iphdr->src),
+ ip4_addr4_16(&iphdr->src)));
+ LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | %3"U16_F" | %3"U16_F" | (dest)\n",
+ ip4_addr1_16(&iphdr->dest),
+ ip4_addr2_16(&iphdr->dest),
+ ip4_addr3_16(&iphdr->dest),
+ ip4_addr4_16(&iphdr->dest)));
+ LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+}
+#endif /* IP_DEBUG */
diff --git a/app/lwip/core/ipv4/ip_addr.c b/app/lwip/core/ipv4/ip_addr.c
new file mode 100644
index 00000000..b39ed7c8
--- /dev/null
+++ b/app/lwip/core/ipv4/ip_addr.c
@@ -0,0 +1,312 @@
+/**
+ * @file
+ * This is the IPv4 address tools implementation.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ *
+ */
+
+#include "lwip/opt.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+
+/* used by IP_ADDR_ANY and IP_ADDR_BROADCAST in ip_addr.h */
+const ip_addr_t ip_addr_any = { IPADDR_ANY };
+const ip_addr_t ip_addr_broadcast = { IPADDR_BROADCAST };
+
+/**
+ * Determine if an address is a broadcast address on a network interface
+ *
+ * @param addr address to be checked
+ * @param netif the network interface against which the address is checked
+ * @return returns non-zero if the address is a broadcast address
+ */
+u8_t
+ip4_addr_isbroadcast(u32_t addr, const struct netif *netif)
+{
+ ip_addr_t ipaddr;
+ ip4_addr_set_u32(&ipaddr, addr);
+
+ /* all ones (broadcast) or all zeroes (old skool broadcast) */
+ if ((~addr == IPADDR_ANY) ||
+ (addr == IPADDR_ANY)) {
+ return 1;
+ /* no broadcast support on this network interface? */
+ } else if ((netif->flags & NETIF_FLAG_BROADCAST) == 0) {
+ /* the given address cannot be a broadcast address
+ * nor can we check against any broadcast addresses */
+ return 0;
+ /* address matches network interface address exactly? => no broadcast */
+ } else if (addr == ip4_addr_get_u32(&netif->ip_addr)) {
+ return 0;
+ /* on the same (sub) network... */
+ } else if (ip_addr_netcmp(&ipaddr, &(netif->ip_addr), &(netif->netmask))
+ /* ...and host identifier bits are all ones? =>... */
+ && ((addr & ~ip4_addr_get_u32(&netif->netmask)) ==
+ (IPADDR_BROADCAST & ~ip4_addr_get_u32(&netif->netmask)))) {
+ /* => network broadcast address */
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/** Checks if a netmask is valid (starting with ones, then only zeros)
+ *
+ * @param netmask the IPv4 netmask to check (in network byte order!)
+ * @return 1 if the netmask is valid, 0 if it is not
+ */
+u8_t
+ip4_addr_netmask_valid(u32_t netmask)
+{
+ u32_t mask;
+ u32_t nm_hostorder = lwip_htonl(netmask);
+
+ /* first, check for the first zero */
+ for (mask = 1U << 31 ; mask != 0; mask >>= 1) {
+ if ((nm_hostorder & mask) == 0) {
+ break;
+ }
+ }
+ /* then check that there is no one */
+ for (; mask != 0; mask >>= 1) {
+ if ((nm_hostorder & mask) != 0) {
+ /* there is a one after the first zero -> invalid */
+ return 0;
+ }
+ }
+ /* no one after the first zero -> valid */
+ return 1;
+}
+
+/* Here for now until needed in other places in lwIP */
+#ifndef isprint
+#define in_range(c, lo, up) ((u8_t)c >= lo && (u8_t)c <= up)
+#define isprint(c) in_range(c, 0x20, 0x7f)
+//#define isdigit(c) in_range(c, '0', '9')
+//#define isxdigit(c) (isdigit(c) || in_range(c, 'a', 'f') || in_range(c, 'A', 'F'))
+#define islower(c) in_range(c, 'a', 'z')
+#define isspace(c) (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v')
+#endif
+
+/**
+ * Ascii internet address interpretation routine.
+ * The value returned is in network order.
+ *
+ * @param cp IP address in ascii represenation (e.g. "127.0.0.1")
+ * @return ip address in network order
+ */
+u32_t
+ipaddr_addr(const char *cp)
+{
+ ip_addr_t val;
+
+ if (ipaddr_aton(cp, &val)) {
+ return ip4_addr_get_u32(&val);
+ }
+ return (IPADDR_NONE);
+}
+
+/**
+ * Check whether "cp" is a valid ascii representation
+ * of an Internet address and convert to a binary address.
+ * Returns 1 if the address is valid, 0 if not.
+ * This replaces inet_addr, the return value from which
+ * cannot distinguish between failure and a local broadcast address.
+ *
+ * @param cp IP address in ascii represenation (e.g. "127.0.0.1")
+ * @param addr pointer to which to save the ip address in network order
+ * @return 1 if cp could be converted to addr, 0 on failure
+ */
+int
+ipaddr_aton(const char *cp, ip_addr_t *addr)
+{
+ u32_t val;
+ u8_t base;
+ char c;
+ u32_t parts[4];
+ u32_t *pp = parts;
+
+ c = *cp;
+ for (;;) {
+ /*
+ * Collect number up to ``.''.
+ * Values are specified as for C:
+ * 0x=hex, 0=octal, 1-9=decimal.
+ */
+ if (!isdigit(c))
+ return (0);
+ val = 0;
+ base = 10;
+ if (c == '0') {
+ c = *++cp;
+ if (c == 'x' || c == 'X') {
+ base = 16;
+ c = *++cp;
+ } else
+ base = 8;
+ }
+ for (;;) {
+ if (isdigit(c)) {
+ val = (val * base) + (int)(c - '0');
+ c = *++cp;
+ } else if (base == 16 && isxdigit(c)) {
+ val = (val << 4) | (int)(c + 10 - (islower(c) ? 'a' : 'A'));
+ c = *++cp;
+ } else
+ break;
+ }
+ if (c == '.') {
+ /*
+ * Internet format:
+ * a.b.c.d
+ * a.b.c (with c treated as 16 bits)
+ * a.b (with b treated as 24 bits)
+ */
+ if (pp >= parts + 3) {
+ return (0);
+ }
+ *pp++ = val;
+ c = *++cp;
+ } else
+ break;
+ }
+ /*
+ * Check for trailing characters.
+ */
+ if (c != '\0' && !isspace(c)) {
+ return (0);
+ }
+ /*
+ * Concoct the address according to
+ * the number of parts specified.
+ */
+ switch (pp - parts + 1) {
+
+ case 0:
+ return (0); /* initial nondigit */
+
+ case 1: /* a -- 32 bits */
+ break;
+
+ case 2: /* a.b -- 8.24 bits */
+ if (val > 0xffffffUL) {
+ return (0);
+ }
+ val |= parts[0] << 24;
+ break;
+
+ case 3: /* a.b.c -- 8.8.16 bits */
+ if (val > 0xffff) {
+ return (0);
+ }
+ val |= (parts[0] << 24) | (parts[1] << 16);
+ break;
+
+ case 4: /* a.b.c.d -- 8.8.8.8 bits */
+ if (val > 0xff) {
+ return (0);
+ }
+ val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
+ break;
+ default:
+ LWIP_ASSERT("unhandled", 0);
+ break;
+ }
+ if (addr) {
+ ip4_addr_set_u32(addr, htonl(val));
+ }
+ return (1);
+}
+
+/**
+ * Convert numeric IP address into decimal dotted ASCII representation.
+ * returns ptr to static buffer; not reentrant!
+ *
+ * @param addr ip address in network order to convert
+ * @return pointer to a global static (!) buffer that holds the ASCII
+ * represenation of addr
+ */
+char *
+ipaddr_ntoa(const ip_addr_t *addr)
+{
+ static char str[16];
+ return ipaddr_ntoa_r(addr, str, 16);
+}
+
+/**
+ * Same as ipaddr_ntoa, but reentrant since a user-supplied buffer is used.
+ *
+ * @param addr ip address in network order to convert
+ * @param buf target buffer where the string is stored
+ * @param buflen length of buf
+ * @return either pointer to buf which now holds the ASCII
+ * representation of addr or NULL if buf was too small
+ */
+char *ipaddr_ntoa_r(const ip_addr_t *addr, char *buf, int buflen)
+{
+ u32_t s_addr;
+ char inv[3];
+ char *rp;
+ u8_t *ap;
+ u8_t rem;
+ u8_t n;
+ u8_t i;
+ int len = 0;
+
+ s_addr = ip4_addr_get_u32(addr);
+
+ rp = buf;
+ ap = (u8_t *)&s_addr;
+ for(n = 0; n < 4; n++) {
+ i = 0;
+ do {
+ rem = *ap % (u8_t)10;
+ *ap /= (u8_t)10;
+ inv[i++] = '0' + rem;
+ } while(*ap);
+ while(i--) {
+ if (len++ >= buflen) {
+ return NULL;
+ }
+ *rp++ = inv[i];
+ }
+ if (len++ >= buflen) {
+ return NULL;
+ }
+ *rp++ = '.';
+ ap++;
+ }
+ *--rp = 0;
+ return buf;
+}
diff --git a/app/lwip/core/ipv4/ip_frag.c b/app/lwip/core/ipv4/ip_frag.c
new file mode 100644
index 00000000..5eff96f2
--- /dev/null
+++ b/app/lwip/core/ipv4/ip_frag.c
@@ -0,0 +1,863 @@
+/**
+ * @file
+ * This is the IPv4 packet segmentation and reassembly implementation.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Jani Monoses
+ * Simon Goldschmidt
+ * original reassembly code by Adam Dunkels
+ *
+ */
+
+#include "lwip/opt.h"
+#include "lwip/ip_frag.h"
+#include "lwip/def.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/netif.h"
+#include "lwip/snmp.h"
+#include "lwip/stats.h"
+#include "lwip/icmp.h"
+
+#include
+
+#if IP_REASSEMBLY
+/**
+ * The IP reassembly code currently has the following limitations:
+ * - IP header options are not supported
+ * - fragments must not overlap (e.g. due to different routes),
+ * currently, overlapping or duplicate fragments are thrown away
+ * if IP_REASS_CHECK_OVERLAP=1 (the default)!
+ *
+ * @todo: work with IP header options
+ */
+
+/** Setting this to 0, you can turn off checking the fragments for overlapping
+ * regions. The code gets a little smaller. Only use this if you know that
+ * overlapping won't occur on your network! */
+#ifndef IP_REASS_CHECK_OVERLAP
+#define IP_REASS_CHECK_OVERLAP 1
+#endif /* IP_REASS_CHECK_OVERLAP */
+
+/** Set to 0 to prevent freeing the oldest datagram when the reassembly buffer is
+ * full (IP_REASS_MAX_PBUFS pbufs are enqueued). The code gets a little smaller.
+ * Datagrams will be freed by timeout only. Especially useful when MEMP_NUM_REASSDATA
+ * is set to 1, so one datagram can be reassembled at a time, only. */
+#ifndef IP_REASS_FREE_OLDEST
+#define IP_REASS_FREE_OLDEST 1
+#endif /* IP_REASS_FREE_OLDEST */
+
+#define IP_REASS_FLAG_LASTFRAG 0x01
+
+/** This is a helper struct which holds the starting
+ * offset and the ending offset of this fragment to
+ * easily chain the fragments.
+ * It has the same packing requirements as the IP header, since it replaces
+ * the IP header in memory in incoming fragments (after copying it) to keep
+ * track of the various fragments. (-> If the IP header doesn't need packing,
+ * this struct doesn't need packing, too.)
+ */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip_reass_helper {
+ PACK_STRUCT_FIELD(struct pbuf *next_pbuf);
+ PACK_STRUCT_FIELD(u16_t start);
+ PACK_STRUCT_FIELD(u16_t end);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+#define IP_ADDRESSES_AND_ID_MATCH(iphdrA, iphdrB) \
+ (ip_addr_cmp(&(iphdrA)->src, &(iphdrB)->src) && \
+ ip_addr_cmp(&(iphdrA)->dest, &(iphdrB)->dest) && \
+ IPH_ID(iphdrA) == IPH_ID(iphdrB)) ? 1 : 0
+
+/* global variables */
+static struct ip_reassdata *reassdatagrams;
+static u16_t ip_reass_pbufcount;
+
+/* function prototypes */
+static void ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)ICACHE_FLASH_ATTR;
+static int ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)ICACHE_FLASH_ATTR;
+
+/**
+ * Reassembly timer base function
+ * for both NO_SYS == 0 and 1 (!).
+ *
+ * Should be called every 1000 msec (defined by IP_TMR_INTERVAL).
+ */
+void
+ip_reass_tmr(void)
+{
+ struct ip_reassdata *r, *prev = NULL;
+
+ r = reassdatagrams;
+ while (r != NULL) {
+ /* Decrement the timer. Once it reaches 0,
+ * clean up the incomplete fragment assembly */
+ if (r->timer > 0) {
+ r->timer--;
+ LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer dec %"U16_F"\n",(u16_t)r->timer));
+ prev = r;
+ r = r->next;
+ } else {
+ /* reassembly timed out */
+ struct ip_reassdata *tmp;
+ LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer timed out\n"));
+ tmp = r;
+ /* get the next pointer before freeing */
+ r = r->next;
+ /* free the helper struct and all enqueued pbufs */
+ ip_reass_free_complete_datagram(tmp, prev);
+ }
+ }
+}
+
+/**
+ * Free a datagram (struct ip_reassdata) and all its pbufs.
+ * Updates the total count of enqueued pbufs (ip_reass_pbufcount),
+ * SNMP counters and sends an ICMP time exceeded packet.
+ *
+ * @param ipr datagram to free
+ * @param prev the previous datagram in the linked list
+ * @return the number of pbufs freed
+ */
+static int
+ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
+{
+ u16_t pbufs_freed = 0;
+ u8_t clen;
+ struct pbuf *p;
+ struct ip_reass_helper *iprh;
+
+ LWIP_ASSERT("prev != ipr", prev != ipr);
+ if (prev != NULL) {
+ LWIP_ASSERT("prev->next == ipr", prev->next == ipr);
+ }
+
+ snmp_inc_ipreasmfails();
+#if LWIP_ICMP
+ iprh = (struct ip_reass_helper *)ipr->p->payload;
+ if (iprh->start == 0) {
+ /* The first fragment was received, send ICMP time exceeded. */
+ /* First, de-queue the first pbuf from r->p. */
+ p = ipr->p;
+ ipr->p = iprh->next_pbuf;
+ /* Then, copy the original header into it. */
+ SMEMCPY(p->payload, &ipr->iphdr, IP_HLEN);
+ icmp_time_exceeded(p, ICMP_TE_FRAG);
+ clen = pbuf_clen(p);
+ LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
+ pbufs_freed += clen;
+ pbuf_free(p);
+ }
+#endif /* LWIP_ICMP */
+
+ /* First, free all received pbufs. The individual pbufs need to be released
+ separately as they have not yet been chained */
+ p = ipr->p;
+ while (p != NULL) {
+ struct pbuf *pcur;
+ iprh = (struct ip_reass_helper *)p->payload;
+ pcur = p;
+ /* get the next pointer before freeing */
+ p = iprh->next_pbuf;
+ clen = pbuf_clen(pcur);
+ LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
+ pbufs_freed += clen;
+ pbuf_free(pcur);
+ }
+ /* Then, unchain the struct ip_reassdata from the list and free it. */
+ ip_reass_dequeue_datagram(ipr, prev);
+ LWIP_ASSERT("ip_reass_pbufcount >= clen", ip_reass_pbufcount >= pbufs_freed);
+ ip_reass_pbufcount -= pbufs_freed;
+
+ return pbufs_freed;
+}
+
+#if IP_REASS_FREE_OLDEST
+/**
+ * Free the oldest datagram to make room for enqueueing new fragments.
+ * The datagram 'fraghdr' belongs to is not freed!
+ *
+ * @param fraghdr IP header of the current fragment
+ * @param pbufs_needed number of pbufs needed to enqueue
+ * (used for freeing other datagrams if not enough space)
+ * @return the number of pbufs freed
+ */
+static int
+ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed)
+{
+ /* @todo Can't we simply remove the last datagram in the
+ * linked list behind reassdatagrams?
+ */
+ struct ip_reassdata *r, *oldest, *prev;
+ int pbufs_freed = 0, pbufs_freed_current;
+ int other_datagrams;
+
+ /* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs,
+ * but don't free the datagram that 'fraghdr' belongs to! */
+ do {
+ oldest = NULL;
+ prev = NULL;
+ other_datagrams = 0;
+ r = reassdatagrams;
+ while (r != NULL) {
+ if (!IP_ADDRESSES_AND_ID_MATCH(&r->iphdr, fraghdr)) {
+ /* Not the same datagram as fraghdr */
+ other_datagrams++;
+ if (oldest == NULL) {
+ oldest = r;
+ } else if (r->timer <= oldest->timer) {
+ /* older than the previous oldest */
+ oldest = r;
+ }
+ }
+ if (r->next != NULL) {
+ prev = r;
+ }
+ r = r->next;
+ }
+ if (oldest != NULL) {
+ pbufs_freed_current = ip_reass_free_complete_datagram(oldest, prev);
+ pbufs_freed += pbufs_freed_current;
+ }
+ } while ((pbufs_freed < pbufs_needed) && (other_datagrams > 1));
+ return pbufs_freed;
+}
+#endif /* IP_REASS_FREE_OLDEST */
+
+/**
+ * Enqueues a new fragment into the fragment queue
+ * @param fraghdr points to the new fragments IP hdr
+ * @param clen number of pbufs needed to enqueue (used for freeing other datagrams if not enough space)
+ * @return A pointer to the queue location into which the fragment was enqueued
+ */
+static struct ip_reassdata* ICACHE_FLASH_ATTR
+ip_reass_enqueue_new_datagram(struct ip_hdr *fraghdr, int clen)
+{
+ struct ip_reassdata* ipr;
+ /* No matching previous fragment found, allocate a new reassdata struct */
+ ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA);
+ if (ipr == NULL) {
+#if IP_REASS_FREE_OLDEST
+ if (ip_reass_remove_oldest_datagram(fraghdr, clen) >= clen) {
+ ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA);
+ }
+ if (ipr == NULL)
+#endif /* IP_REASS_FREE_OLDEST */
+ {
+ IPFRAG_STATS_INC(ip_frag.memerr);
+ LWIP_DEBUGF(IP_REASS_DEBUG,("Failed to alloc reassdata struct\n"));
+ return NULL;
+ }
+ }
+ memset(ipr, 0, sizeof(struct ip_reassdata));
+ ipr->timer = IP_REASS_MAXAGE;
+
+ /* enqueue the new structure to the front of the list */
+ ipr->next = reassdatagrams;
+ reassdatagrams = ipr;
+ /* copy the ip header for later tests and input */
+ /* @todo: no ip options supported? */
+ SMEMCPY(&(ipr->iphdr), fraghdr, IP_HLEN);
+ return ipr;
+}
+
+/**
+ * Dequeues a datagram from the datagram queue. Doesn't deallocate the pbufs.
+ * @param ipr points to the queue entry to dequeue
+ */
+static void ICACHE_FLASH_ATTR
+ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
+{
+
+ /* dequeue the reass struct */
+ if (reassdatagrams == ipr) {
+ /* it was the first in the list */
+ reassdatagrams = ipr->next;
+ } else {
+ /* it wasn't the first, so it must have a valid 'prev' */
+ LWIP_ASSERT("sanity check linked list", prev != NULL);
+ prev->next = ipr->next;
+ }
+
+ /* now we can free the ip_reass struct */
+ memp_free(MEMP_REASSDATA, ipr);
+}
+
+/**
+ * Chain a new pbuf into the pbuf list that composes the datagram. The pbuf list
+ * will grow over time as new pbufs are rx.
+ * Also checks that the datagram passes basic continuity checks (if the last
+ * fragment was received at least once).
+ * @param root_p points to the 'root' pbuf for the current datagram being assembled.
+ * @param new_p points to the pbuf for the current fragment
+ * @return 0 if invalid, >0 otherwise
+ */
+static int ICACHE_FLASH_ATTR
+ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct pbuf *new_p)
+{
+ struct ip_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL;
+ struct pbuf *q;
+ u16_t offset,len;
+ struct ip_hdr *fraghdr;
+ int valid = 1;
+
+ /* Extract length and fragment offset from current fragment */
+ fraghdr = (struct ip_hdr*)new_p->payload;
+ len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
+ offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
+
+ /* overwrite the fragment's ip header from the pbuf with our helper struct,
+ * and setup the embedded helper structure. */
+ /* make sure the struct ip_reass_helper fits into the IP header */
+ LWIP_ASSERT("sizeof(struct ip_reass_helper) <= IP_HLEN",
+ sizeof(struct ip_reass_helper) <= IP_HLEN);
+ iprh = (struct ip_reass_helper*)new_p->payload;
+ iprh->next_pbuf = NULL;
+ iprh->start = offset;
+ iprh->end = offset + len;
+
+ /* Iterate through until we either get to the end of the list (append),
+ * or we find on with a larger offset (insert). */
+ for (q = ipr->p; q != NULL;) {
+ iprh_tmp = (struct ip_reass_helper*)q->payload;
+ if (iprh->start < iprh_tmp->start) {
+ /* the new pbuf should be inserted before this */
+ iprh->next_pbuf = q;
+ if (iprh_prev != NULL) {
+ /* not the fragment with the lowest offset */
+#if IP_REASS_CHECK_OVERLAP
+ if ((iprh->start < iprh_prev->end) || (iprh->end > iprh_tmp->start)) {
+ /* fragment overlaps with previous or following, throw away */
+ goto freepbuf;
+ }
+#endif /* IP_REASS_CHECK_OVERLAP */
+ iprh_prev->next_pbuf = new_p;
+ } else {
+ /* fragment with the lowest offset */
+ ipr->p = new_p;
+ }
+ break;
+ } else if(iprh->start == iprh_tmp->start) {
+ /* received the same datagram twice: no need to keep the datagram */
+ goto freepbuf;
+#if IP_REASS_CHECK_OVERLAP
+ } else if(iprh->start < iprh_tmp->end) {
+ /* overlap: no need to keep the new datagram */
+ goto freepbuf;
+#endif /* IP_REASS_CHECK_OVERLAP */
+ } else {
+ /* Check if the fragments received so far have no wholes. */
+ if (iprh_prev != NULL) {
+ if (iprh_prev->end != iprh_tmp->start) {
+ /* There is a fragment missing between the current
+ * and the previous fragment */
+ valid = 0;
+ }
+ }
+ }
+ q = iprh_tmp->next_pbuf;
+ iprh_prev = iprh_tmp;
+ }
+
+ /* If q is NULL, then we made it to the end of the list. Determine what to do now */
+ if (q == NULL) {
+ if (iprh_prev != NULL) {
+ /* this is (for now), the fragment with the highest offset:
+ * chain it to the last fragment */
+#if IP_REASS_CHECK_OVERLAP
+ LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start);
+#endif /* IP_REASS_CHECK_OVERLAP */
+ iprh_prev->next_pbuf = new_p;
+ if (iprh_prev->end != iprh->start) {
+ valid = 0;
+ }
+ } else {
+#if IP_REASS_CHECK_OVERLAP
+ LWIP_ASSERT("no previous fragment, this must be the first fragment!",
+ ipr->p == NULL);
+#endif /* IP_REASS_CHECK_OVERLAP */
+ /* this is the first fragment we ever received for this ip datagram */
+ ipr->p = new_p;
+ }
+ }
+
+ /* At this point, the validation part begins: */
+ /* If we already received the last fragment */
+ if ((ipr->flags & IP_REASS_FLAG_LASTFRAG) != 0) {
+ /* and had no wholes so far */
+ if (valid) {
+ /* then check if the rest of the fragments is here */
+ /* Check if the queue starts with the first datagram */
+ if (((struct ip_reass_helper*)ipr->p->payload)->start != 0) {
+ valid = 0;
+ } else {
+ /* and check that there are no wholes after this datagram */
+ iprh_prev = iprh;
+ q = iprh->next_pbuf;
+ while (q != NULL) {
+ iprh = (struct ip_reass_helper*)q->payload;
+ if (iprh_prev->end != iprh->start) {
+ valid = 0;
+ break;
+ }
+ iprh_prev = iprh;
+ q = iprh->next_pbuf;
+ }
+ /* if still valid, all fragments are received
+ * (because to the MF==0 already arrived */
+ if (valid) {
+ LWIP_ASSERT("sanity check", ipr->p != NULL);
+ LWIP_ASSERT("sanity check",
+ ((struct ip_reass_helper*)ipr->p->payload) != iprh);
+ LWIP_ASSERT("validate_datagram:next_pbuf!=NULL",
+ iprh->next_pbuf == NULL);
+ LWIP_ASSERT("validate_datagram:datagram end!=datagram len",
+ iprh->end == ipr->datagram_len);
+ }
+ }
+ }
+ /* If valid is 0 here, there are some fragments missing in the middle
+ * (since MF == 0 has already arrived). Such datagrams simply time out if
+ * no more fragments are received... */
+ return valid;
+ }
+ /* If we come here, not all fragments were received, yet! */
+ return 0; /* not yet valid! */
+#if IP_REASS_CHECK_OVERLAP
+freepbuf:
+ ip_reass_pbufcount -= pbuf_clen(new_p);
+ pbuf_free(new_p);
+ return 0;
+#endif /* IP_REASS_CHECK_OVERLAP */
+}
+
+/**
+ * Reassembles incoming IP fragments into an IP datagram.
+ *
+ * @param p points to a pbuf chain of the fragment
+ * @return NULL if reassembly is incomplete, ? otherwise
+ */
+struct pbuf *
+ip_reass(struct pbuf *p)
+{
+ struct pbuf *r;
+ struct ip_hdr *fraghdr;
+ struct ip_reassdata *ipr;
+ struct ip_reass_helper *iprh;
+ u16_t offset, len;
+ u8_t clen;
+ struct ip_reassdata *ipr_prev = NULL;
+
+ IPFRAG_STATS_INC(ip_frag.recv);
+ snmp_inc_ipreasmreqds();
+
+ fraghdr = (struct ip_hdr*)p->payload;
+
+ if ((IPH_HL(fraghdr) * 4) != IP_HLEN) {
+ LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: IP options currently not supported!\n"));
+ IPFRAG_STATS_INC(ip_frag.err);
+ goto nullreturn;
+ }
+
+ offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
+ len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
+
+ /* Check if we are allowed to enqueue more datagrams. */
+ clen = pbuf_clen(p);
+ if ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) {
+#if IP_REASS_FREE_OLDEST
+ if (!ip_reass_remove_oldest_datagram(fraghdr, clen) ||
+ ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS))
+#endif /* IP_REASS_FREE_OLDEST */
+ {
+ /* No datagram could be freed and still too many pbufs enqueued */
+ LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: Overflow condition: pbufct=%d, clen=%d, MAX=%d\n",
+ ip_reass_pbufcount, clen, IP_REASS_MAX_PBUFS));
+ IPFRAG_STATS_INC(ip_frag.memerr);
+ /* @todo: send ICMP time exceeded here? */
+ /* drop this pbuf */
+ goto nullreturn;
+ }
+ }
+
+ /* Look for the datagram the fragment belongs to in the current datagram queue,
+ * remembering the previous in the queue for later dequeueing. */
+ for (ipr = reassdatagrams; ipr != NULL; ipr = ipr->next) {
+ /* Check if the incoming fragment matches the one currently present
+ in the reassembly buffer. If so, we proceed with copying the
+ fragment into the buffer. */
+ if (IP_ADDRESSES_AND_ID_MATCH(&ipr->iphdr, fraghdr)) {
+ LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching previous fragment ID=%"X16_F"\n",
+ ntohs(IPH_ID(fraghdr))));
+ IPFRAG_STATS_INC(ip_frag.cachehit);
+ break;
+ }
+ ipr_prev = ipr;
+ }
+
+ if (ipr == NULL) {
+ /* Enqueue a new datagram into the datagram queue */
+ ipr = ip_reass_enqueue_new_datagram(fraghdr, clen);
+ /* Bail if unable to enqueue */
+ if(ipr == NULL) {
+ goto nullreturn;
+ }
+ } else {
+ if (((ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) == 0) &&
+ ((ntohs(IPH_OFFSET(&ipr->iphdr)) & IP_OFFMASK) != 0)) {
+ /* ipr->iphdr is not the header from the first fragment, but fraghdr is
+ * -> copy fraghdr into ipr->iphdr since we want to have the header
+ * of the first fragment (for ICMP time exceeded and later, for copying
+ * all options, if supported)*/
+ SMEMCPY(&ipr->iphdr, fraghdr, IP_HLEN);
+ }
+ }
+ /* Track the current number of pbufs current 'in-flight', in order to limit
+ the number of fragments that may be enqueued at any one time */
+ ip_reass_pbufcount += clen;
+
+ /* At this point, we have either created a new entry or pointing
+ * to an existing one */
+
+ /* check for 'no more fragments', and update queue entry*/
+ if ((IPH_OFFSET(fraghdr) & PP_NTOHS(IP_MF)) == 0) {
+ ipr->flags |= IP_REASS_FLAG_LASTFRAG;
+ ipr->datagram_len = offset + len;
+ LWIP_DEBUGF(IP_REASS_DEBUG,
+ ("ip_reass: last fragment seen, total len %"S16_F"\n",
+ ipr->datagram_len));
+ }
+ /* find the right place to insert this pbuf */
+ /* @todo: trim pbufs if fragments are overlapping */
+ if (ip_reass_chain_frag_into_datagram_and_validate(ipr, p)) {
+ /* the totally last fragment (flag more fragments = 0) was received at least
+ * once AND all fragments are received */
+ ipr->datagram_len += IP_HLEN;
+
+ /* save the second pbuf before copying the header over the pointer */
+ r = ((struct ip_reass_helper*)ipr->p->payload)->next_pbuf;
+
+ /* copy the original ip header back to the first pbuf */
+ fraghdr = (struct ip_hdr*)(ipr->p->payload);
+ SMEMCPY(fraghdr, &ipr->iphdr, IP_HLEN);
+ IPH_LEN_SET(fraghdr, htons(ipr->datagram_len));
+ IPH_OFFSET_SET(fraghdr, 0);
+ IPH_CHKSUM_SET(fraghdr, 0);
+ /* @todo: do we need to set calculate the correct checksum? */
+ IPH_CHKSUM_SET(fraghdr, inet_chksum(fraghdr, IP_HLEN));
+
+ p = ipr->p;
+
+ /* chain together the pbufs contained within the reass_data list. */
+ while(r != NULL) {
+ iprh = (struct ip_reass_helper*)r->payload;
+
+ /* hide the ip header for every succeding fragment */
+ pbuf_header(r, -IP_HLEN);
+ pbuf_cat(p, r);
+ r = iprh->next_pbuf;
+ }
+ /* release the sources allocate for the fragment queue entry */
+ ip_reass_dequeue_datagram(ipr, ipr_prev);
+
+ /* and adjust the number of pbufs currently queued for reassembly. */
+ ip_reass_pbufcount -= pbuf_clen(p);
+
+ /* Return the pbuf chain */
+ return p;
+ }
+ /* the datagram is not (yet?) reassembled completely */
+ LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass_pbufcount: %d out\n", ip_reass_pbufcount));
+ return NULL;
+
+nullreturn:
+ LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: nullreturn\n"));
+ IPFRAG_STATS_INC(ip_frag.drop);
+ pbuf_free(p);
+ return NULL;
+}
+#endif /* IP_REASSEMBLY */
+
+#if IP_FRAG
+#if IP_FRAG_USES_STATIC_BUF
+static u8_t buf[LWIP_MEM_ALIGN_SIZE(IP_FRAG_MAX_MTU + MEM_ALIGNMENT - 1)];
+#else /* IP_FRAG_USES_STATIC_BUF */
+
+#if !LWIP_NETIF_TX_SINGLE_PBUF
+/** Allocate a new struct pbuf_custom_ref */
+static struct pbuf_custom_ref* ICACHE_FLASH_ATTR
+ip_frag_alloc_pbuf_custom_ref(void)
+{
+ return (struct pbuf_custom_ref*)memp_malloc(MEMP_FRAG_PBUF);
+}
+
+/** Free a struct pbuf_custom_ref */
+static void ICACHE_FLASH_ATTR
+ip_frag_free_pbuf_custom_ref(struct pbuf_custom_ref* p)
+{
+ LWIP_ASSERT("p != NULL", p != NULL);
+ memp_free(MEMP_FRAG_PBUF, p);
+}
+
+/** Free-callback function to free a 'struct pbuf_custom_ref', called by
+ * pbuf_free. */
+static void ICACHE_FLASH_ATTR
+ipfrag_free_pbuf_custom(struct pbuf *p)
+{
+ struct pbuf_custom_ref *pcr = (struct pbuf_custom_ref*)p;
+ LWIP_ASSERT("pcr != NULL", pcr != NULL);
+ LWIP_ASSERT("pcr == p", (void*)pcr == (void*)p);
+ if (pcr->original != NULL) {
+ pbuf_free(pcr->original);
+ }
+ ip_frag_free_pbuf_custom_ref(pcr);
+}
+#endif /* !LWIP_NETIF_TX_SINGLE_PBUF */
+#endif /* IP_FRAG_USES_STATIC_BUF */
+
+/**
+ * Fragment an IP datagram if too large for the netif.
+ *
+ * Chop the datagram in MTU sized chunks and send them in order
+ * by using a fixed size static memory buffer (PBUF_REF) or
+ * point PBUF_REFs into p (depending on IP_FRAG_USES_STATIC_BUF).
+ *
+ * @param p ip packet to send
+ * @param netif the netif on which to send
+ * @param dest destination ip address to which to send
+ *
+ * @return ERR_OK if sent successfully, err_t otherwise
+ */
+err_t
+ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest)
+{
+ struct pbuf *rambuf;
+#if IP_FRAG_USES_STATIC_BUF
+ struct pbuf *header;
+#else
+#if !LWIP_NETIF_TX_SINGLE_PBUF
+ struct pbuf *newpbuf;
+#endif
+ struct ip_hdr *original_iphdr;
+#endif
+ struct ip_hdr *iphdr;
+ u16_t nfb;
+ u16_t left, cop;
+ u16_t mtu = netif->mtu;
+ u16_t ofo, omf;
+ u16_t last;
+ u16_t poff = IP_HLEN;
+ u16_t tmp;
+#if !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF
+ u16_t newpbuflen = 0;
+ u16_t left_to_copy;
+#endif
+
+ /* Get a RAM based MTU sized pbuf */
+#if IP_FRAG_USES_STATIC_BUF
+ /* When using a static buffer, we use a PBUF_REF, which we will
+ * use to reference the packet (without link header).
+ * Layer and length is irrelevant.
+ */
+ rambuf = pbuf_alloc(PBUF_LINK, 0, PBUF_REF);
+ if (rambuf == NULL) {
+ LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc(PBUF_LINK, 0, PBUF_REF) failed\n"));
+ return ERR_MEM;
+ }
+ rambuf->tot_len = rambuf->len = mtu;
+ rambuf->payload = LWIP_MEM_ALIGN((void *)buf);
+
+ /* Copy the IP header in it */
+ iphdr = (struct ip_hdr *)rambuf->payload;
+ SMEMCPY(iphdr, p->payload, IP_HLEN);
+#else /* IP_FRAG_USES_STATIC_BUF */
+ original_iphdr = (struct ip_hdr *)p->payload;
+ iphdr = original_iphdr;
+#endif /* IP_FRAG_USES_STATIC_BUF */
+
+ /* Save original offset */
+ tmp = ntohs(IPH_OFFSET(iphdr));
+ ofo = tmp & IP_OFFMASK;
+ omf = tmp & IP_MF;
+
+ left = p->tot_len - IP_HLEN;
+
+ nfb = (mtu - IP_HLEN) / 8;
+
+ while (left) {
+ last = (left <= mtu - IP_HLEN);
+
+ /* Set new offset and MF flag */
+ tmp = omf | (IP_OFFMASK & (ofo));
+ if (!last) {
+ tmp = tmp | IP_MF;
+ }
+
+ /* Fill this fragment */
+ cop = last ? left : nfb * 8;
+
+#if IP_FRAG_USES_STATIC_BUF
+ poff += pbuf_copy_partial(p, (u8_t*)iphdr + IP_HLEN, cop, poff);
+#else /* IP_FRAG_USES_STATIC_BUF */
+#if LWIP_NETIF_TX_SINGLE_PBUF
+ rambuf = pbuf_alloc(PBUF_IP, cop, PBUF_RAM);
+ if (rambuf == NULL) {
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("this needs a pbuf in one piece!",
+ (rambuf->len == rambuf->tot_len) && (rambuf->next == NULL));
+ poff += pbuf_copy_partial(p, rambuf->payload, cop, poff);
+ /* make room for the IP header */
+ if(pbuf_header(rambuf, IP_HLEN)) {
+ pbuf_free(rambuf);
+ return ERR_MEM;
+ }
+ /* fill in the IP header */
+ SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN);
+ iphdr = rambuf->payload;
+#else /* LWIP_NETIF_TX_SINGLE_PBUF */
+ /* When not using a static buffer, create a chain of pbufs.
+ * The first will be a PBUF_RAM holding the link and IP header.
+ * The rest will be PBUF_REFs mirroring the pbuf chain to be fragged,
+ * but limited to the size of an mtu.
+ */
+ rambuf = pbuf_alloc(PBUF_LINK, IP_HLEN, PBUF_RAM);
+ if (rambuf == NULL) {
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("this needs a pbuf in one piece!",
+ (p->len >= (IP_HLEN)));
+ SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN);
+ iphdr = (struct ip_hdr *)rambuf->payload;
+
+ /* Can just adjust p directly for needed offset. */
+ p->payload = (u8_t *)p->payload + poff;
+ p->len -= poff;
+
+ left_to_copy = cop;
+ while (left_to_copy) {
+ struct pbuf_custom_ref *pcr;
+ newpbuflen = (left_to_copy < p->len) ? left_to_copy : p->len;
+ /* Is this pbuf already empty? */
+ if (!newpbuflen) {
+ p = p->next;
+ continue;
+ }
+ pcr = ip_frag_alloc_pbuf_custom_ref();
+ if (pcr == NULL) {
+ pbuf_free(rambuf);
+ return ERR_MEM;
+ }
+ /* Mirror this pbuf, although we might not need all of it. */
+ newpbuf = pbuf_alloced_custom(PBUF_RAW, newpbuflen, PBUF_REF, &pcr->pc, p->payload, newpbuflen);
+ if (newpbuf == NULL) {
+ ip_frag_free_pbuf_custom_ref(pcr);
+ pbuf_free(rambuf);
+ return ERR_MEM;
+ }
+ pbuf_ref(p);
+ pcr->original = p;
+ pcr->pc.custom_free_function = ipfrag_free_pbuf_custom;
+
+ /* Add it to end of rambuf's chain, but using pbuf_cat, not pbuf_chain
+ * so that it is removed when pbuf_dechain is later called on rambuf.
+ */
+ pbuf_cat(rambuf, newpbuf);
+ left_to_copy -= newpbuflen;
+ if (left_to_copy) {
+ p = p->next;
+ }
+ }
+ poff = newpbuflen;
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+#endif /* IP_FRAG_USES_STATIC_BUF */
+
+ /* Correct header */
+ IPH_OFFSET_SET(iphdr, htons(tmp));
+ IPH_LEN_SET(iphdr, htons(cop + IP_HLEN));
+ IPH_CHKSUM_SET(iphdr, 0);
+ IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
+
+#if IP_FRAG_USES_STATIC_BUF
+ if (last) {
+ pbuf_realloc(rambuf, left + IP_HLEN);
+ }
+
+ /* This part is ugly: we alloc a RAM based pbuf for
+ * the link level header for each chunk and then
+ * free it.A PBUF_ROM style pbuf for which pbuf_header
+ * worked would make things simpler.
+ */
+ header = pbuf_alloc(PBUF_LINK, 0, PBUF_RAM);
+ if (header != NULL) {
+ pbuf_chain(header, rambuf);
+ netif->output(netif, header, dest);
+ IPFRAG_STATS_INC(ip_frag.xmit);
+ snmp_inc_ipfragcreates();
+ pbuf_free(header);
+ } else {
+ LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc() for header failed\n"));
+ pbuf_free(rambuf);
+ return ERR_MEM;
+ }
+#else /* IP_FRAG_USES_STATIC_BUF */
+ /* No need for separate header pbuf - we allowed room for it in rambuf
+ * when allocated.
+ */
+ netif->output(netif, rambuf, dest);
+ IPFRAG_STATS_INC(ip_frag.xmit);
+
+ /* Unfortunately we can't reuse rambuf - the hardware may still be
+ * using the buffer. Instead we free it (and the ensuing chain) and
+ * recreate it next time round the loop. If we're lucky the hardware
+ * will have already sent the packet, the free will really free, and
+ * there will be zero memory penalty.
+ */
+
+ pbuf_free(rambuf);
+#endif /* IP_FRAG_USES_STATIC_BUF */
+ left -= cop;
+ ofo += nfb;
+ }
+#if IP_FRAG_USES_STATIC_BUF
+ pbuf_free(rambuf);
+#endif /* IP_FRAG_USES_STATIC_BUF */
+ snmp_inc_ipfragoks();
+ return ERR_OK;
+}
+#endif /* IP_FRAG */
diff --git a/app/lwip/core/mem.c b/app/lwip/core/mem.c
new file mode 100644
index 00000000..9a66f295
--- /dev/null
+++ b/app/lwip/core/mem.c
@@ -0,0 +1,644 @@
+/**
+ * @file
+ * Dynamic memory manager
+ *
+ * This is a lightweight replacement for the standard C library malloc().
+ *
+ * If you want to use the standard C library malloc() instead, define
+ * MEM_LIBC_MALLOC to 1 in your lwipopts.h
+ *
+ * To let mem_malloc() use pools (prevents fragmentation and is much faster than
+ * a heap but might waste some memory), define MEM_USE_POOLS to 1, define
+ * MEM_USE_CUSTOM_POOLS to 1 and create a file "lwippools.h" that includes a list
+ * of pools like this (more pools can be added between _START and _END):
+ *
+ * Define three pools with sizes 256, 512, and 1512 bytes
+ * LWIP_MALLOC_MEMPOOL_START
+ * LWIP_MALLOC_MEMPOOL(20, 256)
+ * LWIP_MALLOC_MEMPOOL(10, 512)
+ * LWIP_MALLOC_MEMPOOL(5, 1512)
+ * LWIP_MALLOC_MEMPOOL_END
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ * Simon Goldschmidt
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if !MEM_LIBC_MALLOC /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/sys.h"
+#include "lwip/stats.h"
+#include "lwip/err.h"
+
+#include
+
+#if MEM_USE_POOLS
+/* lwIP head implemented with different sized pools */
+
+/**
+ * Allocate memory: determine the smallest pool that is big enough
+ * to contain an element of 'size' and get an element from that pool.
+ *
+ * @param size the size in bytes of the memory needed
+ * @return a pointer to the allocated memory or NULL if the pool is empty
+ */
+void *
+mem_malloc(mem_size_t size)
+{
+ struct memp_malloc_helper *element;
+ memp_t poolnr;
+ mem_size_t required_size = size + sizeof(struct memp_malloc_helper);
+
+ for (poolnr = MEMP_POOL_FIRST; poolnr <= MEMP_POOL_LAST; poolnr = (memp_t)(poolnr + 1)) {
+#if MEM_USE_POOLS_TRY_BIGGER_POOL
+again:
+#endif /* MEM_USE_POOLS_TRY_BIGGER_POOL */
+ /* is this pool big enough to hold an element of the required size
+ plus a struct memp_malloc_helper that saves the pool this element came from? */
+ if (required_size <= memp_sizes[poolnr]) {
+ break;
+ }
+ }
+ if (poolnr > MEMP_POOL_LAST) {
+ LWIP_ASSERT("mem_malloc(): no pool is that big!", 0);
+ return NULL;
+ }
+ element = (struct memp_malloc_helper*)memp_malloc(poolnr);
+ if (element == NULL) {
+ /* No need to DEBUGF or ASSERT: This error is already
+ taken care of in memp.c */
+#if MEM_USE_POOLS_TRY_BIGGER_POOL
+ /** Try a bigger pool if this one is empty! */
+ if (poolnr < MEMP_POOL_LAST) {
+ poolnr++;
+ goto again;
+ }
+#endif /* MEM_USE_POOLS_TRY_BIGGER_POOL */
+ return NULL;
+ }
+
+ /* save the pool number this element came from */
+ element->poolnr = poolnr;
+ /* and return a pointer to the memory directly after the struct memp_malloc_helper */
+ element++;
+
+ return element;
+}
+
+/**
+ * Free memory previously allocated by mem_malloc. Loads the pool number
+ * and calls memp_free with that pool number to put the element back into
+ * its pool
+ *
+ * @param rmem the memory element to free
+ */
+void
+mem_free(void *rmem)
+{
+ struct memp_malloc_helper *hmem = (struct memp_malloc_helper*)rmem;
+
+ LWIP_ASSERT("rmem != NULL", (rmem != NULL));
+ LWIP_ASSERT("rmem == MEM_ALIGN(rmem)", (rmem == LWIP_MEM_ALIGN(rmem)));
+
+ /* get the original struct memp_malloc_helper */
+ hmem--;
+
+ LWIP_ASSERT("hmem != NULL", (hmem != NULL));
+ LWIP_ASSERT("hmem == MEM_ALIGN(hmem)", (hmem == LWIP_MEM_ALIGN(hmem)));
+ LWIP_ASSERT("hmem->poolnr < MEMP_MAX", (hmem->poolnr < MEMP_MAX));
+
+ /* and put it in the pool we saved earlier */
+ memp_free(hmem->poolnr, hmem);
+}
+
+#else /* MEM_USE_POOLS */
+/* lwIP replacement for your libc malloc() */
+
+/**
+ * The heap is made up as a list of structs of this type.
+ * This does not have to be aligned since for getting its size,
+ * we only use the macro SIZEOF_STRUCT_MEM, which automatically alignes.
+ */
+struct mem {
+ /** index (-> ram[next]) of the next struct */
+ mem_size_t next;
+ /** index (-> ram[prev]) of the previous struct */
+ mem_size_t prev;
+ /** 1: this area is used; 0: this area is unused */
+ u8_t used;
+ u8_t pad[3]; /* XXX: pad here instead use global ALIGN */
+} __ATTRIB_PACK;
+
+/** All allocated blocks will be MIN_SIZE bytes big, at least!
+ * MIN_SIZE can be overridden to suit your needs. Smaller values save space,
+ * larger values could prevent too small blocks to fragment the RAM too much. */
+#ifndef MIN_SIZE
+#define MIN_SIZE 12
+#endif /* MIN_SIZE */
+/* some alignment macros: we define them here for better source code layout */
+#define MIN_SIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(MIN_SIZE)
+#define SIZEOF_STRUCT_MEM LWIP_MEM_ALIGN_SIZE(sizeof(struct mem))
+#define MEM_SIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(MEM_SIZE)
+
+/** If you want to relocate the heap to external memory, simply define
+ * LWIP_RAM_HEAP_POINTER as a void-pointer to that location.
+ * If so, make sure the memory at that location is big enough (see below on
+ * how that space is calculated). */
+#ifndef LWIP_RAM_HEAP_POINTER
+/** the heap. we need one struct mem at the end and some room for alignment */
+/* enlarge heap as tx pbuf payload is allocate from heap as well */
+u8_t ram_heap[MEM_SIZE_ALIGNED + (2*SIZEOF_STRUCT_MEM) + MEM_ALIGNMENT] SHMEM_ATTR;
+#define LWIP_RAM_HEAP_POINTER ram_heap
+#endif /* LWIP_RAM_HEAP_POINTER */
+
+/** pointer to the heap (ram_heap): for alignment, ram is now a pointer instead of an array */
+static u8_t *ram;
+/** the last entry, always unused! */
+static struct mem *ram_end;
+/** pointer to the lowest free block, this is used for faster search */
+static struct mem *lfree;
+
+/** concurrent access protection */
+//static sys_mutex_t mem_mutex;
+
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+
+static volatile u8_t mem_free_count;
+
+/* Allow mem_free from other (e.g. interrupt) context */
+#define LWIP_MEM_FREE_DECL_PROTECT() SYS_ARCH_DECL_PROTECT(lev_free)
+#define LWIP_MEM_FREE_PROTECT() SYS_ARCH_PROTECT(lev_free)
+#define LWIP_MEM_FREE_UNPROTECT() SYS_ARCH_UNPROTECT(lev_free)
+#define LWIP_MEM_ALLOC_DECL_PROTECT() SYS_ARCH_DECL_PROTECT(lev_alloc)
+#define LWIP_MEM_ALLOC_PROTECT() SYS_ARCH_PROTECT(lev_alloc)
+#define LWIP_MEM_ALLOC_UNPROTECT() SYS_ARCH_UNPROTECT(lev_alloc)
+
+#else /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+
+/* Protect the heap only by using a semaphore */
+#define LWIP_MEM_FREE_DECL_PROTECT()
+#define LWIP_MEM_FREE_PROTECT() sys_mutex_lock(&mem_mutex)
+#define LWIP_MEM_FREE_UNPROTECT() sys_mutex_unlock(&mem_mutex)
+/* mem_malloc is protected using semaphore AND LWIP_MEM_ALLOC_PROTECT */
+#define LWIP_MEM_ALLOC_DECL_PROTECT()
+#define LWIP_MEM_ALLOC_PROTECT()
+#define LWIP_MEM_ALLOC_UNPROTECT()
+
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+
+
+/**
+ * "Plug holes" by combining adjacent empty struct mems.
+ * After this function is through, there should not exist
+ * one empty struct mem pointing to another empty struct mem.
+ *
+ * @param mem this points to a struct mem which just has been freed
+ * @internal this function is only called by mem_free() and mem_trim()
+ *
+ * This assumes access to the heap is protected by the calling function
+ * already.
+ */
+static void ICACHE_FLASH_ATTR
+plug_holes(struct mem *mem)
+{
+ struct mem *nmem;
+ struct mem *pmem;
+
+ LWIP_ASSERT("plug_holes: mem >= ram", (u8_t *)mem >= ram);
+ LWIP_ASSERT("plug_holes: mem < ram_end", (u8_t *)mem < (u8_t *)ram_end);
+ LWIP_ASSERT("plug_holes: mem->used == 0", mem->used == 0);
+
+ /* plug hole forward */
+ LWIP_ASSERT("plug_holes: mem->next <= MEM_SIZE_ALIGNED", mem->next <= MEM_SIZE_ALIGNED);
+
+ nmem = (struct mem *)(void *)&ram[mem->next];
+ if (mem != nmem && nmem->used == 0 && (u8_t *)nmem != (u8_t *)ram_end) {
+ /* if mem->next is unused and not end of ram, combine mem and mem->next */
+ if (lfree == nmem) {
+ lfree = mem;
+ }
+ mem->next = nmem->next;
+ ((struct mem *)(void *)&ram[nmem->next])->prev = (mem_size_t)((u8_t *)mem - ram);
+ }
+
+ /* plug hole backward */
+ pmem = (struct mem *)(void *)&ram[mem->prev];
+ if (pmem != mem && pmem->used == 0) {
+ /* if mem->prev is unused, combine mem and mem->prev */
+ if (lfree == mem) {
+ lfree = pmem;
+ }
+ pmem->next = mem->next;
+ ((struct mem *)(void *)&ram[mem->next])->prev = (mem_size_t)((u8_t *)pmem - ram);
+ }
+}
+
+/**
+ * Zero the heap and initialize start, end and lowest-free
+ */
+void
+mem_init(void)
+{
+ struct mem *mem;
+
+ LWIP_ASSERT("Sanity check alignment",
+ (SIZEOF_STRUCT_MEM & (MEM_ALIGNMENT-1)) == 0);
+
+ /* align the heap */
+ ram = (u8_t *)LWIP_MEM_ALIGN(LWIP_RAM_HEAP_POINTER);
+ /* initialize the start of the heap */
+ mem = (struct mem *)(void *)ram;
+ mem->next = MEM_SIZE_ALIGNED;
+ mem->prev = 0;
+ mem->used = 0;
+ /* initialize the end of the heap */
+ ram_end = (struct mem *)(void *)&ram[MEM_SIZE_ALIGNED];
+ ram_end->used = 1;
+ ram_end->next = MEM_SIZE_ALIGNED;
+ ram_end->prev = MEM_SIZE_ALIGNED;
+
+ /* initialize the lowest-free pointer to the start of the heap */
+ lfree = (struct mem *)(void *)ram;
+
+ MEM_STATS_AVAIL(avail, MEM_SIZE_ALIGNED);
+
+ if(sys_mutex_new(&mem_mutex) != ERR_OK) {
+ LWIP_ASSERT("failed to create mem_mutex", 0);
+ }
+}
+
+/**
+ * Put a struct mem back on the heap
+ *
+ * @param rmem is the data portion of a struct mem as returned by a previous
+ * call to mem_malloc()
+ */
+void
+mem_free(void *rmem)
+{
+ struct mem *mem;
+ LWIP_MEM_FREE_DECL_PROTECT();
+
+ if (rmem == NULL) {
+ LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("mem_free(p == NULL) was called.\n"));
+ return;
+ }
+ LWIP_ASSERT("mem_free: sanity check alignment", (((mem_ptr_t)rmem) & (MEM_ALIGNMENT-1)) == 0);
+
+ LWIP_ASSERT("mem_free: legal memory", (u8_t *)rmem >= (u8_t *)ram &&
+ (u8_t *)rmem < (u8_t *)ram_end);
+
+ if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) {
+ SYS_ARCH_DECL_PROTECT(lev);
+ LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_free: illegal memory\n"));
+ /* protect mem stats from concurrent access */
+ SYS_ARCH_PROTECT(lev);
+ MEM_STATS_INC(illegal);
+ SYS_ARCH_UNPROTECT(lev);
+ return;
+ }
+ /* protect the heap from concurrent access */
+ LWIP_MEM_FREE_PROTECT();
+ /* Get the corresponding struct mem ... */
+ mem = (struct mem *)(void *)((u8_t *)rmem - SIZEOF_STRUCT_MEM);
+ /* ... which has to be in a used state ... */
+ LWIP_ASSERT("mem_free: mem->used", mem->used);
+ /* ... and is now unused. */
+ mem->used = 0;
+
+ if (mem < lfree) {
+ /* the newly freed struct is now the lowest */
+ lfree = mem;
+ }
+
+ MEM_STATS_DEC_USED(used, mem->next - (mem_size_t)(((u8_t *)mem - ram)));
+
+ /* finally, see if prev or next are free also */
+ plug_holes(mem);
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+ mem_free_count = 1;
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+ LWIP_MEM_FREE_UNPROTECT();
+}
+
+/**
+ * Shrink memory returned by mem_malloc().
+ *
+ * @param rmem pointer to memory allocated by mem_malloc the is to be shrinked
+ * @param newsize required size after shrinking (needs to be smaller than or
+ * equal to the previous size)
+ * @return for compatibility reasons: is always == rmem, at the moment
+ * or NULL if newsize is > old size, in which case rmem is NOT touched
+ * or freed!
+ */
+void *
+mem_trim(void *rmem, mem_size_t newsize)
+{
+ mem_size_t size;
+ mem_size_t ptr, ptr2;
+ struct mem *mem, *mem2;
+ /* use the FREE_PROTECT here: it protects with sem OR SYS_ARCH_PROTECT */
+ LWIP_MEM_FREE_DECL_PROTECT();
+
+ /* Expand the size of the allocated memory region so that we can
+ adjust for alignment. */
+ newsize = LWIP_MEM_ALIGN_SIZE(newsize);
+
+ if(newsize < MIN_SIZE_ALIGNED) {
+ /* every data block must be at least MIN_SIZE_ALIGNED long */
+ newsize = MIN_SIZE_ALIGNED;
+ }
+
+ if (newsize > MEM_SIZE_ALIGNED) {
+ return NULL;
+ }
+
+ LWIP_ASSERT("mem_trim: legal memory", (u8_t *)rmem >= (u8_t *)ram &&
+ (u8_t *)rmem < (u8_t *)ram_end);
+
+ if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) {
+ SYS_ARCH_DECL_PROTECT(lev);
+ LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_trim: illegal memory\n"));
+ /* protect mem stats from concurrent access */
+ SYS_ARCH_PROTECT(lev);
+ MEM_STATS_INC(illegal);
+ SYS_ARCH_UNPROTECT(lev);
+ return rmem;
+ }
+ /* Get the corresponding struct mem ... */
+ mem = (struct mem *)(void *)((u8_t *)rmem - SIZEOF_STRUCT_MEM);
+ /* ... and its offset pointer */
+ ptr = (mem_size_t)((u8_t *)mem - ram);
+
+ size = mem->next - ptr - SIZEOF_STRUCT_MEM;
+ LWIP_ASSERT("mem_trim can only shrink memory", newsize <= size);
+ if (newsize > size) {
+ /* not supported */
+ return NULL;
+ }
+ if (newsize == size) {
+ /* No change in size, simply return */
+ return rmem;
+ }
+
+ /* protect the heap from concurrent access */
+ LWIP_MEM_FREE_PROTECT();
+
+ mem2 = (struct mem *)(void *)&ram[mem->next];
+ if(mem2->used == 0) {
+ /* The next struct is unused, we can simply move it at little */
+ mem_size_t next;
+ /* remember the old next pointer */
+ next = mem2->next;
+ /* create new struct mem which is moved directly after the shrinked mem */
+ ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize;
+ if (lfree == mem2) {
+ lfree = (struct mem *)(void *)&ram[ptr2];
+ }
+ mem2 = (struct mem *)(void *)&ram[ptr2];
+ mem2->used = 0;
+ /* restore the next pointer */
+ mem2->next = next;
+ /* link it back to mem */
+ mem2->prev = ptr;
+ /* link mem to it */
+ mem->next = ptr2;
+ /* last thing to restore linked list: as we have moved mem2,
+ * let 'mem2->next->prev' point to mem2 again. but only if mem2->next is not
+ * the end of the heap */
+ if (mem2->next != MEM_SIZE_ALIGNED) {
+ ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2;
+ }
+ MEM_STATS_DEC_USED(used, (size - newsize));
+ /* no need to plug holes, we've already done that */
+ } else if (newsize + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED <= size) {
+ /* Next struct is used but there's room for another struct mem with
+ * at least MIN_SIZE_ALIGNED of data.
+ * Old size ('size') must be big enough to contain at least 'newsize' plus a struct mem
+ * ('SIZEOF_STRUCT_MEM') with some data ('MIN_SIZE_ALIGNED').
+ * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty
+ * region that couldn't hold data, but when mem->next gets freed,
+ * the 2 regions would be combined, resulting in more free memory */
+ ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize;
+ mem2 = (struct mem *)(void *)&ram[ptr2];
+ if (mem2 < lfree) {
+ lfree = mem2;
+ }
+ mem2->used = 0;
+ mem2->next = mem->next;
+ mem2->prev = ptr;
+ mem->next = ptr2;
+ if (mem2->next != MEM_SIZE_ALIGNED) {
+ ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2;
+ }
+ MEM_STATS_DEC_USED(used, (size - newsize));
+ /* the original mem->next is used, so no need to plug holes! */
+ }
+ /* else {
+ next struct mem is used but size between mem and mem2 is not big enough
+ to create another struct mem
+ -> don't do anyhting.
+ -> the remaining space stays unused since it is too small
+ } */
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+ mem_free_count = 1;
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+ LWIP_MEM_FREE_UNPROTECT();
+ return rmem;
+}
+
+/**
+ * Adam's mem_malloc() plus solution for bug #17922
+ * Allocate a block of memory with a minimum of 'size' bytes.
+ *
+ * @param size is the minimum size of the requested block in bytes.
+ * @return pointer to allocated memory or NULL if no free memory was found.
+ *
+ * Note that the returned value will always be aligned (as defined by MEM_ALIGNMENT).
+ */
+void *
+mem_malloc(mem_size_t size)
+{
+ mem_size_t ptr, ptr2;
+ struct mem *mem, *mem2;
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+ u8_t local_mem_free_count = 0;
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+ LWIP_MEM_ALLOC_DECL_PROTECT();
+
+ if (size == 0) {
+ return NULL;
+ }
+
+ /* Expand the size of the allocated memory region so that we can
+ adjust for alignment. */
+ size = LWIP_MEM_ALIGN_SIZE(size);
+
+ if(size < MIN_SIZE_ALIGNED) {
+ /* every data block must be at least MIN_SIZE_ALIGNED long */
+ size = MIN_SIZE_ALIGNED;
+ }
+
+ if (size > MEM_SIZE_ALIGNED) {
+ return NULL;
+ }
+
+ /* protect the heap from concurrent access */
+ sys_mutex_lock(&mem_mutex);
+ LWIP_MEM_ALLOC_PROTECT();
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+ /* run as long as a mem_free disturbed mem_malloc */
+ do {
+ local_mem_free_count = 0;
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+
+ /* Scan through the heap searching for a free block that is big enough,
+ * beginning with the lowest free block.
+ */
+ for (ptr = (mem_size_t)((u8_t *)lfree - ram); ptr < MEM_SIZE_ALIGNED - size;
+ ptr = ((struct mem *)(void *)&ram[ptr])->next) {
+ mem = (struct mem *)(void *)&ram[ptr];
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+ mem_free_count = 0;
+ LWIP_MEM_ALLOC_UNPROTECT();
+ /* allow mem_free to run */
+ LWIP_MEM_ALLOC_PROTECT();
+ if (mem_free_count != 0) {
+ local_mem_free_count = mem_free_count;
+ }
+ mem_free_count = 0;
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+
+ if ((!mem->used) &&
+ (mem->next - (ptr + SIZEOF_STRUCT_MEM)) >= size) {
+ /* mem is not used and at least perfect fit is possible:
+ * mem->next - (ptr + SIZEOF_STRUCT_MEM) gives us the 'user data size' of mem */
+
+ if (mem->next - (ptr + SIZEOF_STRUCT_MEM) >= (size + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED)) {
+ /* (in addition to the above, we test if another struct mem (SIZEOF_STRUCT_MEM) containing
+ * at least MIN_SIZE_ALIGNED of data also fits in the 'user data space' of 'mem')
+ * -> split large block, create empty remainder,
+ * remainder must be large enough to contain MIN_SIZE_ALIGNED data: if
+ * mem->next - (ptr + (2*SIZEOF_STRUCT_MEM)) == size,
+ * struct mem would fit in but no data between mem2 and mem2->next
+ * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty
+ * region that couldn't hold data, but when mem->next gets freed,
+ * the 2 regions would be combined, resulting in more free memory
+ */
+ ptr2 = ptr + SIZEOF_STRUCT_MEM + size;
+ /* create mem2 struct */
+ mem2 = (struct mem *)(void *)&ram[ptr2];
+ mem2->used = 0;
+ mem2->next = mem->next;
+ mem2->prev = ptr;
+ /* and insert it between mem and mem->next */
+ mem->next = ptr2;
+ mem->used = 1;
+
+ if (mem2->next != MEM_SIZE_ALIGNED) {
+ ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2;
+ }
+ MEM_STATS_INC_USED(used, (size + SIZEOF_STRUCT_MEM));
+ } else {
+ /* (a mem2 struct does no fit into the user data space of mem and mem->next will always
+ * be used at this point: if not we have 2 unused structs in a row, plug_holes should have
+ * take care of this).
+ * -> near fit or excact fit: do not split, no mem2 creation
+ * also can't move mem->next directly behind mem, since mem->next
+ * will always be used at this point!
+ */
+ mem->used = 1;
+ MEM_STATS_INC_USED(used, mem->next - (mem_size_t)((u8_t *)mem - ram));
+ }
+
+ if (mem == lfree) {
+ /* Find next free block after mem and update lowest free pointer */
+ while (lfree->used && lfree != ram_end) {
+ LWIP_MEM_ALLOC_UNPROTECT();
+ /* prevent high interrupt latency... */
+ LWIP_MEM_ALLOC_PROTECT();
+ lfree = (struct mem *)(void *)&ram[lfree->next];
+ }
+ LWIP_ASSERT("mem_malloc: !lfree->used", ((lfree == ram_end) || (!lfree->used)));
+ }
+ LWIP_MEM_ALLOC_UNPROTECT();
+ sys_mutex_unlock(&mem_mutex);
+ LWIP_ASSERT("mem_malloc: allocated memory not above ram_end.",
+ (mem_ptr_t)mem + SIZEOF_STRUCT_MEM + size <= (mem_ptr_t)ram_end);
+ LWIP_ASSERT("mem_malloc: allocated memory properly aligned.",
+ ((mem_ptr_t)mem + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT == 0);
+ LWIP_ASSERT("mem_malloc: sanity check alignment",
+ (((mem_ptr_t)mem) & (MEM_ALIGNMENT-1)) == 0);
+
+ return (u8_t *)mem + SIZEOF_STRUCT_MEM;
+ }
+ }
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+ /* if we got interrupted by a mem_free, try again */
+ } while(local_mem_free_count != 0);
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+ LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("mem_malloc: could not allocate %"S16_F" bytes\n", (s16_t)size));
+ MEM_STATS_INC(err);
+ LWIP_MEM_ALLOC_UNPROTECT();
+ sys_mutex_unlock(&mem_mutex);
+ return NULL;
+}
+
+#endif /* MEM_USE_POOLS */
+/**
+ * Contiguously allocates enough space for count objects that are size bytes
+ * of memory each and returns a pointer to the allocated memory.
+ *
+ * The allocated memory is filled with bytes of value zero.
+ *
+ * @param count number of objects to allocate
+ * @param size size of the objects to allocate
+ * @return pointer to allocated memory / NULL pointer if there is an error
+ */
+void *mem_calloc(mem_size_t count, mem_size_t size)
+{
+ void *p;
+
+ /* allocate 'count' objects of size 'size' */
+ p = mem_malloc(count * size);
+ if (p) {
+ /* zero the memory */
+ memset(p, 0, count * size);
+ }
+ return p;
+}
+
+#endif /* !MEM_LIBC_MALLOC */
diff --git a/app/lwip/core/memp.c b/app/lwip/core/memp.c
new file mode 100644
index 00000000..16caf20b
--- /dev/null
+++ b/app/lwip/core/memp.c
@@ -0,0 +1,490 @@
+/**
+ * @file
+ * Dynamic pool memory manager
+ *
+ * lwIP has dedicated pools for many structures (netconn, protocol control blocks,
+ * packet buffers, ...). All these pools are managed here.
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/memp.h"
+#include "lwip/pbuf.h"
+#include "lwip/udp.h"
+#include "lwip/raw.h"
+#include "lwip/tcp_impl.h"
+#include "lwip/igmp.h"
+#include "lwip/api.h"
+#include "lwip/api_msg.h"
+#include "lwip/tcpip.h"
+#include "lwip/sys.h"
+#include "lwip/timers.h"
+#include "lwip/stats.h"
+#include "netif/etharp.h"
+#include "lwip/ip_frag.h"
+#include "lwip/snmp_structs.h"
+#include "lwip/snmp_msg.h"
+#include "lwip/dns.h"
+#include "netif/ppp_oe.h"
+
+#include
+
+#if !MEMP_MEM_MALLOC /* don't build if not configured for use in lwipopts.h */
+
+struct memp {
+ struct memp *next;
+#if MEMP_OVERFLOW_CHECK
+ const char *file;
+ int line;
+#endif /* MEMP_OVERFLOW_CHECK */
+};
+
+#if MEMP_OVERFLOW_CHECK
+/* if MEMP_OVERFLOW_CHECK is turned on, we reserve some bytes at the beginning
+ * and at the end of each element, initialize them as 0xcd and check
+ * them later. */
+/* If MEMP_OVERFLOW_CHECK is >= 2, on every call to memp_malloc or memp_free,
+ * every single element in each pool is checked!
+ * This is VERY SLOW but also very helpful. */
+/* MEMP_SANITY_REGION_BEFORE and MEMP_SANITY_REGION_AFTER can be overridden in
+ * lwipopts.h to change the amount reserved for checking. */
+#ifndef MEMP_SANITY_REGION_BEFORE
+#define MEMP_SANITY_REGION_BEFORE 16
+#endif /* MEMP_SANITY_REGION_BEFORE*/
+#if MEMP_SANITY_REGION_BEFORE > 0
+#define MEMP_SANITY_REGION_BEFORE_ALIGNED LWIP_MEM_ALIGN_SIZE(MEMP_SANITY_REGION_BEFORE)
+#else
+#define MEMP_SANITY_REGION_BEFORE_ALIGNED 0
+#endif /* MEMP_SANITY_REGION_BEFORE*/
+#ifndef MEMP_SANITY_REGION_AFTER
+#define MEMP_SANITY_REGION_AFTER 16
+#endif /* MEMP_SANITY_REGION_AFTER*/
+#if MEMP_SANITY_REGION_AFTER > 0
+#define MEMP_SANITY_REGION_AFTER_ALIGNED LWIP_MEM_ALIGN_SIZE(MEMP_SANITY_REGION_AFTER)
+#else
+#define MEMP_SANITY_REGION_AFTER_ALIGNED 0
+#endif /* MEMP_SANITY_REGION_AFTER*/
+
+/* MEMP_SIZE: save space for struct memp and for sanity check */
+#define MEMP_SIZE (LWIP_MEM_ALIGN_SIZE(sizeof(struct memp)) + MEMP_SANITY_REGION_BEFORE_ALIGNED)
+#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x) + MEMP_SANITY_REGION_AFTER_ALIGNED)
+
+#else /* MEMP_OVERFLOW_CHECK */
+
+/* No sanity checks
+ * We don't need to preserve the struct memp while not allocated, so we
+ * can save a little space and set MEMP_SIZE to 0.
+ */
+#define MEMP_SIZE 0
+#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x))
+
+#endif /* MEMP_OVERFLOW_CHECK */
+
+/** This array holds the first free element of each pool.
+ * Elements form a linked list. */
+static struct memp *memp_tab[MEMP_MAX];
+
+#else /* MEMP_MEM_MALLOC */
+
+#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x))
+
+#endif /* MEMP_MEM_MALLOC */
+
+/** This array holds the element sizes of each pool. */
+#if !MEM_USE_POOLS && !MEMP_MEM_MALLOC
+static
+#endif
+const u16_t memp_sizes[MEMP_MAX] = { //LWIP_MEM_ALIGN_SIZE
+#define LWIP_MEMPOOL(name,num,size,desc,attr) LWIP_MEM_ALIGN_SIZE(size),
+#include "lwip/memp_std.h"
+};
+
+u16_t memp_sizes_test[1] = {PBUF_POOL_BUFSIZE,};
+
+#if !MEMP_MEM_MALLOC /* don't build if not configured for use in lwipopts.h */
+
+/** This array holds the number of elements in each pool. */
+static const u16_t memp_num[MEMP_MAX] = {
+#define LWIP_MEMPOOL(name,num,size,desc,attr) (num),
+#include "lwip/memp_std.h"
+};
+
+/** This array holds a textual description of each pool. */
+//#ifdef LWIP_DEBUG
+//static const char *memp_desc[MEMP_MAX] = {
+const char *memp_desc[MEMP_MAX] = {
+#define LWIP_MEMPOOL(name,num,size,desc,attr) (desc),
+#include "lwip/memp_std.h"
+};
+//#endif /* LWIP_DEBUG */
+
+#if MEMP_SEPARATE_POOLS
+
+/** This creates each memory pool. These are named memp_memory_XXX_base (where
+ * XXX is the name of the pool defined in memp_std.h).
+ * To relocate a pool, declare it as extern in cc.h. Example for GCC:
+ * extern u8_t __attribute__((section(".onchip_mem"))) memp_memory_UDP_PCB_base[];
+ */
+#define LWIP_MEMPOOL(name,num,size,desc,attr) u8_t memp_memory_ ## name ## _base \
+ [((num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size)))] attr;
+#include "lwip/memp_std.h"
+
+/** This array holds the base of each memory pool. */
+static u8_t *const memp_bases[] = {
+#define LWIP_MEMPOOL(name,num,size,desc,attr) memp_memory_ ## name ## _base,
+#include "lwip/memp_std.h"
+};
+
+#else /* MEMP_SEPARATE_POOLS */
+
+/** This is the actual memory used by the pools (all pools in one big block). */
+static u8_t memp_memory[MEM_ALIGNMENT - 1
+#define LWIP_MEMPOOL(name,num,size,desc, attr) + ( (num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size) ) )
+#include "lwip/memp_std.h"
+];
+
+#endif /* MEMP_SEPARATE_POOLS */
+
+#if MEMP_SANITY_CHECK
+/**
+ * Check that memp-lists don't form a circle, modify by ives at 2014.4.23.
+ */
+static int ICACHE_FLASH_ATTR
+memp_sanity(void)
+{
+ s16_t i;
+ struct memp *t, *h;
+
+ for (i = 0; i < MEMP_MAX; i++) {
+ t = memp_tab[i];
+ if(t != NULL) {
+ for (h = t->next; (t != NULL) && (h != NULL); t = t->next,
+ h = (((h->next != NULL) && (h->next->next != NULL)) ? h->next->next : NULL)) {
+ if (t == h) {
+ return 0;
+ }
+ }
+ }
+ }
+ return 1;
+}
+#endif /* MEMP_SANITY_CHECK*/
+#if MEMP_OVERFLOW_CHECK
+#if defined(LWIP_DEBUG) && MEMP_STATS
+static const char * memp_overflow_names[] = {
+#define LWIP_MEMPOOL(name,num,size,desc,attr) "/"desc,
+#include "lwip/memp_std.h"
+ };
+#endif
+
+/**
+ * Check if a memp element was victim of an overflow
+ * (e.g. the restricted area after it has been altered)
+ *
+ * @param p the memp element to check
+ * @param memp_type the pool p comes from
+ */
+static void ICACHE_FLASH_ATTR
+memp_overflow_check_element_overflow(struct memp *p, u16_t memp_type)
+{
+ u16_t k;
+ u8_t *m;
+#if MEMP_SANITY_REGION_AFTER_ALIGNED > 0
+ m = (u8_t*)p + MEMP_SIZE + memp_sizes[memp_type];
+ for (k = 0; k < MEMP_SANITY_REGION_AFTER_ALIGNED; k++) {
+ if (m[k] != 0xcd) {
+ char errstr[128] = "detected memp overflow in pool ";
+ char digit[] = "0";
+ if(memp_type >= 10) {
+ digit[0] = '0' + (memp_type/10);
+ strcat(errstr, digit);
+ }
+ digit[0] = '0' + (memp_type%10);
+ strcat(errstr, digit);
+#if defined(LWIP_DEBUG) && MEMP_STATS
+ strcat(errstr, memp_overflow_names[memp_type]);
+#endif
+ LWIP_ASSERT(errstr, 0);
+ }
+ }
+#endif
+}
+
+/**
+ * Check if a memp element was victim of an underflow
+ * (e.g. the restricted area before it has been altered)
+ *
+ * @param p the memp element to check
+ * @param memp_type the pool p comes from
+ */
+static void ICACHE_FLASH_ATTR
+memp_overflow_check_element_underflow(struct memp *p, u16_t memp_type)
+{
+ u16_t k;
+ u8_t *m;
+#if MEMP_SANITY_REGION_BEFORE_ALIGNED > 0
+ m = (u8_t*)p + MEMP_SIZE - MEMP_SANITY_REGION_BEFORE_ALIGNED;
+ for (k = 0; k < MEMP_SANITY_REGION_BEFORE_ALIGNED; k++) {
+ if (m[k] != 0xcd) {
+ char errstr[128] = "detected memp underflow in pool ";
+ char digit[] = "0";
+ if(memp_type >= 10) {
+ digit[0] = '0' + (memp_type/10);
+ strcat(errstr, digit);
+ }
+ digit[0] = '0' + (memp_type%10);
+ strcat(errstr, digit);
+#if defined(LWIP_DEBUG) && MEMP_STATS
+ strcat(errstr, memp_overflow_names[memp_type]);
+#endif
+ LWIP_ASSERT(errstr, 0);
+ }
+ }
+#endif
+}
+
+/**
+ * Do an overflow check for all elements in every pool.
+ *
+ * @see memp_overflow_check_element for a description of the check
+ */
+static void ICACHE_FLASH_ATTR
+memp_overflow_check_all(void)
+{
+ u16_t i, j;
+ struct memp *p;
+
+ p = (struct memp *)LWIP_MEM_ALIGN(memp_memory);
+ for (i = 0; i < MEMP_MAX; ++i) {
+ p = p;
+ for (j = 0; j < memp_num[i]; ++j) {
+ memp_overflow_check_element_overflow(p, i);
+ p = (struct memp*)((u8_t*)p + MEMP_SIZE + memp_sizes[i] + MEMP_SANITY_REGION_AFTER_ALIGNED);
+ }
+ }
+ p = (struct memp *)LWIP_MEM_ALIGN(memp_memory);
+ for (i = 0; i < MEMP_MAX; ++i) {
+ p = p;
+ for (j = 0; j < memp_num[i]; ++j) {
+ memp_overflow_check_element_underflow(p, i);
+ p = (struct memp*)((u8_t*)p + MEMP_SIZE + memp_sizes[i] + MEMP_SANITY_REGION_AFTER_ALIGNED);
+ }
+ }
+}
+
+/**
+ * Initialize the restricted areas of all memp elements in every pool.
+ */
+static void ICACHE_FLASH_ATTR
+memp_overflow_init(void)
+{
+ u16_t i, j;
+ struct memp *p;
+ u8_t *m;
+
+ p = (struct memp *)LWIP_MEM_ALIGN(memp_memory);
+ for (i = 0; i < MEMP_MAX; ++i) {
+ p = p;
+ for (j = 0; j < memp_num[i]; ++j) {
+#if MEMP_SANITY_REGION_BEFORE_ALIGNED > 0
+ m = (u8_t*)p + MEMP_SIZE - MEMP_SANITY_REGION_BEFORE_ALIGNED;
+ memset(m, 0xcd, MEMP_SANITY_REGION_BEFORE_ALIGNED);
+#endif
+#if MEMP_SANITY_REGION_AFTER_ALIGNED > 0
+ m = (u8_t*)p + MEMP_SIZE + memp_sizes[i];
+ memset(m, 0xcd, MEMP_SANITY_REGION_AFTER_ALIGNED);
+#endif
+ p = (struct memp*)((u8_t*)p + MEMP_SIZE + memp_sizes[i] + MEMP_SANITY_REGION_AFTER_ALIGNED);
+ }
+ }
+}
+#endif /* MEMP_OVERFLOW_CHECK */
+
+/**
+ * Initialize this module.
+ *
+ * Carves out memp_memory into linked lists for each pool-type.
+ */
+void
+memp_init(void)
+{
+ struct memp *memp;
+ u16_t i, j;
+
+ for (i = 0; i < MEMP_MAX; ++i) {
+ MEMP_STATS_AVAIL(used, i, 0);
+ MEMP_STATS_AVAIL(max, i, 0);
+ MEMP_STATS_AVAIL(err, i, 0);
+ MEMP_STATS_AVAIL(avail, i, memp_num[i]);
+ }
+
+#if !MEMP_SEPARATE_POOLS
+ memp = (struct memp *)LWIP_MEM_ALIGN(memp_memory);
+#endif /* !MEMP_SEPARATE_POOLS */
+ /* for every pool: */
+ for (i = 0; i < MEMP_MAX; ++i) {
+ memp_tab[i] = NULL;
+#if MEMP_SEPARATE_POOLS
+ memp = (struct memp*)memp_bases[i];
+#endif /* MEMP_SEPARATE_POOLS */
+ /* create a linked list of memp elements */
+ for (j = 0; j < memp_num[i]; ++j) {
+ memp->next = (struct memp *)memp_tab[i];
+ memp_tab[i] = memp;
+ memp = (struct memp *)(void *)((u8_t *)memp + MEMP_SIZE + memp_sizes[i]
+#if MEMP_OVERFLOW_CHECK
+ + MEMP_SANITY_REGION_AFTER_ALIGNED
+#endif
+ );
+ }
+ }
+#if MEMP_OVERFLOW_CHECK
+ memp_overflow_init();
+ /* check everything a first time to see if it worked */
+ memp_overflow_check_all();
+#endif /* MEMP_OVERFLOW_CHECK */
+}
+
+/**
+ * Get an element from a specific pool.
+ *
+ * @param type the pool to get an element from
+ *
+ * the debug version has two more parameters:
+ * @param file file name calling this function
+ * @param line number of line where this function is called
+ *
+ * @return a pointer to the allocated memory or a NULL pointer on error
+ */
+void *
+#if !MEMP_OVERFLOW_CHECK
+memp_malloc(memp_t type)
+#else
+memp_malloc_fn(memp_t type, const char* file, const int line)
+#endif
+{
+ struct memp *memp;
+ SYS_ARCH_DECL_PROTECT(old_level);
+
+ LWIP_ERROR("memp_malloc: type < MEMP_MAX", (type < MEMP_MAX), return NULL;);
+
+ SYS_ARCH_PROTECT(old_level);
+#if MEMP_OVERFLOW_CHECK >= 2
+ memp_overflow_check_all();
+#endif /* MEMP_OVERFLOW_CHECK >= 2 */
+
+ memp = memp_tab[type];
+
+ if (memp != NULL) {
+ memp_tab[type] = memp->next;
+#if MEMP_OVERFLOW_CHECK
+ memp->next = NULL;
+ memp->file = file;
+ memp->line = line;
+#endif /* MEMP_OVERFLOW_CHECK */
+ MEMP_STATS_INC_USED(used, type);
+ LWIP_ASSERT("memp_malloc: memp properly aligned",
+ ((mem_ptr_t)memp % MEM_ALIGNMENT) == 0);
+ memp = (struct memp*)(void *)((u8_t*)memp + MEMP_SIZE);
+ } else {
+ LWIP_DEBUGF(MEMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("memp_malloc: out of memory in pool %s\n", memp_desc[type]));
+ MEMP_STATS_INC(err, type);
+ }
+
+ SYS_ARCH_UNPROTECT(old_level);
+
+ return memp;
+}
+
+/**
+ * Put an element back into its pool.
+ *
+ * @param type the pool where to put mem
+ * @param mem the memp element to free
+ */
+void
+memp_free(memp_t type, void *mem)
+{
+ struct memp *memp;
+ SYS_ARCH_DECL_PROTECT(old_level);
+
+ if (mem == NULL) {
+ return;
+ }
+ LWIP_ASSERT("memp_free: mem properly aligned",
+ ((mem_ptr_t)mem % MEM_ALIGNMENT) == 0);
+
+ memp = (struct memp *)(void *)((u8_t*)mem - MEMP_SIZE);
+
+ SYS_ARCH_PROTECT(old_level);
+#if MEMP_OVERFLOW_CHECK
+#if MEMP_OVERFLOW_CHECK >= 2
+ memp_overflow_check_all();
+#else
+ memp_overflow_check_element_overflow(memp, type);
+ memp_overflow_check_element_underflow(memp, type);
+#endif /* MEMP_OVERFLOW_CHECK >= 2 */
+#endif /* MEMP_OVERFLOW_CHECK */
+
+ MEMP_STATS_DEC(used, type);
+
+ memp->next = memp_tab[type];
+ memp_tab[type] = memp;
+
+#if MEMP_SANITY_CHECK
+ LWIP_ASSERT("memp sanity", memp_sanity());
+#endif /* MEMP_SANITY_CHECK */
+
+ SYS_ARCH_UNPROTECT(old_level);
+}
+
+#endif /* MEMP_MEM_MALLOC */
+#if 0
+void memp_dump(void)
+{
+ printf("sizeof raw_pcb %u, memp_s1 %u, %s\n", sizeof(struct raw_pcb), memp_sizes[0], memp_desc[0]);
+ printf("sizeof udp_pcb %u, memp_s2 %u, %s\n", sizeof(struct udp_pcb), memp_sizes[1], memp_desc[1]);
+ printf("sizeof tcp_pcb %u, memp_s3 %u, %s\n", sizeof(struct tcp_pcb), memp_sizes[2], memp_desc[2]);
+ printf("sizeof tcp_pcb_listen %u, memp_s4 %u, %s\n", sizeof(struct tcp_pcb_listen), memp_sizes[3], memp_desc[3]);
+ printf("sizeof tcp_seg %u, memp_s5 %u, %s\n", sizeof(struct tcp_seg), memp_sizes[4], memp_desc[4]);
+ printf("sizeof sys_timeo %u, memp_s6 %u, %s\n", sizeof(struct sys_timeo), memp_sizes[5], memp_desc[5]);
+ printf("sizeof pbuf %u, memp_s7 %u, %s\n", sizeof(struct pbuf), memp_sizes[6], memp_desc[6]);
+ printf("align pbuf size %u, memp_s8 %u, %s\n", (PBUF_POOL_BUFSIZE), memp_sizes[7], memp_desc[7]);
+ printf("TCP_MSS %d PBUF_LINK_HLEN %d ETH_PAD_SIZE %d\n", TCP_MSS, PBUF_LINK_HLEN, ETH_PAD_SIZE);
+ printf("TCP_MSS + PBUF_LINK_HLEN + ETH_PAD_SIZE %d \n", TCP_MSS+PBUF_LINK_HLEN+ETH_PAD_SIZE+40);
+ printf("test size %u\n",memp_sizes_test[0]);
+ printf("sizeof memp_memory_PBUF_pool %u \n", sizeof(memp_memory_PBUF_POOL_base));
+}
+#endif //0000
diff --git a/app/lwip/core/netif.c b/app/lwip/core/netif.c
new file mode 100644
index 00000000..6a11dcac
--- /dev/null
+++ b/app/lwip/core/netif.c
@@ -0,0 +1,757 @@
+/**
+ * @file
+ * lwIP network interface abstraction
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/def.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/tcp_impl.h"
+#include "lwip/snmp.h"
+#include "lwip/igmp.h"
+#include "netif/etharp.h"
+#include "lwip/stats.h"
+#if ENABLE_LOOPBACK
+#include "lwip/sys.h"
+#if LWIP_NETIF_LOOPBACK_MULTITHREADING
+#include "lwip/tcpip.h"
+#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */
+#endif /* ENABLE_LOOPBACK */
+
+#if LWIP_AUTOIP
+#include "lwip/autoip.h"
+#endif /* LWIP_AUTOIP */
+#if LWIP_DHCP
+#include "lwip/dhcp.h"
+#endif /* LWIP_DHCP */
+
+#if LWIP_NETIF_STATUS_CALLBACK
+#define NETIF_STATUS_CALLBACK(n) do{ if (n->status_callback) { (n->status_callback)(n); }}while(0)
+#else
+#define NETIF_STATUS_CALLBACK(n)
+#endif /* LWIP_NETIF_STATUS_CALLBACK */
+
+#if LWIP_NETIF_LINK_CALLBACK
+#define NETIF_LINK_CALLBACK(n) do{ if (n->link_callback) { (n->link_callback)(n); }}while(0)
+#else
+#define NETIF_LINK_CALLBACK(n)
+#endif /* LWIP_NETIF_LINK_CALLBACK */
+
+struct netif *netif_list;
+struct netif *netif_default;
+
+#if LWIP_HAVE_LOOPIF
+static struct netif loop_netif;
+
+/**
+ * Initialize a lwip network interface structure for a loopback interface
+ *
+ * @param netif the lwip network interface structure for this loopif
+ * @return ERR_OK if the loopif is initialized
+ * ERR_MEM if private data couldn't be allocated
+ */
+ static err_t ICACHE_FLASH_ATTR
+netif_loopif_init(struct netif *netif)
+{
+ /* initialize the snmp variables and counters inside the struct netif
+ * ifSpeed: no assumption can be made!
+ */
+ NETIF_INIT_SNMP(netif, snmp_ifType_softwareLoopback, 0);
+
+ netif->name[0] = 'l';
+ netif->name[1] = 'o';
+ netif->output = netif_loop_output;
+ return ERR_OK;
+}
+#endif /* LWIP_HAVE_LOOPIF */
+
+void
+netif_init(void)
+{
+#if LWIP_HAVE_LOOPIF
+ ip_addr_t loop_ipaddr, loop_netmask, loop_gw;
+ IP4_ADDR(&loop_gw, 127,0,0,1);
+ IP4_ADDR(&loop_ipaddr, 127,0,0,1);
+ IP4_ADDR(&loop_netmask, 255,0,0,0);
+
+#if NO_SYS
+ netif_add(&loop_netif, &loop_ipaddr, &loop_netmask, &loop_gw, NULL, netif_loopif_init, ip_input);
+#else /* NO_SYS */
+ netif_add(&loop_netif, &loop_ipaddr, &loop_netmask, &loop_gw, NULL, netif_loopif_init, tcpip_input);
+#endif /* NO_SYS */
+ netif_set_up(&loop_netif);
+
+#endif /* LWIP_HAVE_LOOPIF */
+}
+
+/**
+ * Add a network interface to the list of lwIP netifs.
+ *
+ * @param netif a pre-allocated netif structure
+ * @param ipaddr IP address for the new netif
+ * @param netmask network mask for the new netif
+ * @param gw default gateway IP address for the new netif
+ * @param state opaque data passed to the new netif
+ * @param init callback function that initializes the interface
+ * @param input callback function that is called to pass
+ * ingress packets up in the protocol layer stack.
+ *
+ * @return netif, or NULL if failed.
+ */
+struct netif *
+netif_add(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask,
+ ip_addr_t *gw, void *state, netif_init_fn init, netif_input_fn input)
+{
+ static u8_t netifnum = 0;
+
+ LWIP_ASSERT("No init function given", init != NULL);
+
+ /* reset new interface configuration state */
+ ip_addr_set_zero(&netif->ip_addr);
+ ip_addr_set_zero(&netif->netmask);
+ ip_addr_set_zero(&netif->gw);
+ netif->flags = 0;
+#if LWIP_DHCP
+ /* netif not under DHCP control by default */
+ netif->dhcp = NULL;
+#endif /* LWIP_DHCP */
+#if LWIP_AUTOIP
+ /* netif not under AutoIP control by default */
+ netif->autoip = NULL;
+#endif /* LWIP_AUTOIP */
+#if LWIP_NETIF_STATUS_CALLBACK
+ netif->status_callback = NULL;
+#endif /* LWIP_NETIF_STATUS_CALLBACK */
+#if LWIP_NETIF_LINK_CALLBACK
+ netif->link_callback = NULL;
+#endif /* LWIP_NETIF_LINK_CALLBACK */
+#if LWIP_IGMP
+ netif->igmp_mac_filter = NULL;
+#endif /* LWIP_IGMP */
+#if ENABLE_LOOPBACK
+ netif->loop_first = NULL;
+ netif->loop_last = NULL;
+#endif /* ENABLE_LOOPBACK */
+
+ /* remember netif specific state information data */
+ netif->state = state;
+ netif->num = netifnum++;
+ netif->input = input;
+#if LWIP_NETIF_HWADDRHINT
+ netif->addr_hint = NULL;
+#endif /* LWIP_NETIF_HWADDRHINT*/
+#if ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS
+ netif->loop_cnt_current = 0;
+#endif /* ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS */
+
+ netif_set_addr(netif, ipaddr, netmask, gw);
+
+ /* call user specified initialization function for netif */
+ if (init(netif) != ERR_OK) {
+ return NULL;
+ }
+
+ /* add this netif to the list */
+ netif->next = netif_list;
+ netif_list = netif;
+ snmp_inc_iflist();
+
+#if LWIP_IGMP
+ /* start IGMP processing */
+ if (netif->flags & NETIF_FLAG_IGMP) {
+ igmp_start(netif);
+ }
+#endif /* LWIP_IGMP */
+
+ LWIP_DEBUGF(NETIF_DEBUG, ("netif: added interface %c%c IP addr ",
+ netif->name[0], netif->name[1]));
+ ip_addr_debug_print(NETIF_DEBUG, ipaddr);
+ LWIP_DEBUGF(NETIF_DEBUG, (" netmask "));
+ ip_addr_debug_print(NETIF_DEBUG, netmask);
+ LWIP_DEBUGF(NETIF_DEBUG, (" gw "));
+ ip_addr_debug_print(NETIF_DEBUG, gw);
+ LWIP_DEBUGF(NETIF_DEBUG, ("\n"));
+ return netif;
+}
+
+/**
+ * Change IP address configuration for a network interface (including netmask
+ * and default gateway).
+ *
+ * @param netif the network interface to change
+ * @param ipaddr the new IP address
+ * @param netmask the new netmask
+ * @param gw the new default gateway
+ */
+void
+netif_set_addr(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask,
+ ip_addr_t *gw)
+{
+ netif_set_ipaddr(netif, ipaddr);
+ netif_set_netmask(netif, netmask);
+ netif_set_gw(netif, gw);
+}
+
+/**
+ * Remove a network interface from the list of lwIP netifs.
+ *
+ * @param netif the network interface to remove
+ */
+void
+netif_remove(struct netif *netif)
+{
+ if (netif == NULL) {
+ return;
+ }
+
+#if LWIP_IGMP
+ /* stop IGMP processing */
+ if (netif->flags & NETIF_FLAG_IGMP) {
+ igmp_stop(netif);
+ }
+#endif /* LWIP_IGMP */
+ if (netif_is_up(netif)) {
+ /* set netif down before removing (call callback function) */
+ netif_set_down(netif);
+ }
+
+ snmp_delete_ipaddridx_tree(netif);
+
+ /* is it the first netif? */
+ if (netif_list == netif) {
+ netif_list = netif->next;
+ } else {
+ /* look for netif further down the list */
+ struct netif * tmpNetif;
+ for (tmpNetif = netif_list; tmpNetif != NULL; tmpNetif = tmpNetif->next) {
+ if (tmpNetif->next == netif) {
+ tmpNetif->next = netif->next;
+ break;
+ }
+ }
+ if (tmpNetif == NULL)
+ return; /* we didn't find any netif today */
+ }
+ snmp_dec_iflist();
+ /* this netif is default? */
+ if (netif_default == netif) {
+ /* reset default netif */
+ netif_set_default(NULL);
+ }
+ LWIP_DEBUGF( NETIF_DEBUG, ("netif_remove: removed netif\n") );
+}
+
+/**
+ * Find a network interface by searching for its name
+ *
+ * @param name the name of the netif (like netif->name) plus concatenated number
+ * in ascii representation (e.g. 'en0')
+ */
+struct netif *
+netif_find(char *name)
+{
+ struct netif *netif;
+ u8_t num;
+
+ if (name == NULL) {
+ return NULL;
+ }
+
+ num = name[2] - '0';
+
+ for(netif = netif_list; netif != NULL; netif = netif->next) {
+ if (num == netif->num &&
+ name[0] == netif->name[0] &&
+ name[1] == netif->name[1]) {
+ LWIP_DEBUGF(NETIF_DEBUG, ("netif_find: found %c%c\n", name[0], name[1]));
+ return netif;
+ }
+ }
+ LWIP_DEBUGF(NETIF_DEBUG, ("netif_find: didn't find %c%c\n", name[0], name[1]));
+ return NULL;
+}
+
+/**
+ * Change the IP address of a network interface
+ *
+ * @param netif the network interface to change
+ * @param ipaddr the new IP address
+ *
+ * @note call netif_set_addr() if you also want to change netmask and
+ * default gateway
+ */
+void
+netif_set_ipaddr(struct netif *netif, ip_addr_t *ipaddr)
+{
+ /* TODO: Handling of obsolete pcbs */
+ /* See: http://mail.gnu.org/archive/html/lwip-users/2003-03/msg00118.html */
+#if LWIP_TCP
+ struct tcp_pcb *pcb;
+ struct tcp_pcb_listen *lpcb;
+
+ /* address is actually being changed? */
+ if (ipaddr && (ip_addr_cmp(ipaddr, &(netif->ip_addr))) == 0) {
+ /* extern struct tcp_pcb *tcp_active_pcbs; defined by tcp.h */
+ LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_set_ipaddr: netif address being changed\n"));
+ pcb = tcp_active_pcbs;
+ while (pcb != NULL) {
+ /* PCB bound to current local interface address? */
+ if (ip_addr_cmp(&(pcb->local_ip), &(netif->ip_addr))
+#if LWIP_AUTOIP
+ /* connections to link-local addresses must persist (RFC3927 ch. 1.9) */
+ && !ip_addr_islinklocal(&(pcb->local_ip))
+#endif /* LWIP_AUTOIP */
+ ) {
+ /* this connection must be aborted */
+ struct tcp_pcb *next = pcb->next;
+ LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_set_ipaddr: aborting TCP pcb %p\n", (void *)pcb));
+ tcp_abort(pcb);
+ pcb = next;
+ } else {
+ pcb = pcb->next;
+ }
+ }
+ for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
+ /* PCB bound to current local interface address? */
+ if ((!(ip_addr_isany(&(lpcb->local_ip)))) &&
+ (ip_addr_cmp(&(lpcb->local_ip), &(netif->ip_addr)))) {
+ /* The PCB is listening to the old ipaddr and
+ * is set to listen to the new one instead */
+ ip_addr_set(&(lpcb->local_ip), ipaddr);
+ }
+ }
+ }
+#endif
+ snmp_delete_ipaddridx_tree(netif);
+ snmp_delete_iprteidx_tree(0,netif);
+ /* set new IP address to netif */
+ ip_addr_set(&(netif->ip_addr), ipaddr);
+ snmp_insert_ipaddridx_tree(netif);
+ snmp_insert_iprteidx_tree(0,netif);
+
+ LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: IP address of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ netif->name[0], netif->name[1],
+ ip4_addr1_16(&netif->ip_addr),
+ ip4_addr2_16(&netif->ip_addr),
+ ip4_addr3_16(&netif->ip_addr),
+ ip4_addr4_16(&netif->ip_addr)));
+}
+
+/**
+ * Change the default gateway for a network interface
+ *
+ * @param netif the network interface to change
+ * @param gw the new default gateway
+ *
+ * @note call netif_set_addr() if you also want to change ip address and netmask
+ */
+void
+netif_set_gw(struct netif *netif, ip_addr_t *gw)
+{
+ ip_addr_set(&(netif->gw), gw);
+ LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: GW address of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ netif->name[0], netif->name[1],
+ ip4_addr1_16(&netif->gw),
+ ip4_addr2_16(&netif->gw),
+ ip4_addr3_16(&netif->gw),
+ ip4_addr4_16(&netif->gw)));
+}
+
+/**
+ * Change the netmask of a network interface
+ *
+ * @param netif the network interface to change
+ * @param netmask the new netmask
+ *
+ * @note call netif_set_addr() if you also want to change ip address and
+ * default gateway
+ */
+void
+netif_set_netmask(struct netif *netif, ip_addr_t *netmask)
+{
+ snmp_delete_iprteidx_tree(0, netif);
+ /* set new netmask to netif */
+ ip_addr_set(&(netif->netmask), netmask);
+ snmp_insert_iprteidx_tree(0, netif);
+ LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: netmask of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ netif->name[0], netif->name[1],
+ ip4_addr1_16(&netif->netmask),
+ ip4_addr2_16(&netif->netmask),
+ ip4_addr3_16(&netif->netmask),
+ ip4_addr4_16(&netif->netmask)));
+}
+
+/**
+ * Set a network interface as the default network interface
+ * (used to output all packets for which no specific route is found)
+ *
+ * @param netif the default network interface
+ */
+void
+netif_set_default(struct netif *netif)
+{
+ if (netif == NULL) {
+ /* remove default route */
+ snmp_delete_iprteidx_tree(1, netif);
+ } else {
+ /* install default route */
+ snmp_insert_iprteidx_tree(1, netif);
+ }
+ netif_default = netif;
+ LWIP_DEBUGF(NETIF_DEBUG, ("netif: setting default interface %c%c\n",
+ netif ? netif->name[0] : '\'', netif ? netif->name[1] : '\''));
+}
+
+/**
+ * Bring an interface up, available for processing
+ * traffic.
+ *
+ * @note: Enabling DHCP on a down interface will make it come
+ * up once configured.
+ *
+ * @see dhcp_start()
+ */
+void netif_set_up(struct netif *netif)
+{
+ if (!(netif->flags & NETIF_FLAG_UP)) {
+ netif->flags |= NETIF_FLAG_UP;
+
+#if LWIP_SNMP
+ snmp_get_sysuptime(&netif->ts);
+#endif /* LWIP_SNMP */
+
+ NETIF_STATUS_CALLBACK(netif);
+
+ if (netif->flags & NETIF_FLAG_LINK_UP) {
+#if LWIP_ARP
+ /* For Ethernet network interfaces, we would like to send a "gratuitous ARP" */
+ if (netif->flags & (NETIF_FLAG_ETHARP)) {
+ etharp_gratuitous(netif);
+ }
+#endif /* LWIP_ARP */
+
+#if LWIP_IGMP
+ /* resend IGMP memberships */
+ if (netif->flags & NETIF_FLAG_IGMP) {
+ igmp_report_groups( netif);
+ }
+#endif /* LWIP_IGMP */
+ }
+ }
+}
+
+/**
+ * Bring an interface down, disabling any traffic processing.
+ *
+ * @note: Enabling DHCP on a down interface will make it come
+ * up once configured.
+ *
+ * @see dhcp_start()
+ */
+void netif_set_down(struct netif *netif)
+{
+ if (netif->flags & NETIF_FLAG_UP) {
+ netif->flags &= ~NETIF_FLAG_UP;
+#if LWIP_SNMP
+ snmp_get_sysuptime(&netif->ts);
+#endif
+
+#if LWIP_ARP
+ if (netif->flags & NETIF_FLAG_ETHARP) {
+ etharp_cleanup_netif(netif);
+ }
+#endif /* LWIP_ARP */
+ NETIF_STATUS_CALLBACK(netif);
+ }
+}
+
+#if LWIP_NETIF_STATUS_CALLBACK
+/**
+ * Set callback to be called when interface is brought up/down
+ */
+void netif_set_status_callback(struct netif *netif, netif_status_callback_fn status_callback)
+{
+ if (netif) {
+ netif->status_callback = status_callback;
+ }
+}
+#endif /* LWIP_NETIF_STATUS_CALLBACK */
+
+/**
+ * Called by a driver when its link goes up
+ */
+void netif_set_link_up(struct netif *netif )
+{
+ if (!(netif->flags & NETIF_FLAG_LINK_UP)) {
+ netif->flags |= NETIF_FLAG_LINK_UP;
+
+#if LWIP_DHCP
+ if (netif->dhcp) {
+ dhcp_network_changed(netif);
+ }
+#endif /* LWIP_DHCP */
+
+#if LWIP_AUTOIP
+ if (netif->autoip) {
+ autoip_network_changed(netif);
+ }
+#endif /* LWIP_AUTOIP */
+
+ if (netif->flags & NETIF_FLAG_UP) {
+#if LWIP_ARP
+ /* For Ethernet network interfaces, we would like to send a "gratuitous ARP" */
+ if (netif->flags & NETIF_FLAG_ETHARP) {
+ etharp_gratuitous(netif);
+ }
+#endif /* LWIP_ARP */
+
+#if LWIP_IGMP
+ /* resend IGMP memberships */
+ if (netif->flags & NETIF_FLAG_IGMP) {
+ igmp_report_groups( netif);
+ }
+#endif /* LWIP_IGMP */
+ }
+ NETIF_LINK_CALLBACK(netif);
+ }
+}
+
+/**
+ * Called by a driver when its link goes down
+ */
+void netif_set_link_down(struct netif *netif )
+{
+ if (netif->flags & NETIF_FLAG_LINK_UP) {
+ netif->flags &= ~NETIF_FLAG_LINK_UP;
+ NETIF_LINK_CALLBACK(netif);
+ }
+}
+
+#if LWIP_NETIF_LINK_CALLBACK
+/**
+ * Set callback to be called when link is brought up/down
+ */
+void netif_set_link_callback(struct netif *netif, netif_status_callback_fn link_callback)
+{
+ if (netif) {
+ netif->link_callback = link_callback;
+ }
+}
+#endif /* LWIP_NETIF_LINK_CALLBACK */
+
+#if ENABLE_LOOPBACK
+/**
+ * Send an IP packet to be received on the same netif (loopif-like).
+ * The pbuf is simply copied and handed back to netif->input.
+ * In multithreaded mode, this is done directly since netif->input must put
+ * the packet on a queue.
+ * In callback mode, the packet is put on an internal queue and is fed to
+ * netif->input by netif_poll().
+ *
+ * @param netif the lwip network interface structure
+ * @param p the (IP) packet to 'send'
+ * @param ipaddr the ip address to send the packet to (not used)
+ * @return ERR_OK if the packet has been sent
+ * ERR_MEM if the pbuf used to copy the packet couldn't be allocated
+ */
+err_t
+netif_loop_output(struct netif *netif, struct pbuf *p,
+ ip_addr_t *ipaddr)
+{
+ struct pbuf *r;
+ err_t err;
+ struct pbuf *last;
+#if LWIP_LOOPBACK_MAX_PBUFS
+ u8_t clen = 0;
+#endif /* LWIP_LOOPBACK_MAX_PBUFS */
+ /* If we have a loopif, SNMP counters are adjusted for it,
+ * if not they are adjusted for 'netif'. */
+#if LWIP_SNMP
+#if LWIP_HAVE_LOOPIF
+ struct netif *stats_if = &loop_netif;
+#else /* LWIP_HAVE_LOOPIF */
+ struct netif *stats_if = netif;
+#endif /* LWIP_HAVE_LOOPIF */
+#endif /* LWIP_SNMP */
+ SYS_ARCH_DECL_PROTECT(lev);
+ LWIP_UNUSED_ARG(ipaddr);
+
+ /* Allocate a new pbuf */
+ r = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM);
+ if (r == NULL) {
+ LINK_STATS_INC(link.memerr);
+ LINK_STATS_INC(link.drop);
+ snmp_inc_ifoutdiscards(stats_if);
+ return ERR_MEM;
+ }
+#if LWIP_LOOPBACK_MAX_PBUFS
+ clen = pbuf_clen(r);
+ /* check for overflow or too many pbuf on queue */
+ if(((netif->loop_cnt_current + clen) < netif->loop_cnt_current) ||
+ ((netif->loop_cnt_current + clen) > LWIP_LOOPBACK_MAX_PBUFS)) {
+ pbuf_free(r);
+ LINK_STATS_INC(link.memerr);
+ LINK_STATS_INC(link.drop);
+ snmp_inc_ifoutdiscards(stats_if);
+ return ERR_MEM;
+ }
+ netif->loop_cnt_current += clen;
+#endif /* LWIP_LOOPBACK_MAX_PBUFS */
+
+ /* Copy the whole pbuf queue p into the single pbuf r */
+ if ((err = pbuf_copy(r, p)) != ERR_OK) {
+ pbuf_free(r);
+ LINK_STATS_INC(link.memerr);
+ LINK_STATS_INC(link.drop);
+ snmp_inc_ifoutdiscards(stats_if);
+ return err;
+ }
+
+ /* Put the packet on a linked list which gets emptied through calling
+ netif_poll(). */
+
+ /* let last point to the last pbuf in chain r */
+ for (last = r; last->next != NULL; last = last->next);
+
+ SYS_ARCH_PROTECT(lev);
+ if(netif->loop_first != NULL) {
+ LWIP_ASSERT("if first != NULL, last must also be != NULL", netif->loop_last != NULL);
+ netif->loop_last->next = r;
+ netif->loop_last = last;
+ } else {
+ netif->loop_first = r;
+ netif->loop_last = last;
+ }
+ SYS_ARCH_UNPROTECT(lev);
+
+ LINK_STATS_INC(link.xmit);
+ snmp_add_ifoutoctets(stats_if, p->tot_len);
+ snmp_inc_ifoutucastpkts(stats_if);
+
+#if LWIP_NETIF_LOOPBACK_MULTITHREADING
+ /* For multithreading environment, schedule a call to netif_poll */
+ tcpip_callback((tcpip_callback_fn)netif_poll, netif);
+#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */
+
+ return ERR_OK;
+}
+
+/**
+ * Call netif_poll() in the main loop of your application. This is to prevent
+ * reentering non-reentrant functions like tcp_input(). Packets passed to
+ * netif_loop_output() are put on a list that is passed to netif->input() by
+ * netif_poll().
+ */
+void
+netif_poll(struct netif *netif)
+{
+ struct pbuf *in;
+ /* If we have a loopif, SNMP counters are adjusted for it,
+ * if not they are adjusted for 'netif'. */
+#if LWIP_SNMP
+#if LWIP_HAVE_LOOPIF
+ struct netif *stats_if = &loop_netif;
+#else /* LWIP_HAVE_LOOPIF */
+ struct netif *stats_if = netif;
+#endif /* LWIP_HAVE_LOOPIF */
+#endif /* LWIP_SNMP */
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ do {
+ /* Get a packet from the list. With SYS_LIGHTWEIGHT_PROT=1, this is protected */
+ SYS_ARCH_PROTECT(lev);
+ in = netif->loop_first;
+ if (in != NULL) {
+ struct pbuf *in_end = in;
+#if LWIP_LOOPBACK_MAX_PBUFS
+ u8_t clen = pbuf_clen(in);
+ /* adjust the number of pbufs on queue */
+ LWIP_ASSERT("netif->loop_cnt_current underflow",
+ ((netif->loop_cnt_current - clen) < netif->loop_cnt_current));
+ netif->loop_cnt_current -= clen;
+#endif /* LWIP_LOOPBACK_MAX_PBUFS */
+ while (in_end->len != in_end->tot_len) {
+ LWIP_ASSERT("bogus pbuf: len != tot_len but next == NULL!", in_end->next != NULL);
+ in_end = in_end->next;
+ }
+ /* 'in_end' now points to the last pbuf from 'in' */
+ if (in_end == netif->loop_last) {
+ /* this was the last pbuf in the list */
+ netif->loop_first = netif->loop_last = NULL;
+ } else {
+ /* pop the pbuf off the list */
+ netif->loop_first = in_end->next;
+ LWIP_ASSERT("should not be null since first != last!", netif->loop_first != NULL);
+ }
+ /* De-queue the pbuf from its successors on the 'loop_' list. */
+ in_end->next = NULL;
+ }
+ SYS_ARCH_UNPROTECT(lev);
+
+ if (in != NULL) {
+ LINK_STATS_INC(link.recv);
+ snmp_add_ifinoctets(stats_if, in->tot_len);
+ snmp_inc_ifinucastpkts(stats_if);
+ /* loopback packets are always IP packets! */
+ if (ip_input(in, netif) != ERR_OK) {
+ pbuf_free(in);
+ }
+ /* Don't reference the packet any more! */
+ in = NULL;
+ }
+ /* go on while there is a packet on the list */
+ } while (netif->loop_first != NULL);
+}
+
+#if !LWIP_NETIF_LOOPBACK_MULTITHREADING
+/**
+ * Calls netif_poll() for every netif on the netif_list.
+ */
+void
+netif_poll_all(void)
+{
+ struct netif *netif = netif_list;
+ /* loop through netifs */
+ while (netif != NULL) {
+ netif_poll(netif);
+ /* proceed to next network interface */
+ netif = netif->next;
+ }
+}
+#endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */
+#endif /* ENABLE_LOOPBACK */
diff --git a/app/lwip/core/pbuf.c b/app/lwip/core/pbuf.c
new file mode 100644
index 00000000..e30c0c17
--- /dev/null
+++ b/app/lwip/core/pbuf.c
@@ -0,0 +1,1212 @@
+/**
+ * @file
+ * Packet buffer management
+ *
+ * Packets are built from the pbuf data structure. It supports dynamic
+ * memory allocation for packet contents or can reference externally
+ * managed packet contents both in RAM and ROM. Quick allocation for
+ * incoming packets is provided through pools with fixed sized pbufs.
+ *
+ * A packet may span over multiple pbufs, chained as a singly linked
+ * list. This is called a "pbuf chain".
+ *
+ * Multiple packets may be queued, also using this singly linked list.
+ * This is called a "packet queue".
+ *
+ * So, a packet queue consists of one or more pbuf chains, each of
+ * which consist of one or more pbufs. CURRENTLY, PACKET QUEUES ARE
+ * NOT SUPPORTED!!! Use helper structs to queue multiple packets.
+ *
+ * The differences between a pbuf chain and a packet queue are very
+ * precise but subtle.
+ *
+ * The last pbuf of a packet has a ->tot_len field that equals the
+ * ->len field. It can be found by traversing the list. If the last
+ * pbuf of a packet has a ->next field other than NULL, more packets
+ * are on the queue.
+ *
+ * Therefore, looping through a pbuf of a single packet, has an
+ * loop end condition (tot_len == p->len), NOT (next == NULL).
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/stats.h"
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/pbuf.h"
+#include "lwip/sys.h"
+#include "arch/perf.h"
+#if TCP_QUEUE_OOSEQ
+#include "lwip/tcp_impl.h"
+#endif
+#if LWIP_CHECKSUM_ON_COPY
+#include "lwip/inet_chksum.h"
+#endif
+
+#include
+
+#ifdef EBUF_LWIP
+#include "pp/esf_buf.h"
+#else
+#define EP_OFFSET 0
+#endif /* ESF_LWIP */
+
+#define SIZEOF_STRUCT_PBUF LWIP_MEM_ALIGN_SIZE(sizeof(struct pbuf))
+/* Since the pool is created in memp, PBUF_POOL_BUFSIZE will be automatically
+ aligned there. Therefore, PBUF_POOL_BUFSIZE_ALIGNED can be used here. */
+#define PBUF_POOL_BUFSIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(PBUF_POOL_BUFSIZE)
+
+#if !LWIP_TCP || !TCP_QUEUE_OOSEQ || NO_SYS
+#define PBUF_POOL_IS_EMPTY()
+#else /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || NO_SYS */
+/** Define this to 0 to prevent freeing ooseq pbufs when the PBUF_POOL is empty */
+#ifndef PBUF_POOL_FREE_OOSEQ
+#define PBUF_POOL_FREE_OOSEQ 1
+#endif /* PBUF_POOL_FREE_OOSEQ */
+
+#if PBUF_POOL_FREE_OOSEQ
+#include "lwip/tcpip.h"
+#define PBUF_POOL_IS_EMPTY() pbuf_pool_is_empty()
+static u8_t pbuf_free_ooseq_queued;
+/**
+ * Attempt to reclaim some memory from queued out-of-sequence TCP segments
+ * if we run out of pool pbufs. It's better to give priority to new packets
+ * if we're running out.
+ *
+ * This must be done in the correct thread context therefore this function
+ * can only be used with NO_SYS=0 and through tcpip_callback.
+ */
+static void ICACHE_FLASH_ATTR
+pbuf_free_ooseq(void* arg)
+{
+ struct tcp_pcb* pcb;
+ SYS_ARCH_DECL_PROTECT(old_level);
+ LWIP_UNUSED_ARG(arg);
+
+ SYS_ARCH_PROTECT(old_level);
+ pbuf_free_ooseq_queued = 0;
+ SYS_ARCH_UNPROTECT(old_level);
+
+ for (pcb = tcp_active_pcbs; NULL != pcb; pcb = pcb->next) {
+ if (NULL != pcb->ooseq) {
+ /** Free the ooseq pbufs of one PCB only */
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free_ooseq: freeing out-of-sequence pbufs\n"));
+ tcp_segs_free(pcb->ooseq);
+ pcb->ooseq = NULL;
+ return;
+ }
+ }
+}
+
+/** Queue a call to pbuf_free_ooseq if not already queued. */
+static void ICACHE_FLASH_ATTR
+pbuf_pool_is_empty(void)
+{
+ u8_t queued;
+ SYS_ARCH_DECL_PROTECT(old_level);
+
+ SYS_ARCH_PROTECT(old_level);
+ queued = pbuf_free_ooseq_queued;
+ pbuf_free_ooseq_queued = 1;
+ SYS_ARCH_UNPROTECT(old_level);
+
+ if(!queued) {
+ /* queue a call to pbuf_free_ooseq if not already queued */
+ if(tcpip_callback_with_block(pbuf_free_ooseq, NULL, 0) != ERR_OK) {
+ SYS_ARCH_PROTECT(old_level);
+ pbuf_free_ooseq_queued = 0;
+ SYS_ARCH_UNPROTECT(old_level);
+ }
+ }
+}
+#endif /* PBUF_POOL_FREE_OOSEQ */
+#endif /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || NO_SYS */
+
+/**
+ * Allocates a pbuf of the given type (possibly a chain for PBUF_POOL type).
+ *
+ * The actual memory allocated for the pbuf is determined by the
+ * layer at which the pbuf is allocated and the requested size
+ * (from the size parameter).
+ *
+ * @param layer flag to define header size
+ * @param length size of the pbuf's payload
+ * @param type this parameter decides how and where the pbuf
+ * should be allocated as follows:
+ *
+ * - PBUF_RAM: buffer memory for pbuf is allocated as one large
+ * chunk. This includes protocol headers as well.
+ * - PBUF_ROM: no buffer memory is allocated for the pbuf, even for
+ * protocol headers. Additional headers must be prepended
+ * by allocating another pbuf and chain in to the front of
+ * the ROM pbuf. It is assumed that the memory used is really
+ * similar to ROM in that it is immutable and will not be
+ * changed. Memory which is dynamic should generally not
+ * be attached to PBUF_ROM pbufs. Use PBUF_REF instead.
+ * - PBUF_REF: no buffer memory is allocated for the pbuf, even for
+ * protocol headers. It is assumed that the pbuf is only
+ * being used in a single thread. If the pbuf gets queued,
+ * then pbuf_take should be called to copy the buffer.
+ * - PBUF_POOL: the pbuf is allocated as a pbuf chain, with pbufs from
+ * the pbuf pool that is allocated during pbuf_init().
+ *
+ * @return the allocated pbuf. If multiple pbufs where allocated, this
+ * is the first pbuf of a pbuf chain.
+ */
+struct pbuf *
+pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type)
+{
+ struct pbuf *p, *q, *r;
+ u16_t offset;
+ s32_t rem_len; /* remaining length */
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F")\n", length));
+
+ /* determine header offset */
+ offset = 0;
+ switch (layer) {
+ case PBUF_TRANSPORT:
+ /* add room for transport (often TCP) layer header */
+ offset += PBUF_TRANSPORT_HLEN;
+ /* FALLTHROUGH */
+ case PBUF_IP:
+ /* add room for IP layer header */
+ offset += PBUF_IP_HLEN;
+ /* FALLTHROUGH */
+ case PBUF_LINK:
+ /* add room for link layer header */
+ offset += PBUF_LINK_HLEN;
+
+#ifdef PBUF_RSV_FOR_WLAN
+ /*
+ * 1. LINK_HLEN 14Byte will be remove in WLAN layer
+ * 2. IEEE80211_HDR_MAX_LEN needs 40 bytes.
+ * 3. encryption needs exra 4 bytes ahead of actual data payload, and require
+ * DAddr and SAddr to be 4-byte aligned.
+ * 4. TRANSPORT and IP are all 20, 4 bytes aligned, nice...
+ * 5. LCC add 6 bytes more, We don't consider WAPI yet...
+ * 6. define LWIP_MEM_ALIGN to be 4 Byte aligned, pbuf struct is 16B, Only thing may be
+ * matter is ether_hdr is not 4B aligned.
+ *
+ * So, we need extra (40 + 4 - 14) = 30 and it's happen to be 4-Byte aligned
+ *
+ * 1. lwip
+ * | empty 30B | eth_hdr (14B) | payload ...|
+ * total: 44B ahead payload
+ * 2. net80211
+ * | max 80211 hdr, 32B | ccmp/tkip iv (8B) | sec rsv(4B) | payload ...|
+ * total: 40B ahead sec_rsv and 44B ahead payload
+ *
+ */
+ offset += EP_OFFSET; //remove LINK hdr in wlan
+#endif /* PBUF_RSV_FOR_WLAN */
+
+ break;
+ case PBUF_RAW:
+#ifdef PBUF_RSV_FOR_WLAN
+ /*
+ * RAW pbuf suppose
+ */
+ offset += EP_OFFSET; //remove LINK hdr in wlan
+#endif /* PBUF_RAW */
+ break;
+ default:
+ LWIP_ASSERT("pbuf_alloc: bad pbuf layer", 0);
+ return NULL;
+ }
+
+ switch (type) {
+ case PBUF_POOL:
+ /* allocate head of pbuf chain into p */
+ p = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL);
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc: allocated pbuf %p\n", (void *)p));
+ if (p == NULL) {
+ PBUF_POOL_IS_EMPTY();
+ return NULL;
+ }
+ p->type = type;
+ p->next = NULL;
+
+ /* make the payload pointer point 'offset' bytes into pbuf data memory */
+ p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + (SIZEOF_STRUCT_PBUF + offset)));
+ LWIP_ASSERT("pbuf_alloc: pbuf p->payload properly aligned",
+ ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0);
+ /* the total length of the pbuf chain is the requested size */
+ p->tot_len = length;
+ /* set the length of the first pbuf in the chain */
+ p->len = LWIP_MIN(length, PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset));
+ LWIP_ASSERT("check p->payload + p->len does not overflow pbuf",
+ ((u8_t*)p->payload + p->len <=
+ (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED));
+ LWIP_ASSERT("PBUF_POOL_BUFSIZE must be bigger than MEM_ALIGNMENT",
+ (PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)) > 0 );
+ /* set reference count (needed here in case we fail) */
+ p->ref = 1;
+
+ /* now allocate the tail of the pbuf chain */
+
+ /* remember first pbuf for linkage in next iteration */
+ r = p;
+ /* remaining length to be allocated */
+ rem_len = length - p->len;
+ /* any remaining pbufs to be allocated? */
+ while (rem_len > 0) {
+ q = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL);
+ if (q == NULL) {
+ PBUF_POOL_IS_EMPTY();
+ /* free chain so far allocated */
+ pbuf_free(p);
+ /* bail out unsuccesfully */
+ return NULL;
+ }
+ q->type = type;
+ q->flags = 0;
+ q->next = NULL;
+ /* make previous pbuf point to this pbuf */
+ r->next = q;
+ /* set total length of this pbuf and next in chain */
+ LWIP_ASSERT("rem_len < max_u16_t", rem_len < 0xffff);
+ q->tot_len = (u16_t)rem_len;
+ /* this pbuf length is pool size, unless smaller sized tail */
+ q->len = LWIP_MIN((u16_t)rem_len, PBUF_POOL_BUFSIZE_ALIGNED);
+ q->payload = (void *)((u8_t *)q + SIZEOF_STRUCT_PBUF);
+ LWIP_ASSERT("pbuf_alloc: pbuf q->payload properly aligned",
+ ((mem_ptr_t)q->payload % MEM_ALIGNMENT) == 0);
+ LWIP_ASSERT("check p->payload + p->len does not overflow pbuf",
+ ((u8_t*)p->payload + p->len <=
+ (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED));
+ q->ref = 1;
+ /* calculate remaining length to be allocated */
+ rem_len -= q->len;
+ /* remember this pbuf for linkage in next iteration */
+ r = q;
+ }
+ /* end of chain */
+ /*r->next = NULL;*/
+
+ break;
+ case PBUF_RAM:
+ /* If pbuf is to be allocated in RAM, allocate memory for it. */
+ p = (struct pbuf*)mem_malloc(LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF + offset) + LWIP_MEM_ALIGN_SIZE(length));
+ if (p == NULL) {
+ return NULL;
+ }
+ /* Set up internal structure of the pbuf. */
+ p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + SIZEOF_STRUCT_PBUF + offset));
+ p->len = p->tot_len = length;
+ p->next = NULL;
+ p->type = type;
+
+ LWIP_ASSERT("pbuf_alloc: pbuf->payload properly aligned",
+ ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0);
+ break;
+#ifdef EBUF_LWIP
+ case PBUF_ESF_RX:
+#endif /* ESF_LWIP */
+ /* pbuf references existing (non-volatile static constant) ROM payload? */
+ case PBUF_ROM:
+ /* pbuf references existing (externally allocated) RAM payload? */
+ case PBUF_REF:
+ /* only allocate memory for the pbuf structure */
+ p = (struct pbuf *)memp_malloc(MEMP_PBUF);
+ if (p == NULL) {
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("pbuf_alloc: Could not allocate MEMP_PBUF for PBUF_%s.\n",
+ (type == PBUF_ROM) ? "ROM" : "REF"));
+ return NULL;
+ }
+ /* caller must set this field properly, afterwards */
+ p->payload = NULL;
+ p->len = p->tot_len = length;
+ p->next = NULL;
+ p->type = type;
+ break;
+ default:
+ LWIP_ASSERT("pbuf_alloc: erroneous type", 0);
+ return NULL;
+ }
+ /* set reference count */
+ p->ref = 1;
+ /* set flags */
+ p->flags = 0;
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F") == %p\n", length, (void *)p));
+ return p;
+}
+
+#if LWIP_SUPPORT_CUSTOM_PBUF
+/** Initialize a custom pbuf (already allocated).
+ *
+ * @param layer flag to define header size
+ * @param length size of the pbuf's payload
+ * @param type type of the pbuf (only used to treat the pbuf accordingly, as
+ * this function allocates no memory)
+ * @param p pointer to the custom pbuf to initialize (already allocated)
+ * @param payload_mem pointer to the buffer that is used for payload and headers,
+ * must be at least big enough to hold 'length' plus the header size,
+ * may be NULL if set later
+ * @param payload_mem_len the size of the 'payload_mem' buffer, must be at least
+ * big enough to hold 'length' plus the header size
+ */
+struct pbuf*
+pbuf_alloced_custom(pbuf_layer l, u16_t length, pbuf_type type, struct pbuf_custom *p,
+ void *payload_mem, u16_t payload_mem_len)
+{
+ u16_t offset;
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloced_custom(length=%"U16_F")\n", length));
+
+ /* determine header offset */
+ offset = 0;
+ switch (l) {
+ case PBUF_TRANSPORT:
+ /* add room for transport (often TCP) layer header */
+ offset += PBUF_TRANSPORT_HLEN;
+ /* FALLTHROUGH */
+ case PBUF_IP:
+ /* add room for IP layer header */
+ offset += PBUF_IP_HLEN;
+ /* FALLTHROUGH */
+ case PBUF_LINK:
+ /* add room for link layer header */
+ offset += PBUF_LINK_HLEN;
+ break;
+ case PBUF_RAW:
+ break;
+ default:
+ LWIP_ASSERT("pbuf_alloced_custom: bad pbuf layer", 0);
+ return NULL;
+ }
+
+ if (LWIP_MEM_ALIGN_SIZE(offset) + length < payload_mem_len) {
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_WARNING, ("pbuf_alloced_custom(length=%"U16_F") buffer too short\n", length));
+ return NULL;
+ }
+
+ p->pbuf.next = NULL;
+ if (payload_mem != NULL) {
+ p->pbuf.payload = LWIP_MEM_ALIGN((void *)((u8_t *)payload_mem + offset));
+ } else {
+ p->pbuf.payload = NULL;
+ }
+ p->pbuf.flags = PBUF_FLAG_IS_CUSTOM;
+ p->pbuf.len = p->pbuf.tot_len = length;
+ p->pbuf.type = type;
+ p->pbuf.ref = 1;
+ return &p->pbuf;
+}
+#endif /* LWIP_SUPPORT_CUSTOM_PBUF */
+
+/**
+ * Shrink a pbuf chain to a desired length.
+ *
+ * @param p pbuf to shrink.
+ * @param new_len desired new length of pbuf chain
+ *
+ * Depending on the desired length, the first few pbufs in a chain might
+ * be skipped and left unchanged. The new last pbuf in the chain will be
+ * resized, and any remaining pbufs will be freed.
+ *
+ * @note If the pbuf is ROM/REF, only the ->tot_len and ->len fields are adjusted.
+ * @note May not be called on a packet queue.
+ *
+ * @note Despite its name, pbuf_realloc cannot grow the size of a pbuf (chain).
+ */
+void
+pbuf_realloc(struct pbuf *p, u16_t new_len)
+{
+ struct pbuf *q;
+ u16_t rem_len; /* remaining length */
+ s32_t grow;
+
+ LWIP_ASSERT("pbuf_realloc: p != NULL", p != NULL);
+ LWIP_ASSERT("pbuf_realloc: sane p->type", p->type == PBUF_POOL ||
+ p->type == PBUF_ROM ||
+ p->type == PBUF_RAM ||
+ p->type == PBUF_REF);
+
+ /* desired length larger than current length? */
+ if (new_len >= p->tot_len) {
+ /* enlarging not yet supported */
+ return;
+ }
+
+ /* the pbuf chain grows by (new_len - p->tot_len) bytes
+ * (which may be negative in case of shrinking) */
+ grow = new_len - p->tot_len;
+
+ /* first, step over any pbufs that should remain in the chain */
+ rem_len = new_len;
+ q = p;
+ /* should this pbuf be kept? */
+ while (rem_len > q->len) {
+ /* decrease remaining length by pbuf length */
+ rem_len -= q->len;
+ /* decrease total length indicator */
+ LWIP_ASSERT("grow < max_u16_t", grow < 0xffff);
+ q->tot_len += (u16_t)grow;
+ /* proceed to next pbuf in chain */
+ q = q->next;
+ LWIP_ASSERT("pbuf_realloc: q != NULL", q != NULL);
+ }
+ /* we have now reached the new last pbuf (in q) */
+ /* rem_len == desired length for pbuf q */
+
+ /* shrink allocated memory for PBUF_RAM */
+ /* (other types merely adjust their length fields */
+ if ((q->type == PBUF_RAM) && (rem_len != q->len)) {
+ /* reallocate and adjust the length of the pbuf that will be split */
+ q = (struct pbuf *)mem_trim(q, (u16_t)((u8_t *)q->payload - (u8_t *)q) + rem_len);
+ LWIP_ASSERT("mem_trim returned q == NULL", q != NULL);
+ }
+ /* adjust length fields for new last pbuf */
+ q->len = rem_len;
+ q->tot_len = q->len;
+
+ /* any remaining pbufs in chain? */
+ if (q->next != NULL) {
+ /* free remaining pbufs in chain */
+ pbuf_free(q->next);
+ }
+ /* q is last packet in chain */
+ q->next = NULL;
+
+}
+
+/**
+ * Adjusts the payload pointer to hide or reveal headers in the payload.
+ *
+ * Adjusts the ->payload pointer so that space for a header
+ * (dis)appears in the pbuf payload.
+ *
+ * The ->payload, ->tot_len and ->len fields are adjusted.
+ *
+ * @param p pbuf to change the header size.
+ * @param header_size_increment Number of bytes to increment header size which
+ * increases the size of the pbuf. New space is on the front.
+ * (Using a negative value decreases the header size.)
+ * If hdr_size_inc is 0, this function does nothing and returns succesful.
+ *
+ * PBUF_ROM and PBUF_REF type buffers cannot have their sizes increased, so
+ * the call will fail. A check is made that the increase in header size does
+ * not move the payload pointer in front of the start of the buffer.
+ * @return non-zero on failure, zero on success.
+ *
+ */
+u8_t
+pbuf_header(struct pbuf *p, s16_t header_size_increment)
+{
+ u16_t type;
+ void *payload;
+ u16_t increment_magnitude;
+
+ LWIP_ASSERT("p != NULL", p != NULL);
+ if ((header_size_increment == 0) || (p == NULL)) {
+ return 0;
+ }
+
+ if (header_size_increment < 0){
+ increment_magnitude = -header_size_increment;
+ /* Check that we aren't going to move off the end of the pbuf */
+ LWIP_ERROR("increment_magnitude <= p->len", (increment_magnitude <= p->len), return 1;);
+ } else {
+ increment_magnitude = header_size_increment;
+#if 0
+ /* Can't assert these as some callers speculatively call
+ pbuf_header() to see if it's OK. Will return 1 below instead. */
+ /* Check that we've got the correct type of pbuf to work with */
+ LWIP_ASSERT("p->type == PBUF_RAM || p->type == PBUF_POOL",
+ p->type == PBUF_RAM || p->type == PBUF_POOL);
+ /* Check that we aren't going to move off the beginning of the pbuf */
+ LWIP_ASSERT("p->payload - increment_magnitude >= p + SIZEOF_STRUCT_PBUF",
+ (u8_t *)p->payload - increment_magnitude >= (u8_t *)p + SIZEOF_STRUCT_PBUF);
+#endif
+ }
+
+ type = p->type;
+ /* remember current payload pointer */
+ payload = p->payload;
+
+ /* pbuf types containing payloads? */
+ if (type == PBUF_RAM || type == PBUF_POOL) {
+ /* set new payload pointer */
+ p->payload = (u8_t *)p->payload - header_size_increment;
+ /* boundary check fails? */
+ if ((u8_t *)p->payload < (u8_t *)p + SIZEOF_STRUCT_PBUF + EP_OFFSET) {
+ LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("pbuf_header: failed as %p < %p (not enough space for new header size)\n",
+ (void *)p->payload, (void *)(p + 1)));
+ /* restore old payload pointer */
+ p->payload = payload;
+ /* bail out unsuccesfully */
+ return 1;
+ }
+ /* pbuf types refering to external payloads? */
+ } else if (type == PBUF_REF || type == PBUF_ROM) {
+ /* hide a header in the payload? */
+ if ((header_size_increment < 0) && (increment_magnitude <= p->len)) {
+ /* increase payload pointer */
+ p->payload = (u8_t *)p->payload - header_size_increment;
+ } else {
+ /* cannot expand payload to front (yet!)
+ * bail out unsuccesfully */
+ if (type == PBUF_REF) {
+ /* increase payload pointer */
+ p->payload = (u8_t *)p->payload - header_size_increment;
+ } else {
+ return 1;
+ }
+ }
+ } else {
+ /* Unknown type */
+ LWIP_ASSERT("bad pbuf type", 0);
+ return 1;
+ }
+ /* modify pbuf length fields */
+ p->len += header_size_increment;
+ p->tot_len += header_size_increment;
+
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_header: old %p new %p (%"S16_F")\n",
+ (void *)payload, (void *)p->payload, header_size_increment));
+
+ return 0;
+}
+
+/**
+ * Dereference a pbuf chain or queue and deallocate any no-longer-used
+ * pbufs at the head of this chain or queue.
+ *
+ * Decrements the pbuf reference count. If it reaches zero, the pbuf is
+ * deallocated.
+ *
+ * For a pbuf chain, this is repeated for each pbuf in the chain,
+ * up to the first pbuf which has a non-zero reference count after
+ * decrementing. So, when all reference counts are one, the whole
+ * chain is free'd.
+ *
+ * @param p The pbuf (chain) to be dereferenced.
+ *
+ * @return the number of pbufs that were de-allocated
+ * from the head of the chain.
+ *
+ * @note MUST NOT be called on a packet queue (Not verified to work yet).
+ * @note the reference counter of a pbuf equals the number of pointers
+ * that refer to the pbuf (or into the pbuf).
+ *
+ * @internal examples:
+ *
+ * Assuming existing chains a->b->c with the following reference
+ * counts, calling pbuf_free(a) results in:
+ *
+ * 1->2->3 becomes ...1->3
+ * 3->3->3 becomes 2->3->3
+ * 1->1->2 becomes ......1
+ * 2->1->1 becomes 1->1->1
+ * 1->1->1 becomes .......
+ *
+ */
+u8_t
+pbuf_free(struct pbuf *p)
+{
+ u16_t type;
+ struct pbuf *q;
+ u8_t count;
+
+ if (p == NULL) {
+ LWIP_ASSERT("p != NULL", p != NULL);
+ /* if assertions are disabled, proceed with debug output */
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("pbuf_free(p == NULL) was called.\n"));
+ return 0;
+ }
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free(%p)\n", (void *)p));
+
+ PERF_START;
+
+ LWIP_ASSERT("pbuf_free: sane type",
+ p->type == PBUF_RAM || p->type == PBUF_ROM ||
+ p->type == PBUF_REF || p->type == PBUF_POOL
+#ifdef EBUF_LWIP
+ || p->type == PBUF_ESF_RX
+#endif //EBUF_LWIP
+ );
+
+ count = 0;
+ /* de-allocate all consecutive pbufs from the head of the chain that
+ * obtain a zero reference count after decrementing*/
+ while (p != NULL) {
+ u16_t ref;
+ SYS_ARCH_DECL_PROTECT(old_level);
+ /* Since decrementing ref cannot be guaranteed to be a single machine operation
+ * we must protect it. We put the new ref into a local variable to prevent
+ * further protection. */
+ SYS_ARCH_PROTECT(old_level);
+ /* all pbufs in a chain are referenced at least once */
+ LWIP_ASSERT("pbuf_free: p->ref > 0", p->ref > 0);
+ /* decrease reference count (number of pointers to pbuf) */
+ ref = --(p->ref);
+ SYS_ARCH_UNPROTECT(old_level);
+ /* this pbuf is no longer referenced to? */
+ if (ref == 0) {
+ /* remember next pbuf in chain for next iteration */
+ q = p->next;
+ LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free: deallocating %p\n", (void *)p));
+ type = p->type;
+#if LWIP_SUPPORT_CUSTOM_PBUF
+ /* is this a custom pbuf? */
+ if ((p->flags & PBUF_FLAG_IS_CUSTOM) != 0) {
+ struct pbuf_custom *pc = (struct pbuf_custom*)p;
+ LWIP_ASSERT("pc->custom_free_function != NULL", pc->custom_free_function != NULL);
+ pc->custom_free_function(p);
+ } else
+#endif /* LWIP_SUPPORT_CUSTOM_PBUF */
+ {
+ /* is this a pbuf from the pool? */
+ if (type == PBUF_POOL) {
+ memp_free(MEMP_PBUF_POOL, p);
+ /* is this a ROM or RAM referencing pbuf? */
+ } else if (type == PBUF_ROM || type == PBUF_REF
+#ifdef EBUF_LWIP
+ || type == PBUF_ESF_RX
+#endif //EBUF_LWIP
+ ) {
+#ifdef EBUF_LWIP
+ system_pp_recycle_rx_pkt(p->eb);
+#endif //EBUF_LWIP
+ memp_free(MEMP_PBUF, p);
+ /* type == PBUF_RAM */
+ } else {
+ mem_free(p);
+ }
+ }
+ count++;
+ /* proceed to next pbuf */
+ p = q;
+ /* p->ref > 0, this pbuf is still referenced to */
+ /* (and so the remaining pbufs in chain as well) */
+ } else {
+ LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free: %p has ref %"U16_F", ending here.\n", (void *)p, ref));
+ /* stop walking through the chain */
+ p = NULL;
+ }
+ }
+ PERF_STOP("pbuf_free");
+ /* return number of de-allocated pbufs */
+ return count;
+}
+
+/**
+ * Count number of pbufs in a chain
+ *
+ * @param p first pbuf of chain
+ * @return the number of pbufs in a chain
+ */
+
+u8_t
+pbuf_clen(struct pbuf *p)
+{
+ u8_t len;
+
+ len = 0;
+ while (p != NULL) {
+ ++len;
+ p = p->next;
+ }
+ return len;
+}
+
+/**
+ * Increment the reference count of the pbuf.
+ *
+ * @param p pbuf to increase reference counter of
+ *
+ */
+void
+pbuf_ref(struct pbuf *p)
+{
+ SYS_ARCH_DECL_PROTECT(old_level);
+ /* pbuf given? */
+ if (p != NULL) {
+ SYS_ARCH_PROTECT(old_level);
+ ++(p->ref);
+ SYS_ARCH_UNPROTECT(old_level);
+ }
+}
+
+/**
+ * Concatenate two pbufs (each may be a pbuf chain) and take over
+ * the caller's reference of the tail pbuf.
+ *
+ * @note The caller MAY NOT reference the tail pbuf afterwards.
+ * Use pbuf_chain() for that purpose.
+ *
+ * @see pbuf_chain()
+ */
+
+void
+pbuf_cat(struct pbuf *h, struct pbuf *t)
+{
+ struct pbuf *p;
+
+ LWIP_ERROR("(h != NULL) && (t != NULL) (programmer violates API)",
+ ((h != NULL) && (t != NULL)), return;);
+
+ /* proceed to last pbuf of chain */
+ for (p = h; p->next != NULL; p = p->next) {
+ /* add total length of second chain to all totals of first chain */
+ p->tot_len += t->tot_len;
+ }
+ /* { p is last pbuf of first h chain, p->next == NULL } */
+ LWIP_ASSERT("p->tot_len == p->len (of last pbuf in chain)", p->tot_len == p->len);
+ LWIP_ASSERT("p->next == NULL", p->next == NULL);
+ /* add total length of second chain to last pbuf total of first chain */
+ p->tot_len += t->tot_len;
+ /* chain last pbuf of head (p) with first of tail (t) */
+ p->next = t;
+ /* p->next now references t, but the caller will drop its reference to t,
+ * so netto there is no change to the reference count of t.
+ */
+}
+
+/**
+ * Chain two pbufs (or pbuf chains) together.
+ *
+ * The caller MUST call pbuf_free(t) once it has stopped
+ * using it. Use pbuf_cat() instead if you no longer use t.
+ *
+ * @param h head pbuf (chain)
+ * @param t tail pbuf (chain)
+ * @note The pbufs MUST belong to the same packet.
+ * @note MAY NOT be called on a packet queue.
+ *
+ * The ->tot_len fields of all pbufs of the head chain are adjusted.
+ * The ->next field of the last pbuf of the head chain is adjusted.
+ * The ->ref field of the first pbuf of the tail chain is adjusted.
+ *
+ */
+void
+pbuf_chain(struct pbuf *h, struct pbuf *t)
+{
+ pbuf_cat(h, t);
+ /* t is now referenced by h */
+ pbuf_ref(t);
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_chain: %p references %p\n", (void *)h, (void *)t));
+}
+
+/**
+ * Dechains the first pbuf from its succeeding pbufs in the chain.
+ *
+ * Makes p->tot_len field equal to p->len.
+ * @param p pbuf to dechain
+ * @return remainder of the pbuf chain, or NULL if it was de-allocated.
+ * @note May not be called on a packet queue.
+ */
+struct pbuf *
+pbuf_dechain(struct pbuf *p)
+{
+ struct pbuf *q;
+ u8_t tail_gone = 1;
+ /* tail */
+ q = p->next;
+ /* pbuf has successor in chain? */
+ if (q != NULL) {
+ /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */
+ LWIP_ASSERT("p->tot_len == p->len + q->tot_len", q->tot_len == p->tot_len - p->len);
+ /* enforce invariant if assertion is disabled */
+ q->tot_len = p->tot_len - p->len;
+ /* decouple pbuf from remainder */
+ p->next = NULL;
+ /* total length of pbuf p is its own length only */
+ p->tot_len = p->len;
+ /* q is no longer referenced by p, free it */
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_dechain: unreferencing %p\n", (void *)q));
+ tail_gone = pbuf_free(q);
+ if (tail_gone > 0) {
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE,
+ ("pbuf_dechain: deallocated %p (as it is no longer referenced)\n", (void *)q));
+ }
+ /* return remaining tail or NULL if deallocated */
+ }
+ /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */
+ LWIP_ASSERT("p->tot_len == p->len", p->tot_len == p->len);
+ return ((tail_gone > 0) ? NULL : q);
+}
+
+/**
+ *
+ * Create PBUF_RAM copies of pbufs.
+ *
+ * Used to queue packets on behalf of the lwIP stack, such as
+ * ARP based queueing.
+ *
+ * @note You MUST explicitly use p = pbuf_take(p);
+ *
+ * @note Only one packet is copied, no packet queue!
+ *
+ * @param p_to pbuf destination of the copy
+ * @param p_from pbuf source of the copy
+ *
+ * @return ERR_OK if pbuf was copied
+ * ERR_ARG if one of the pbufs is NULL or p_to is not big
+ * enough to hold p_from
+ */
+err_t
+pbuf_copy(struct pbuf *p_to, struct pbuf *p_from)
+{
+ u16_t offset_to=0, offset_from=0, len;
+
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy(%p, %p)\n",
+ (void*)p_to, (void*)p_from));
+
+ /* is the target big enough to hold the source? */
+ LWIP_ERROR("pbuf_copy: target not big enough to hold source", ((p_to != NULL) &&
+ (p_from != NULL) && (p_to->tot_len >= p_from->tot_len)), return ERR_ARG;);
+
+ /* iterate through pbuf chain */
+ do
+ {
+ LWIP_ASSERT("p_to != NULL", p_to != NULL);
+ /* copy one part of the original chain */
+ if ((p_to->len - offset_to) >= (p_from->len - offset_from)) {
+ /* complete current p_from fits into current p_to */
+ len = p_from->len - offset_from;
+ } else {
+ /* current p_from does not fit into current p_to */
+ len = p_to->len - offset_to;
+ }
+ MEMCPY((u8_t*)p_to->payload + offset_to, (u8_t*)p_from->payload + offset_from, len);
+ offset_to += len;
+ offset_from += len;
+ LWIP_ASSERT("offset_to <= p_to->len", offset_to <= p_to->len);
+ if (offset_to == p_to->len) {
+ /* on to next p_to (if any) */
+ offset_to = 0;
+ p_to = p_to->next;
+ }
+ LWIP_ASSERT("offset_from <= p_from->len", offset_from <= p_from->len);
+ if (offset_from >= p_from->len) {
+ /* on to next p_from (if any) */
+ offset_from = 0;
+ p_from = p_from->next;
+ }
+
+ if((p_from != NULL) && (p_from->len == p_from->tot_len)) {
+ /* don't copy more than one packet! */
+ LWIP_ERROR("pbuf_copy() does not allow packet queues!\n",
+ (p_from->next == NULL), return ERR_VAL;);
+ }
+ if((p_to != NULL) && (p_to->len == p_to->tot_len)) {
+ /* don't copy more than one packet! */
+ LWIP_ERROR("pbuf_copy() does not allow packet queues!\n",
+ (p_to->next == NULL), return ERR_VAL;);
+ }
+ } while (p_from);
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy: end of chain reached.\n"));
+ return ERR_OK;
+}
+
+/**
+ * Copy (part of) the contents of a packet buffer
+ * to an application supplied buffer.
+ *
+ * @param buf the pbuf from which to copy data
+ * @param dataptr the application supplied buffer
+ * @param len length of data to copy (dataptr must be big enough). No more
+ * than buf->tot_len will be copied, irrespective of len
+ * @param offset offset into the packet buffer from where to begin copying len bytes
+ * @return the number of bytes copied, or 0 on failure
+ */
+u16_t
+pbuf_copy_partial(struct pbuf *buf, void *dataptr, u16_t len, u16_t offset)
+{
+ struct pbuf *p;
+ u16_t left;
+ u16_t buf_copy_len;
+ u16_t copied_total = 0;
+
+ LWIP_ERROR("pbuf_copy_partial: invalid buf", (buf != NULL), return 0;);
+ LWIP_ERROR("pbuf_copy_partial: invalid dataptr", (dataptr != NULL), return 0;);
+
+ left = 0;
+
+ if((buf == NULL) || (dataptr == NULL)) {
+ return 0;
+ }
+
+ /* Note some systems use byte copy if dataptr or one of the pbuf payload pointers are unaligned. */
+ for(p = buf; len != 0 && p != NULL; p = p->next) {
+ if ((offset != 0) && (offset >= p->len)) {
+ /* don't copy from this buffer -> on to the next */
+ offset -= p->len;
+ } else {
+ /* copy from this buffer. maybe only partially. */
+ buf_copy_len = p->len - offset;
+ if (buf_copy_len > len)
+ buf_copy_len = len;
+ /* copy the necessary parts of the buffer */
+ MEMCPY(&((char*)dataptr)[left], &((char*)p->payload)[offset], buf_copy_len);
+ copied_total += buf_copy_len;
+ left += buf_copy_len;
+ len -= buf_copy_len;
+ offset = 0;
+ }
+ }
+ return copied_total;
+}
+
+/**
+ * Copy application supplied data into a pbuf.
+ * This function can only be used to copy the equivalent of buf->tot_len data.
+ *
+ * @param buf pbuf to fill with data
+ * @param dataptr application supplied data buffer
+ * @param len length of the application supplied data buffer
+ *
+ * @return ERR_OK if successful, ERR_MEM if the pbuf is not big enough
+ */
+err_t
+pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len)
+{
+ struct pbuf *p;
+ u16_t buf_copy_len;
+ u16_t total_copy_len = len;
+ u16_t copied_total = 0;
+
+ LWIP_ERROR("pbuf_take: invalid buf", (buf != NULL), return 0;);
+ LWIP_ERROR("pbuf_take: invalid dataptr", (dataptr != NULL), return 0;);
+
+ if ((buf == NULL) || (dataptr == NULL) || (buf->tot_len < len)) {
+ return ERR_ARG;
+ }
+
+ /* Note some systems use byte copy if dataptr or one of the pbuf payload pointers are unaligned. */
+ for(p = buf; total_copy_len != 0; p = p->next) {
+ LWIP_ASSERT("pbuf_take: invalid pbuf", p != NULL);
+ buf_copy_len = total_copy_len;
+ if (buf_copy_len > p->len) {
+ /* this pbuf cannot hold all remaining data */
+ buf_copy_len = p->len;
+ }
+ /* copy the necessary parts of the buffer */
+ MEMCPY(p->payload, &((char*)dataptr)[copied_total], buf_copy_len);
+ total_copy_len -= buf_copy_len;
+ copied_total += buf_copy_len;
+ }
+ LWIP_ASSERT("did not copy all data", total_copy_len == 0 && copied_total == len);
+ return ERR_OK;
+}
+
+/**
+ * Creates a single pbuf out of a queue of pbufs.
+ *
+ * @remark: Either the source pbuf 'p' is freed by this function or the original
+ * pbuf 'p' is returned, therefore the caller has to check the result!
+ *
+ * @param p the source pbuf
+ * @param layer pbuf_layer of the new pbuf
+ *
+ * @return a new, single pbuf (p->next is NULL)
+ * or the old pbuf if allocation fails
+ */
+struct pbuf*
+pbuf_coalesce(struct pbuf *p, pbuf_layer layer)
+{
+ struct pbuf *q;
+ err_t err;
+ if (p->next == NULL) {
+ return p;
+ }
+ q = pbuf_alloc(layer, p->tot_len, PBUF_RAM);
+ if (q == NULL) {
+ /* @todo: what do we do now? */
+ return p;
+ }
+ err = pbuf_copy(q, p);
+ LWIP_ASSERT("pbuf_copy failed", err == ERR_OK);
+ pbuf_free(p);
+ return q;
+}
+
+#if LWIP_CHECKSUM_ON_COPY
+/**
+ * Copies data into a single pbuf (*not* into a pbuf queue!) and updates
+ * the checksum while copying
+ *
+ * @param p the pbuf to copy data into
+ * @param start_offset offset of p->payload where to copy the data to
+ * @param dataptr data to copy into the pbuf
+ * @param len length of data to copy into the pbuf
+ * @param chksum pointer to the checksum which is updated
+ * @return ERR_OK if successful, another error if the data does not fit
+ * within the (first) pbuf (no pbuf queues!)
+ */
+err_t
+pbuf_fill_chksum(struct pbuf *p, u16_t start_offset, const void *dataptr,
+ u16_t len, u16_t *chksum)
+{
+ u32_t acc;
+ u16_t copy_chksum;
+ char *dst_ptr;
+ LWIP_ASSERT("p != NULL", p != NULL);
+ LWIP_ASSERT("dataptr != NULL", dataptr != NULL);
+ LWIP_ASSERT("chksum != NULL", chksum != NULL);
+ LWIP_ASSERT("len != 0", len != 0);
+
+ if ((start_offset >= p->len) || (start_offset + len > p->len)) {
+ return ERR_ARG;
+ }
+
+ dst_ptr = ((char*)p->payload) + start_offset;
+ copy_chksum = LWIP_CHKSUM_COPY(dst_ptr, dataptr, len);
+ if ((start_offset & 1) != 0) {
+ copy_chksum = SWAP_BYTES_IN_WORD(copy_chksum);
+ }
+ acc = *chksum;
+ acc += copy_chksum;
+ *chksum = FOLD_U32T(acc);
+ return ERR_OK;
+}
+#endif /* LWIP_CHECKSUM_ON_COPY */
+
+ /** Get one byte from the specified position in a pbuf
+ * WARNING: returns zero for offset >= p->tot_len
+ *
+ * @param p pbuf to parse
+ * @param offset offset into p of the byte to return
+ * @return byte at an offset into p OR ZERO IF 'offset' >= p->tot_len
+ */
+u8_t
+pbuf_get_at(struct pbuf* p, u16_t offset)
+{
+ u16_t copy_from = offset;
+ struct pbuf* q = p;
+
+ /* get the correct pbuf */
+ while ((q != NULL) && (q->len <= copy_from)) {
+ copy_from -= q->len;
+ q = q->next;
+ }
+ /* return requested data if pbuf is OK */
+ if ((q != NULL) && (q->len > copy_from)) {
+ return ((u8_t*)q->payload)[copy_from];
+ }
+ return 0;
+}
+
+/** Compare pbuf contents at specified offset with memory s2, both of length n
+ *
+ * @param p pbuf to compare
+ * @param offset offset into p at wich to start comparing
+ * @param s2 buffer to compare
+ * @param n length of buffer to compare
+ * @return zero if equal, nonzero otherwise
+ * (0xffff if p is too short, diffoffset+1 otherwise)
+ */
+u16_t
+pbuf_memcmp(struct pbuf* p, u16_t offset, const void* s2, u16_t n)
+{
+ u16_t start = offset;
+ struct pbuf* q = p;
+
+ /* get the correct pbuf */
+ while ((q != NULL) && (q->len <= start)) {
+ start -= q->len;
+ q = q->next;
+ }
+ /* return requested data if pbuf is OK */
+ if ((q != NULL) && (q->len > start)) {
+ u16_t i;
+ for(i = 0; i < n; i++) {
+ u8_t a = pbuf_get_at(q, start + i);
+ u8_t b = ((u8_t*)s2)[i];
+ if (a != b) {
+ return i+1;
+ }
+ }
+ return 0;
+ }
+ return 0xffff;
+}
+
+/** Find occurrence of mem (with length mem_len) in pbuf p, starting at offset
+ * start_offset.
+ *
+ * @param p pbuf to search, maximum length is 0xFFFE since 0xFFFF is used as
+ * return value 'not found'
+ * @param mem search for the contents of this buffer
+ * @param mem_len length of 'mem'
+ * @param start_offset offset into p at which to start searching
+ * @return 0xFFFF if substr was not found in p or the index where it was found
+ */
+u16_t
+pbuf_memfind(struct pbuf* p, const void* mem, u16_t mem_len, u16_t start_offset)
+{
+ u16_t i;
+ u16_t max = p->tot_len - mem_len;
+ if (p->tot_len >= mem_len + start_offset) {
+ for(i = start_offset; i <= max; ) {
+ u16_t plus = pbuf_memcmp(p, i, mem, mem_len);
+ if (plus == 0) {
+ return i;
+ } else {
+ i += plus;
+ }
+ }
+ }
+ return 0xFFFF;
+}
+
+/** Find occurrence of substr with length substr_len in pbuf p, start at offset
+ * start_offset
+ * WARNING: in contrast to strstr(), this one does not stop at the first \0 in
+ * the pbuf/source string!
+ *
+ * @param p pbuf to search, maximum length is 0xFFFE since 0xFFFF is used as
+ * return value 'not found'
+ * @param substr string to search for in p, maximum length is 0xFFFE
+ * @return 0xFFFF if substr was not found in p or the index where it was found
+ */
+u16_t
+pbuf_strstr(struct pbuf* p, const char* substr)
+{
+ size_t substr_len;
+ if ((substr == NULL) || (substr[0] == 0) || (p->tot_len == 0xFFFF)) {
+ return 0xFFFF;
+ }
+ substr_len = strlen(substr);
+ if (substr_len >= 0xFFFF) {
+ return 0xFFFF;
+ }
+ return pbuf_memfind(p, substr, (u16_t)substr_len, 0);
+}
diff --git a/app/lwip/core/raw.c b/app/lwip/core/raw.c
new file mode 100644
index 00000000..d0e65579
--- /dev/null
+++ b/app/lwip/core/raw.c
@@ -0,0 +1,354 @@
+/**
+ * @file
+ * Implementation of raw protocol PCBs for low-level handling of
+ * different types of protocols besides (or overriding) those
+ * already available in lwIP.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_RAW /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/memp.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/raw.h"
+#include "lwip/stats.h"
+#include "arch/perf.h"
+
+#include
+
+/** The list of RAW PCBs */
+static struct raw_pcb *raw_pcbs;
+
+/**
+ * Determine if in incoming IP packet is covered by a RAW PCB
+ * and if so, pass it to a user-provided receive callback function.
+ *
+ * Given an incoming IP datagram (as a chain of pbufs) this function
+ * finds a corresponding RAW PCB and calls the corresponding receive
+ * callback function.
+ *
+ * @param p pbuf to be demultiplexed to a RAW PCB.
+ * @param inp network interface on which the datagram was received.
+ * @return - 1 if the packet has been eaten by a RAW PCB receive
+ * callback function. The caller MAY NOT not reference the
+ * packet any longer, and MAY NOT call pbuf_free().
+ * @return - 0 if packet is not eaten (pbuf is still referenced by the
+ * caller).
+ *
+ */
+u8_t ICACHE_FLASH_ATTR
+raw_input(struct pbuf *p, struct netif *inp)
+{
+ struct raw_pcb *pcb, *prev;
+ struct ip_hdr *iphdr;
+ s16_t proto;
+ u8_t eaten = 0;
+
+ LWIP_UNUSED_ARG(inp);
+
+ iphdr = (struct ip_hdr *)p->payload;
+ proto = IPH_PROTO(iphdr);
+
+ prev = NULL;
+ pcb = raw_pcbs;
+ /* loop through all raw pcbs until the packet is eaten by one */
+ /* this allows multiple pcbs to match against the packet by design */
+ while ((eaten == 0) && (pcb != NULL)) {
+ if ((pcb->protocol == proto) &&
+ (ip_addr_isany(&pcb->local_ip) ||
+ ip_addr_cmp(&(pcb->local_ip), ¤t_iphdr_dest))) {
+#if IP_SOF_BROADCAST_RECV
+ /* broadcast filter? */
+ if ((pcb->so_options & SOF_BROADCAST) || !ip_addr_isbroadcast(¤t_iphdr_dest, inp))
+#endif /* IP_SOF_BROADCAST_RECV */
+ {
+ /* receive callback function available? */
+ if (pcb->recv != NULL) {
+ /* the receive callback function did not eat the packet? */
+ if (pcb->recv(pcb->recv_arg, pcb, p, ip_current_src_addr()) != 0) {
+ /* receive function ate the packet */
+ p = NULL;
+ eaten = 1;
+ if (prev != NULL) {
+ /* move the pcb to the front of raw_pcbs so that is
+ found faster next time */
+ prev->next = pcb->next;
+ pcb->next = raw_pcbs;
+ raw_pcbs = pcb;
+ }
+ }
+ }
+ /* no receive callback function was set for this raw PCB */
+ }
+ /* drop the packet */
+ }
+ prev = pcb;
+ pcb = pcb->next;
+ }
+ return eaten;
+}
+
+/**
+ * Bind a RAW PCB.
+ *
+ * @param pcb RAW PCB to be bound with a local address ipaddr.
+ * @param ipaddr local IP address to bind with. Use IP_ADDR_ANY to
+ * bind to all local interfaces.
+ *
+ * @return lwIP error code.
+ * - ERR_OK. Successful. No error occured.
+ * - ERR_USE. The specified IP address is already bound to by
+ * another RAW PCB.
+ *
+ * @see raw_disconnect()
+ */
+err_t ICACHE_FLASH_ATTR
+raw_bind(struct raw_pcb *pcb, ip_addr_t *ipaddr)
+{
+ ip_addr_set(&pcb->local_ip, ipaddr);
+ return ERR_OK;
+}
+
+/**
+ * Connect an RAW PCB. This function is required by upper layers
+ * of lwip. Using the raw api you could use raw_sendto() instead
+ *
+ * This will associate the RAW PCB with the remote address.
+ *
+ * @param pcb RAW PCB to be connected with remote address ipaddr and port.
+ * @param ipaddr remote IP address to connect with.
+ *
+ * @return lwIP error code
+ *
+ * @see raw_disconnect() and raw_sendto()
+ */
+err_t ICACHE_FLASH_ATTR
+raw_connect(struct raw_pcb *pcb, ip_addr_t *ipaddr)
+{
+ ip_addr_set(&pcb->remote_ip, ipaddr);
+ return ERR_OK;
+}
+
+
+/**
+ * Set the callback function for received packets that match the
+ * raw PCB's protocol and binding.
+ *
+ * The callback function MUST either
+ * - eat the packet by calling pbuf_free() and returning non-zero. The
+ * packet will not be passed to other raw PCBs or other protocol layers.
+ * - not free the packet, and return zero. The packet will be matched
+ * against further PCBs and/or forwarded to another protocol layers.
+ *
+ * @return non-zero if the packet was free()d, zero if the packet remains
+ * available for others.
+ */
+void ICACHE_FLASH_ATTR
+raw_recv(struct raw_pcb *pcb, raw_recv_fn recv, void *recv_arg)
+{
+ /* remember recv() callback and user data */
+ pcb->recv = recv;
+ pcb->recv_arg = recv_arg;
+}
+
+/**
+ * Send the raw IP packet to the given address. Note that actually you cannot
+ * modify the IP headers (this is inconsistent with the receive callback where
+ * you actually get the IP headers), you can only specify the IP payload here.
+ * It requires some more changes in lwIP. (there will be a raw_send() function
+ * then.)
+ *
+ * @param pcb the raw pcb which to send
+ * @param p the IP payload to send
+ * @param ipaddr the destination address of the IP packet
+ *
+ */
+err_t ICACHE_FLASH_ATTR
+raw_sendto(struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *ipaddr)
+{
+ err_t err;
+ struct netif *netif;
+ ip_addr_t *src_ip;
+ struct pbuf *q; /* q will be sent down the stack */
+
+ LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_sendto\n"));
+
+ /* not enough space to add an IP header to first pbuf in given p chain? */
+ if (pbuf_header(p, IP_HLEN)) {
+ /* allocate header in new pbuf */
+ q = pbuf_alloc(PBUF_IP, 0, PBUF_RAM);
+ /* new header pbuf could not be allocated? */
+ if (q == NULL) {
+ LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("raw_sendto: could not allocate header\n"));
+ return ERR_MEM;
+ }
+ if (p->tot_len != 0) {
+ /* chain header q in front of given pbuf p */
+ pbuf_chain(q, p);
+ }
+ /* { first pbuf q points to header pbuf } */
+ LWIP_DEBUGF(RAW_DEBUG, ("raw_sendto: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p));
+ } else {
+ /* first pbuf q equals given pbuf */
+ q = p;
+ if(pbuf_header(q, -IP_HLEN)) {
+ LWIP_ASSERT("Can't restore header we just removed!", 0);
+ return ERR_MEM;
+ }
+ }
+
+ if ((netif = ip_route(ipaddr)) == NULL) {
+ LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr)));
+ /* free any temporary header pbuf allocated by pbuf_header() */
+ if (q != p) {
+ pbuf_free(q);
+ }
+ return ERR_RTE;
+ }
+
+#if IP_SOF_BROADCAST
+ /* broadcast filter? */
+ if (((pcb->so_options & SOF_BROADCAST) == 0) && ip_addr_isbroadcast(ipaddr, netif)) {
+ LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb));
+ /* free any temporary header pbuf allocated by pbuf_header() */
+ if (q != p) {
+ pbuf_free(q);
+ }
+ return ERR_VAL;
+ }
+#endif /* IP_SOF_BROADCAST */
+
+ if (ip_addr_isany(&pcb->local_ip)) {
+ /* use outgoing network interface IP address as source address */
+ src_ip = &(netif->ip_addr);
+ } else {
+ /* use RAW PCB local IP address as source address */
+ src_ip = &(pcb->local_ip);
+ }
+
+#if LWIP_NETIF_HWADDRHINT
+ netif->addr_hint = &(pcb->addr_hint);
+#endif /* LWIP_NETIF_HWADDRHINT*/
+ err = ip_output_if (q, src_ip, ipaddr, pcb->ttl, pcb->tos, pcb->protocol, netif);
+#if LWIP_NETIF_HWADDRHINT
+ netif->addr_hint = NULL;
+#endif /* LWIP_NETIF_HWADDRHINT*/
+
+ /* did we chain a header earlier? */
+ if (q != p) {
+ /* free the header */
+ pbuf_free(q);
+ }
+ return err;
+}
+
+/**
+ * Send the raw IP packet to the address given by raw_connect()
+ *
+ * @param pcb the raw pcb which to send
+ * @param p the IP payload to send
+ *
+ */
+err_t ICACHE_FLASH_ATTR
+raw_send(struct raw_pcb *pcb, struct pbuf *p)
+{
+ return raw_sendto(pcb, p, &pcb->remote_ip);
+}
+
+/**
+ * Remove an RAW PCB.
+ *
+ * @param pcb RAW PCB to be removed. The PCB is removed from the list of
+ * RAW PCB's and the data structure is freed from memory.
+ *
+ * @see raw_new()
+ */
+void ICACHE_FLASH_ATTR
+raw_remove(struct raw_pcb *pcb)
+{
+ struct raw_pcb *pcb2;
+ /* pcb to be removed is first in list? */
+ if (raw_pcbs == pcb) {
+ /* make list start at 2nd pcb */
+ raw_pcbs = raw_pcbs->next;
+ /* pcb not 1st in list */
+ } else {
+ for(pcb2 = raw_pcbs; pcb2 != NULL; pcb2 = pcb2->next) {
+ /* find pcb in raw_pcbs list */
+ if (pcb2->next != NULL && pcb2->next == pcb) {
+ /* remove pcb from list */
+ pcb2->next = pcb->next;
+ }
+ }
+ }
+ memp_free(MEMP_RAW_PCB, pcb);
+}
+
+/**
+ * Create a RAW PCB.
+ *
+ * @return The RAW PCB which was created. NULL if the PCB data structure
+ * could not be allocated.
+ *
+ * @param proto the protocol number of the IPs payload (e.g. IP_PROTO_ICMP)
+ *
+ * @see raw_remove()
+ */
+struct raw_pcb * ICACHE_FLASH_ATTR
+raw_new(u8_t proto)
+{
+ struct raw_pcb *pcb;
+
+ LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_new\n"));
+
+ pcb = (struct raw_pcb *)memp_malloc(MEMP_RAW_PCB);
+ /* could allocate RAW PCB? */
+ if (pcb != NULL) {
+ /* initialize PCB to all zeroes */
+ memset(pcb, 0, sizeof(struct raw_pcb));
+ pcb->protocol = proto;
+ pcb->ttl = RAW_TTL;
+ pcb->next = raw_pcbs;
+ raw_pcbs = pcb;
+ }
+ return pcb;
+}
+
+#endif /* LWIP_RAW */
diff --git a/app/lwip/core/stats.c b/app/lwip/core/stats.c
new file mode 100644
index 00000000..69f97d41
--- /dev/null
+++ b/app/lwip/core/stats.c
@@ -0,0 +1,176 @@
+/**
+ * @file
+ * Statistics module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_STATS /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/stats.h"
+#include "lwip/mem.h"
+
+#include
+
+struct stats_ lwip_stats;
+
+void stats_init(void)
+{
+#ifdef LWIP_DEBUG
+#if MEMP_STATS
+ const char * memp_names[] = {
+#define LWIP_MEMPOOL(name,num,size,desc) desc,
+#include "lwip/memp_std.h"
+ };
+ int i;
+ for (i = 0; i < MEMP_MAX; i++) {
+ lwip_stats.memp[i].name = memp_names[i];
+ }
+#endif /* MEMP_STATS */
+#if MEM_STATS
+ lwip_stats.mem.name = "MEM";
+#endif /* MEM_STATS */
+#endif /* LWIP_DEBUG */
+}
+
+#if LWIP_STATS_DISPLAY
+void
+stats_display_proto(struct stats_proto *proto, char *name)
+{
+ LWIP_PLATFORM_DIAG(("\n%s\n\t", name));
+ LWIP_PLATFORM_DIAG(("xmit: %"STAT_COUNTER_F"\n\t", proto->xmit));
+ LWIP_PLATFORM_DIAG(("recv: %"STAT_COUNTER_F"\n\t", proto->recv));
+ LWIP_PLATFORM_DIAG(("fw: %"STAT_COUNTER_F"\n\t", proto->fw));
+ LWIP_PLATFORM_DIAG(("drop: %"STAT_COUNTER_F"\n\t", proto->drop));
+ LWIP_PLATFORM_DIAG(("chkerr: %"STAT_COUNTER_F"\n\t", proto->chkerr));
+ LWIP_PLATFORM_DIAG(("lenerr: %"STAT_COUNTER_F"\n\t", proto->lenerr));
+ LWIP_PLATFORM_DIAG(("memerr: %"STAT_COUNTER_F"\n\t", proto->memerr));
+ LWIP_PLATFORM_DIAG(("rterr: %"STAT_COUNTER_F"\n\t", proto->rterr));
+ LWIP_PLATFORM_DIAG(("proterr: %"STAT_COUNTER_F"\n\t", proto->proterr));
+ LWIP_PLATFORM_DIAG(("opterr: %"STAT_COUNTER_F"\n\t", proto->opterr));
+ LWIP_PLATFORM_DIAG(("err: %"STAT_COUNTER_F"\n\t", proto->err));
+ LWIP_PLATFORM_DIAG(("cachehit: %"STAT_COUNTER_F"\n", proto->cachehit));
+}
+
+#if IGMP_STATS
+void
+stats_display_igmp(struct stats_igmp *igmp)
+{
+ LWIP_PLATFORM_DIAG(("\nIGMP\n\t"));
+ LWIP_PLATFORM_DIAG(("xmit: %"STAT_COUNTER_F"\n\t", igmp->xmit));
+ LWIP_PLATFORM_DIAG(("recv: %"STAT_COUNTER_F"\n\t", igmp->recv));
+ LWIP_PLATFORM_DIAG(("drop: %"STAT_COUNTER_F"\n\t", igmp->drop));
+ LWIP_PLATFORM_DIAG(("chkerr: %"STAT_COUNTER_F"\n\t", igmp->chkerr));
+ LWIP_PLATFORM_DIAG(("lenerr: %"STAT_COUNTER_F"\n\t", igmp->lenerr));
+ LWIP_PLATFORM_DIAG(("memerr: %"STAT_COUNTER_F"\n\t", igmp->memerr));
+ LWIP_PLATFORM_DIAG(("proterr: %"STAT_COUNTER_F"\n\t", igmp->proterr));
+ LWIP_PLATFORM_DIAG(("rx_v1: %"STAT_COUNTER_F"\n\t", igmp->rx_v1));
+ LWIP_PLATFORM_DIAG(("rx_group: %"STAT_COUNTER_F"\n", igmp->rx_group));
+ LWIP_PLATFORM_DIAG(("rx_general: %"STAT_COUNTER_F"\n", igmp->rx_general));
+ LWIP_PLATFORM_DIAG(("rx_report: %"STAT_COUNTER_F"\n\t", igmp->rx_report));
+ LWIP_PLATFORM_DIAG(("tx_join: %"STAT_COUNTER_F"\n\t", igmp->tx_join));
+ LWIP_PLATFORM_DIAG(("tx_leave: %"STAT_COUNTER_F"\n\t", igmp->tx_leave));
+ LWIP_PLATFORM_DIAG(("tx_report: %"STAT_COUNTER_F"\n\t", igmp->tx_report));
+}
+#endif /* IGMP_STATS */
+
+#if MEM_STATS || MEMP_STATS
+void
+stats_display_mem(struct stats_mem *mem, char *name)
+{
+ LWIP_PLATFORM_DIAG(("\nMEM %s\n\t", name));
+ LWIP_PLATFORM_DIAG(("avail: %"U32_F"\n\t", (u32_t)mem->avail));
+ LWIP_PLATFORM_DIAG(("used: %"U32_F"\n\t", (u32_t)mem->used));
+ LWIP_PLATFORM_DIAG(("max: %"U32_F"\n\t", (u32_t)mem->max));
+ LWIP_PLATFORM_DIAG(("err: %"U32_F"\n", (u32_t)mem->err));
+}
+
+#if MEMP_STATS
+void
+stats_display_memp(struct stats_mem *mem, int index)
+{
+ char * memp_names[] = {
+#define LWIP_MEMPOOL(name,num,size,desc) desc,
+#include "lwip/memp_std.h"
+ };
+ if(index < MEMP_MAX) {
+ stats_display_mem(mem, memp_names[index]);
+ }
+}
+#endif /* MEMP_STATS */
+#endif /* MEM_STATS || MEMP_STATS */
+
+#if SYS_STATS
+void
+stats_display_sys(struct stats_sys *sys)
+{
+ LWIP_PLATFORM_DIAG(("\nSYS\n\t"));
+ LWIP_PLATFORM_DIAG(("sem.used: %"U32_F"\n\t", (u32_t)sys->sem.used));
+ LWIP_PLATFORM_DIAG(("sem.max: %"U32_F"\n\t", (u32_t)sys->sem.max));
+ LWIP_PLATFORM_DIAG(("sem.err: %"U32_F"\n\t", (u32_t)sys->sem.err));
+ LWIP_PLATFORM_DIAG(("mutex.used: %"U32_F"\n\t", (u32_t)sys->mutex.used));
+ LWIP_PLATFORM_DIAG(("mutex.max: %"U32_F"\n\t", (u32_t)sys->mutex.max));
+ LWIP_PLATFORM_DIAG(("mutex.err: %"U32_F"\n\t", (u32_t)sys->mutex.err));
+ LWIP_PLATFORM_DIAG(("mbox.used: %"U32_F"\n\t", (u32_t)sys->mbox.used));
+ LWIP_PLATFORM_DIAG(("mbox.max: %"U32_F"\n\t", (u32_t)sys->mbox.max));
+ LWIP_PLATFORM_DIAG(("mbox.err: %"U32_F"\n\t", (u32_t)sys->mbox.err));
+}
+#endif /* SYS_STATS */
+
+void
+stats_display(void)
+{
+ s16_t i;
+
+ LINK_STATS_DISPLAY();
+ ETHARP_STATS_DISPLAY();
+ IPFRAG_STATS_DISPLAY();
+ IP_STATS_DISPLAY();
+ IGMP_STATS_DISPLAY();
+ ICMP_STATS_DISPLAY();
+ UDP_STATS_DISPLAY();
+ TCP_STATS_DISPLAY();
+ MEM_STATS_DISPLAY();
+ for (i = 0; i < MEMP_MAX; i++) {
+ MEMP_STATS_DISPLAY(i);
+ }
+ SYS_STATS_DISPLAY();
+}
+#endif /* LWIP_STATS_DISPLAY */
+
+#endif /* LWIP_STATS */
+
diff --git a/app/lwip/core/sys.c b/app/lwip/core/sys.c
new file mode 100644
index 00000000..d3a77deb
--- /dev/null
+++ b/app/lwip/core/sys.c
@@ -0,0 +1,66 @@
+/**
+ * @file
+ * lwIP Operating System abstraction
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/sys.h"
+
+/* Most of the functions defined in sys.h must be implemented in the
+ * architecture-dependent file sys_arch.c */
+
+#if !NO_SYS
+
+/**
+ * Sleep for some ms. Timeouts are NOT processed while sleeping.
+ *
+ * @param ms number of milliseconds to sleep
+ */
+void
+sys_msleep(u32_t ms)
+{
+ if (ms > 0) {
+ sys_sem_t delaysem;
+ err_t err = sys_sem_new(&delaysem, 0);
+ if (err == ERR_OK) {
+ sys_arch_sem_wait(&delaysem, ms);
+ sys_sem_free(&delaysem);
+ }
+ }
+}
+
+#endif /* !NO_SYS */
diff --git a/app/lwip/core/sys_arch.c b/app/lwip/core/sys_arch.c
new file mode 100644
index 00000000..81bb2208
--- /dev/null
+++ b/app/lwip/core/sys_arch.c
@@ -0,0 +1,13 @@
+/*
+ * copyright (c) 2010 - 2011 espressif system
+ */
+
+#include "c_types.h"
+#include "ets_sys.h"
+#include "osapi.h"
+#include "os_type.h"
+
+#include "lwip/opt.h"
+#include "lwip/sys.h"
+
+#include "eagle_soc.h"
diff --git a/app/lwip/core/tcp.c b/app/lwip/core/tcp.c
new file mode 100644
index 00000000..e3193cc7
--- /dev/null
+++ b/app/lwip/core/tcp.c
@@ -0,0 +1,1651 @@
+/**
+ * @file
+ * Transmission Control Protocol for IP
+ *
+ * This file contains common functions for the TCP implementation, such as functinos
+ * for manipulating the data structures and the TCP timer functions. TCP functions
+ * related to input and output is found in tcp_in.c and tcp_out.c respectively.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/snmp.h"
+#include "lwip/tcp.h"
+#include "lwip/tcp_impl.h"
+#include "lwip/debug.h"
+#include "lwip/stats.h"
+
+#include
+
+const char * const tcp_state_str[] = {
+ "CLOSED",
+ "LISTEN",
+ "SYN_SENT",
+ "SYN_RCVD",
+ "ESTABLISHED",
+ "FIN_WAIT_1",
+ "FIN_WAIT_2",
+ "CLOSE_WAIT",
+ "CLOSING",
+ "LAST_ACK",
+ "TIME_WAIT"
+};
+
+/* Incremented every coarse grained timer shot (typically every 500 ms). */
+u32_t tcp_ticks;
+const u8_t tcp_backoff[13] =
+ { 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7};
+ /* Times per slowtmr hits */
+const u8_t tcp_persist_backoff[7] = { 3, 6, 12, 24, 48, 96, 120 };
+
+/* The TCP PCB lists. */
+
+/** List of all TCP PCBs bound but not yet (connected || listening) */
+struct tcp_pcb *tcp_bound_pcbs;
+/** List of all TCP PCBs in LISTEN state */
+union tcp_listen_pcbs_t tcp_listen_pcbs;
+/** List of all TCP PCBs that are in a state in which
+ * they accept or send data. */
+struct tcp_pcb *tcp_active_pcbs;
+/** List of all TCP PCBs in TIME-WAIT state */
+struct tcp_pcb *tcp_tw_pcbs;
+
+#define NUM_TCP_PCB_LISTS 4
+#define NUM_TCP_PCB_LISTS_NO_TIME_WAIT 3
+/** An array with all (non-temporary) PCB lists, mainly used for smaller code size */
+struct tcp_pcb ** const tcp_pcb_lists[] = {&tcp_listen_pcbs.pcbs, &tcp_bound_pcbs,
+ &tcp_active_pcbs, &tcp_tw_pcbs};
+
+/** Only used for temporary storage. */
+struct tcp_pcb *tcp_tmp_pcb;
+
+/** Timer counter to handle calling slow-timer from tcp_tmr() */
+static u8_t tcp_timer;
+static u16_t tcp_new_port(void);//����µ�tcp���ض˿�
+
+/**
+ * Called periodically to dispatch TCP timers.
+ *
+ */
+void
+tcp_tmr(void)
+{
+ /* Call tcp_fasttmr() every 250 ms */
+ tcp_fasttmr();
+
+ if (++tcp_timer & 1) {
+ /* Call tcp_tmr() every 500 ms, i.e., every other timer
+ tcp_tmr() is called. */
+ tcp_slowtmr();
+ }
+}
+
+/**
+ * Closes the TX side of a connection held by the PCB.
+ * For tcp_close(), a RST is sent if the application didn't receive all data
+ * (tcp_recved() not called for all data passed to recv callback).
+ *
+ * Listening pcbs are freed and may not be referenced any more.
+ * Connection pcbs are freed if not yet connected and may not be referenced
+ * any more. If a connection is established (at least SYN received or in
+ * a closing state), the connection is closed, and put in a closing state.
+ * The pcb is then automatically freed in tcp_slowtmr(). It is therefore
+ * unsafe to reference it.
+ *
+ * @param pcb the tcp_pcb to close
+ * @return ERR_OK if connection has been closed
+ * another err_t if closing failed and pcb is not freed
+ */
+static err_t
+tcp_close_shutdown(struct tcp_pcb *pcb, u8_t rst_on_unacked_data)
+{
+ err_t err;
+
+ if (rst_on_unacked_data && (pcb->state != LISTEN)) {
+ if ((pcb->refused_data != NULL) || (pcb->rcv_wnd != TCP_WND)) {
+ /* Not all data received by application, send RST to tell the remote
+ side about this. */
+ LWIP_ASSERT("pcb->flags & TF_RXCLOSED", pcb->flags & TF_RXCLOSED);
+
+ /* don't call tcp_abort here: we must not deallocate the pcb since
+ that might not be expected when calling tcp_close */
+ tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip,
+ pcb->local_port, pcb->remote_port);
+
+ tcp_pcb_purge(pcb);
+
+ /* TODO: to which state do we move now? */
+
+ /* move to TIME_WAIT since we close actively */
+ TCP_RMV(&tcp_active_pcbs, pcb);
+ pcb->state = TIME_WAIT;
+ TCP_REG(&tcp_tw_pcbs, pcb);
+
+ return ERR_OK;
+ }
+ }
+
+ switch (pcb->state) {
+ case CLOSED:
+ /* Closing a pcb in the CLOSED state might seem erroneous,
+ * however, it is in this state once allocated and as yet unused
+ * and the user needs some way to free it should the need arise.
+ * Calling tcp_close() with a pcb that has already been closed, (i.e. twice)
+ * or for a pcb that has been used and then entered the CLOSED state
+ * is erroneous, but this should never happen as the pcb has in those cases
+ * been freed, and so any remaining handles are bogus. */
+ /*��CLOSED״̬�¹ر�һ��pcb�ƺ��Ǵ���ģ�
+ *������ˣ�һ�������״̬�·����˶��һ�û��ʹ��,�û���ҪһЩ�취���ͷ���
+ *����һ���Ѿ����رյ�pcb��tcp_close(),(��2��)����һ���Ѿ���ʹ����֮����CLOSE״̬�Ǵ����
+ *������Щ����±��ͷŵ�pcb�Dz�����ڵ�,��ˣ��κ�ʣ��ľ���Ǽٵ�
+ */
+ err = ERR_OK;//�趨����ֵ
+ if (pcb->local_port != 0) {
+ TCP_RMV(&tcp_bound_pcbs, pcb);
+ }
+ memp_free(MEMP_TCP_PCB, pcb);//��MEMP_TCP_PCB�ڴ���趨�ͷŵ���pcb��Ӧ�ĵ�Ԫֵ,�ͷ��ڴ�
+ pcb = NULL;
+ break;
+ case LISTEN:
+ err = ERR_OK;
+ tcp_pcb_remove(&tcp_listen_pcbs.pcbs, pcb);//�Ӽ����PCB�б���ɾ���Ӧ��pcb
+ memp_free(MEMP_TCP_PCB_LISTEN, pcb);//��MEMP_TCP_PCB_LISTEN�ڴ�����趨�ͷŵ�pcb��Ԫֵ ,�ͷ��ڴ�
+ pcb = NULL;
+ break;
+ case SYN_SENT:
+ err = ERR_OK;
+ tcp_pcb_remove(&tcp_active_pcbs, pcb);
+ memp_free(MEMP_TCP_PCB, pcb);
+ pcb = NULL;
+ snmp_inc_tcpattemptfails();
+ break;
+ case SYN_RCVD:
+ err = tcp_send_fin(pcb);//���������ر�FIN���ֱ���
+ if (err == ERR_OK) {
+ snmp_inc_tcpattemptfails();
+ pcb->state = FIN_WAIT_1;//ת��FIN_WAIT_1״̬
+ }
+ break;
+ case ESTABLISHED:
+ err = tcp_send_fin(pcb);
+ if (err == ERR_OK) {
+ snmp_inc_tcpestabresets();
+ pcb->state = FIN_WAIT_1;
+ }
+ break;
+ case CLOSE_WAIT:
+ err = tcp_send_fin(pcb);
+ if (err == ERR_OK) {
+ snmp_inc_tcpestabresets();
+ pcb->state = LAST_ACK;//����LAST_ACK�ȴ�ACK��ʱ
+ }
+ break;
+ default:
+ /* Has already been closed, do nothing. */
+ err = ERR_OK;
+ pcb = NULL;
+ break;
+ }
+
+ if (pcb != NULL && err == ERR_OK) {
+ /* To ensure all data has been sent when tcp_close returns, we have
+ to make sure tcp_output doesn't fail.
+ Since we don't really have to ensure all data has been sent when tcp_close
+ returns (unsent data is sent from tcp timer functions, also), we don't care
+ for the return value of tcp_output for now. */
+ /* @todo: When implementing SO_LINGER, this must be changed somehow:
+ If SOF_LINGER is set, the data should be sent and acked before close returns.
+ This can only be valid for sequential APIs, not for the raw API. */
+ tcp_output(pcb);//���ú����Ϳ��ƿ������ʣ��ı��ģ�����FIN���ֱ��Ķ�
+ }
+ return err;
+}
+
+/**
+ * Closes the connection held by the PCB.
+ *
+ * Listening pcbs are freed and may not be referenced any more.
+ * Connection pcbs are freed if not yet connected and may not be referenced
+ * any more. If a connection is established (at least SYN received or in
+ * a closing state), the connection is closed, and put in a closing state.
+ * The pcb is then automatically freed in tcp_slowtmr(). It is therefore
+ * unsafe to reference it (unless an error is returned).
+ *
+ * @param pcb the tcp_pcb to close
+ * @return ERR_OK if connection has been closed
+ * another err_t if closing failed and pcb is not freed
+ */
+ /*
+ *ͨ��PCB�ر���������
+ *�����е�pcbӦ�ñ��ͷŵģ�Ҳ����ԶҲ���ᱻʹ����
+ *���û�����ӻ�����Ҳû�б�����,���ӵ�pcbӦ�ñ��ͷŵ�
+ *���һ�����ӱ�����(����SYN�Ѿ������ջ�����һ���ر��е�״̬)
+ *���ӱ��ر��ˣ�����������һ�����ڹرյ�״̬
+ *pcb�Զ���tcp_slowtmr()�ͷ�,�����������Dz���ȫ��
+ */
+err_t
+tcp_close(struct tcp_pcb *pcb)
+{
+#if TCP_DEBUG //TCP debug��Ϣ����ӡpcb��״̬
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_close: closing in "));
+ tcp_debug_print_state(pcb->state);
+#endif /* TCP_DEBUG */
+
+ if (pcb->state != LISTEN) {
+ /* Set a flag not to receive any more data... */
+ pcb->flags |= TF_RXCLOSED;
+ }
+ /* ... and close */
+ return tcp_close_shutdown(pcb, 1);
+}
+
+/**
+ * Causes all or part of a full-duplex connection of this PCB to be shut down.
+ * This doesn't deallocate the PCB!
+ *
+ * @param pcb PCB to shutdown
+ * @param shut_rx shut down receive side if this is != 0
+ * @param shut_tx shut down send side if this is != 0
+ * @return ERR_OK if shutdown succeeded (or the PCB has already been shut down)
+ * another err_t on error.
+ */
+err_t
+tcp_shutdown(struct tcp_pcb *pcb, int shut_rx, int shut_tx)
+{
+ if (pcb->state == LISTEN) {
+ return ERR_CONN;
+ }
+ if (shut_rx) {
+ /* shut down the receive side: free buffered data... */
+ if (pcb->refused_data != NULL) {
+ pbuf_free(pcb->refused_data);
+ pcb->refused_data = NULL;
+ }
+ /* ... and set a flag not to receive any more data */
+ pcb->flags |= TF_RXCLOSED;
+ }
+ if (shut_tx) {
+ /* This can't happen twice since if it succeeds, the pcb's state is changed.
+ Only close in these states as the others directly deallocate the PCB */
+ switch (pcb->state) {
+ case SYN_RCVD:
+ case ESTABLISHED:
+ case CLOSE_WAIT:
+ return tcp_close_shutdown(pcb, 0);
+ default:
+ /* don't shut down other states */
+ break;
+ }
+ }
+ /* @todo: return another err_t if not in correct state or already shut? */
+ return ERR_OK;
+}
+
+/**
+ * Abandons a connection and optionally sends a RST to the remote
+ * host. Deletes the local protocol control block. This is done when
+ * a connection is killed because of shortage of memory.
+ *
+ * @param pcb the tcp_pcb to abort
+ * @param reset boolean to indicate whether a reset should be sent
+ */
+void
+tcp_abandon(struct tcp_pcb *pcb, int reset)
+{
+ u32_t seqno, ackno;
+ u16_t remote_port, local_port;
+ ip_addr_t remote_ip, local_ip;
+#if LWIP_CALLBACK_API
+ tcp_err_fn errf;
+#endif /* LWIP_CALLBACK_API */
+ void *errf_arg;
+
+ /* pcb->state LISTEN not allowed here */
+ LWIP_ASSERT("don't call tcp_abort/tcp_abandon for listen-pcbs",
+ pcb->state != LISTEN);
+ /* Figure out on which TCP PCB list we are, and remove us. If we
+ are in an active state, call the receive function associated with
+ the PCB with a NULL argument, and send an RST to the remote end. */
+ if (pcb->state == TIME_WAIT) {
+ tcp_pcb_remove(&tcp_tw_pcbs, pcb);
+ memp_free(MEMP_TCP_PCB, pcb);
+ } else {
+ seqno = pcb->snd_nxt;
+ ackno = pcb->rcv_nxt;
+ ip_addr_copy(local_ip, pcb->local_ip);
+ ip_addr_copy(remote_ip, pcb->remote_ip);
+ local_port = pcb->local_port;
+ remote_port = pcb->remote_port;
+#if LWIP_CALLBACK_API
+ errf = pcb->errf;
+#endif /* LWIP_CALLBACK_API */
+ errf_arg = pcb->callback_arg;
+ tcp_pcb_remove(&tcp_active_pcbs, pcb);
+ if (pcb->unacked != NULL) {
+ tcp_segs_free(pcb->unacked);
+ }
+ if (pcb->unsent != NULL) {
+ tcp_segs_free(pcb->unsent);
+ }
+#if TCP_QUEUE_OOSEQ
+ if (pcb->ooseq != NULL) {
+ tcp_segs_free(pcb->ooseq);
+ }
+#endif /* TCP_QUEUE_OOSEQ */
+ if (reset) {
+ LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_abandon: sending RST\n"));
+ tcp_rst(seqno, ackno, &local_ip, &remote_ip, local_port, remote_port);
+ }
+ TCP_EVENT_ERR(errf, errf_arg, ERR_ABRT);
+ memp_free(MEMP_TCP_PCB, pcb);
+ }
+}
+
+/**
+ * Aborts the connection by sending a RST (reset) segment to the remote
+ * host. The pcb is deallocated. This function never fails.
+ *
+ * ATTENTION: When calling this from one of the TCP callbacks, make
+ * sure you always return ERR_ABRT (and never return ERR_ABRT otherwise
+ * or you will risk accessing deallocated memory or memory leaks!
+ *
+ * @param pcb the tcp pcb to abort
+ */
+void
+tcp_abort(struct tcp_pcb *pcb)
+{
+ tcp_abandon(pcb, 1);
+}
+
+/**
+ * Binds the connection to a local portnumber and IP address. If the
+ * IP address is not given (i.e., ipaddr == NULL), the IP address of
+ * the outgoing network interface is used instead.
+ *
+ * @param pcb the tcp_pcb to bind (no check is done whether this pcb is
+ * already bound!)
+ * @param ipaddr the local ip address to bind to (use IP_ADDR_ANY to bind
+ * to any local address
+ * @param port the local port to bind to
+ * @return ERR_USE if the port is already in use
+ * ERR_OK if bound
+ */
+err_t
+tcp_bind(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port)
+{
+ int i;
+ int max_pcb_list = NUM_TCP_PCB_LISTS;
+ struct tcp_pcb *cpcb;
+
+ LWIP_ERROR("tcp_bind: can only bind in state CLOSED", pcb->state == CLOSED, return ERR_ISCONN);
+
+#if SO_REUSE
+ /* Unless the REUSEADDR flag is set,
+ we have to check the pcbs in TIME-WAIT state, also.
+ We do not dump TIME_WAIT pcb's; they can still be matched by incoming
+ packets using both local and remote IP addresses and ports to distinguish.
+ */
+ if ((pcb->so_options & SOF_REUSEADDR) != 0) {
+ max_pcb_list = NUM_TCP_PCB_LISTS_NO_TIME_WAIT;
+ }
+#endif /* SO_REUSE */
+
+ if (port == 0) {
+ port = tcp_new_port();
+ }
+
+ /* Check if the address already is in use (on all lists) */
+ for (i = 0; i < max_pcb_list; i++) {
+ for(cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) {
+ if (cpcb->local_port == port) {
+#if SO_REUSE
+ /* Omit checking for the same port if both pcbs have REUSEADDR set.
+ For SO_REUSEADDR, the duplicate-check for a 5-tuple is done in
+ tcp_connect. */
+ if (((pcb->so_options & SOF_REUSEADDR) == 0) ||
+ ((cpcb->so_options & SOF_REUSEADDR) == 0))
+#endif /* SO_REUSE */
+ {
+ if (ip_addr_isany(&(cpcb->local_ip)) ||
+ ip_addr_isany(ipaddr) ||
+ ip_addr_cmp(&(cpcb->local_ip), ipaddr)) {
+ //os_printf("Address in use\n");
+ return ERR_USE;
+ }
+ }
+ }
+ }
+ }
+
+ if (!ip_addr_isany(ipaddr)) {
+ pcb->local_ip = *ipaddr;
+ }
+ pcb->local_port = port;
+ TCP_REG(&tcp_bound_pcbs, pcb);
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_bind: bind to port %"U16_F"\n", port));
+ return ERR_OK;
+}
+#if LWIP_CALLBACK_API
+/**
+ * Default accept callback if no accept callback is specified by the user.
+ */
+static err_t
+tcp_accept_null(void *arg, struct tcp_pcb *pcb, err_t err)
+{
+ LWIP_UNUSED_ARG(arg);
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_UNUSED_ARG(err);
+
+ return ERR_ABRT;
+}
+#endif /* LWIP_CALLBACK_API */
+
+/**
+ * Set the state of the connection to be LISTEN, which means that it
+ * is able to accept incoming connections. The protocol control block
+ * is reallocated in order to consume less memory. Setting the
+ * connection to LISTEN is an irreversible process.
+ *��ij���Ŀ��ƿ���Ϊ����״̬
+ * @param pcb the original tcp_pcb �����Ŀ��ƿ����
+ * @param backlog the incoming connections queue limit
+ * @return tcp_pcb used for listening, consumes less memory.ָ������״̬�Ŀ��ƿ�
+ *
+ * @note The original tcp_pcb is freed. This function therefore has to be
+ * called like this:
+ * tpcb = tcp_listen(tpcb);
+ */
+struct tcp_pcb *
+tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog)
+{
+ struct tcp_pcb_listen *lpcb;
+
+ LWIP_UNUSED_ARG(backlog);
+ LWIP_ERROR("tcp_listen: pcb already connected", pcb->state == CLOSED, return NULL);
+
+ /* already listening? */
+ if (pcb->state == LISTEN) {
+ return pcb;
+ }
+#if SO_REUSE
+ if ((pcb->so_options & SOF_REUSEADDR) != 0) {
+ /* Since SOF_REUSEADDR allows reusing a local address before the pcb's usage
+ is declared (listen-/connection-pcb), we have to make sure now that
+ this port is only used once for every local IP. */
+ for(lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
+ if (lpcb->local_port == pcb->local_port) {
+ if (ip_addr_cmp(&lpcb->local_ip, &pcb->local_ip)) {
+ /* this address/port is already used */
+ return NULL;
+ }
+ }
+ }
+ }
+#endif /* SO_REUSE */
+ lpcb = (struct tcp_pcb_listen *)memp_malloc(MEMP_TCP_PCB_LISTEN);//�����ڴ�ؿռ�
+ if (lpcb == NULL) {
+ return NULL;
+ }
+ lpcb->callback_arg = pcb->callback_arg;
+ lpcb->local_port = pcb->local_port;
+ lpcb->state = LISTEN;
+ lpcb->prio = pcb->prio;
+ lpcb->so_options = pcb->so_options;
+ lpcb->so_options |= SOF_ACCEPTCONN;
+ lpcb->ttl = pcb->ttl;
+ lpcb->tos = pcb->tos;
+ ip_addr_copy(lpcb->local_ip, pcb->local_ip);
+ if (pcb->local_port != 0) {
+ TCP_RMV(&tcp_bound_pcbs, pcb);
+ }
+ memp_free(MEMP_TCP_PCB, pcb);
+#if LWIP_CALLBACK_API
+ lpcb->accept = tcp_accept_null;//���ܿͻ������ӵ�Ĭ�ϻص�����
+#endif /* LWIP_CALLBACK_API */
+#if TCP_LISTEN_BACKLOG
+ lpcb->accepts_pending = 0;
+ lpcb->backlog = (backlog ? backlog : 1);
+#endif /* TCP_LISTEN_BACKLOG */
+ TCP_REG(&tcp_listen_pcbs.pcbs, (struct tcp_pcb *)lpcb);//���ƿ����tcp_listen_pcbs�����ײ�
+ return (struct tcp_pcb *)lpcb;
+}
+
+/**
+ * Update the state that tracks the available window space to advertise.
+ *
+ * Returns how much extra window would be advertised if we sent an
+ * update now.
+ */
+u32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb)
+{
+ u32_t new_right_edge = pcb->rcv_nxt + pcb->rcv_wnd;
+
+ if (TCP_SEQ_GEQ(new_right_edge, pcb->rcv_ann_right_edge + LWIP_MIN((TCP_WND / 2), pcb->mss))) {
+ /* we can advertise more window */
+ pcb->rcv_ann_wnd = pcb->rcv_wnd;
+ return new_right_edge - pcb->rcv_ann_right_edge;
+ } else {
+ if (TCP_SEQ_GT(pcb->rcv_nxt, pcb->rcv_ann_right_edge)) {
+ /* Can happen due to other end sending out of advertised window,
+ * but within actual available (but not yet advertised) window */
+ pcb->rcv_ann_wnd = 0;
+ } else {
+ /* keep the right edge of window constant */
+ u32_t new_rcv_ann_wnd = pcb->rcv_ann_right_edge - pcb->rcv_nxt;
+ LWIP_ASSERT("new_rcv_ann_wnd <= 0xffff", new_rcv_ann_wnd <= 0xffff);
+ pcb->rcv_ann_wnd = (u16_t)new_rcv_ann_wnd;
+ }
+ return 0;
+ }
+}
+
+/**
+ * This function should be called by the application when it has
+ * processed the data. The purpose is to advertise a larger window
+ * when the data has been processed.
+ *Ӧ�ó�����ݴ�����Ϻ�֪ͨ�ں˸��½��մ���
+ * @param pcb the tcp_pcb for which data is read
+ * @param len the amount of bytes that have been read by the application
+ */
+void
+tcp_recved(struct tcp_pcb *pcb, u16_t len)
+{
+ int wnd_inflation;
+
+ LWIP_ASSERT("tcp_recved: len would wrap rcv_wnd\n",
+ len <= 0xffff - pcb->rcv_wnd );
+
+ pcb->rcv_wnd += len;
+ if (pcb->rcv_wnd > TCP_WND) {
+ pcb->rcv_wnd = TCP_WND;
+ }
+
+ wnd_inflation = tcp_update_rcv_ann_wnd(pcb);
+
+ /* If the change in the right edge of window is significant (default
+ * watermark is TCP_WND/4), then send an explicit update now.
+ * Otherwise wait for a packet to be sent in the normal course of
+ * events (or more window to be available later) */
+ if (wnd_inflation >= TCP_WND_UPDATE_THRESHOLD) {
+ tcp_ack_now(pcb);
+ tcp_output(pcb);
+ }
+
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_recved: recveived %"U16_F" bytes, wnd %"U16_F" (%"U16_F").\n",
+ len, pcb->rcv_wnd, TCP_WND - pcb->rcv_wnd));
+}
+
+/**
+ * A nastly hack featuring 'goto' statements that allocates a
+ * new TCP local port.
+ *
+ * @return a new (free) local TCP port number
+ */
+static u16_t
+tcp_new_port(void)
+{
+ int i;
+ struct tcp_pcb *pcb;
+#ifndef TCP_LOCAL_PORT_RANGE_START
+#define TCP_LOCAL_PORT_RANGE_START 4096
+#define TCP_LOCAL_PORT_RANGE_END 0x7fff
+#endif
+ static u16_t port = TCP_LOCAL_PORT_RANGE_START;
+
+ again:
+ if (++port >= TCP_LOCAL_PORT_RANGE_END) {
+ port = TCP_LOCAL_PORT_RANGE_START;
+ }
+ /* Check all PCB lists. */
+ for (i = 0; i < NUM_TCP_PCB_LISTS; i++) {
+ for(pcb = *tcp_pcb_lists[i]; pcb != NULL; pcb = pcb->next) {
+ if (pcb->local_port == port) {
+ goto again;
+ }
+ }
+ }
+ return port;
+}
+
+/**
+ * Connects to another host. The function given as the "connected"
+ * argument will be called when the connection has been established.
+ *�����������һ��SYN���ֱ���
+ * @param pcb the tcp_pcb used to establish the connection �����Ŀ��ƿ����
+ * @param ipaddr the remote ip address to connect to ������IP��ַ
+ * @param port the remote tcp port to connect to �������˿ں�
+ * @param connected callback function to call when connected (or on error)
+ * @return ERR_VAL if invalid arguments are given
+ * ERR_OK if connect request has been sent
+ * other err_t values if connect request couldn't be sent
+ */
+err_t
+tcp_connect(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port,
+ tcp_connected_fn connected)
+{
+ err_t ret;
+ u32_t iss;
+ u16_t old_local_port;
+
+ LWIP_ERROR("tcp_connect: can only connected from state CLOSED", pcb->state == CLOSED, return ERR_ISCONN);
+
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_connect to port %"U16_F"\n", port));
+ if (ipaddr != NULL) {
+ pcb->remote_ip = *ipaddr;//������IP��ַ��Ч�������Ӽ�¼�м�¼��IP��ַ�����ش���
+ } else {
+ return ERR_VAL;
+ }
+ pcb->remote_port = port;//��¼�������˿�(Ŀ�Ķ˿�)
+
+ /* check if we have a route to the remote host */
+ if (ip_addr_isany(&(pcb->local_ip))) {
+ /* no local IP address set, yet. */
+ struct netif *netif = ip_route(&(pcb->remote_ip));
+ if (netif == NULL) {
+ /* Don't even try to send a SYN packet if we have no route
+ since that will fail. */
+ return ERR_RTE;
+ }
+ /* Use the netif's IP address as local address. */
+ ip_addr_copy(pcb->local_ip, netif->ip_addr);
+ }
+
+ old_local_port = pcb->local_port;
+ if (pcb->local_port == 0) {
+ pcb->local_port = tcp_new_port();
+
+ }
+#if SO_REUSE
+ if ((pcb->so_options & SOF_REUSEADDR) != 0) {
+ /* Since SOF_REUSEADDR allows reusing a local address, we have to make sure
+ now that the 5-tuple is unique. */
+ struct tcp_pcb *cpcb;
+ int i;
+ /* Don't check listen- and bound-PCBs, check active- and TIME-WAIT PCBs. */
+ for (i = 2; i < NUM_TCP_PCB_LISTS; i++) {
+ for(cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) {
+ if ((cpcb->local_port == pcb->local_port) &&
+ (cpcb->remote_port == port) &&
+ ip_addr_cmp(&cpcb->local_ip, &pcb->local_ip) &&
+ ip_addr_cmp(&cpcb->remote_ip, ipaddr)) {
+ /* linux returns EISCONN here, but ERR_USE should be OK for us */
+ return ERR_USE;
+ }
+ }
+ }
+ }
+#endif /* SO_REUSE */
+ iss = tcp_next_iss();//��ʼ�����
+ pcb->rcv_nxt = 0;//���÷��ʹ��ڵĸ����ֶ�
+ pcb->snd_nxt = iss;
+ pcb->lastack = iss - 1;
+ pcb->snd_lbb = iss - 1;
+ pcb->rcv_wnd = TCP_WND;//����Ĭ�Ͻ��մ��ڸ����ֶ�ֵ
+ pcb->rcv_ann_wnd = TCP_WND;
+ pcb->rcv_ann_right_edge = pcb->rcv_nxt;
+ pcb->snd_wnd = TCP_WND;
+ /* As initial send MSS, we use TCP_MSS but limit it to 536.
+ The send MSS is updated when an MSS option is received. */
+ pcb->mss = (TCP_MSS > 536) ? 536 : TCP_MSS;//��ʼ������Ķδ�С
+#if TCP_CALCULATE_EFF_SEND_MSS
+ pcb->mss = tcp_eff_send_mss(pcb->mss, ipaddr);
+#endif /* TCP_CALCULATE_EFF_SEND_MSS */
+ pcb->cwnd = 1;//��ʼ�������
+ pcb->ssthresh = pcb->mss * 10;
+#if LWIP_CALLBACK_API
+ pcb->connected = connected;//ע��connected�ص�����
+#else /* LWIP_CALLBACK_API */
+ LWIP_UNUSED_ARG(connected);
+#endif /* LWIP_CALLBACK_API */
+
+ /* Send a SYN together with the MSS option. */
+ ret = tcp_enqueue_flags(pcb, TCP_SYN);
+ if (ret == ERR_OK) {
+ /* SYN segment was enqueued, changed the pcbs state now ���ƿ�����ΪSYN_SENT ״̬*/
+ pcb->state = SYN_SENT;
+ if (old_local_port != 0) {
+ TCP_RMV(&tcp_bound_pcbs, pcb);
+ }
+ TCP_REG(&tcp_active_pcbs, pcb);
+ snmp_inc_tcpactiveopens();
+
+ tcp_output(pcb);//�����ƿ������ӵı��ķ��ͳ�ȥ
+ }
+ return ret;
+}
+
+/**
+ * Called every 500 ms and implements the retransmission timer and the timer that
+ * removes PCBs that have been in TIME-WAIT for enough time. It also increments
+ * various timers such as the inactivity timer in each PCB.
+ *
+ * Automatically called from tcp_tmr().
+ */
+void
+tcp_slowtmr(void)
+{
+ struct tcp_pcb *pcb, *prev;
+ u16_t eff_wnd;
+ u8_t pcb_remove; /* flag if a PCB should be removed */
+ u8_t pcb_reset; /* flag if a RST should be sent when removing */
+ err_t err;
+
+ err = ERR_OK;
+
+ ++tcp_ticks;
+
+ /* Steps through all of the active PCBs. */
+ prev = NULL;
+ pcb = tcp_active_pcbs;
+ if (pcb == NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: no active pcbs\n"));
+ }
+ while (pcb != NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: processing active pcb\n"));
+ LWIP_ASSERT("tcp_slowtmr: active pcb->state != CLOSED\n", pcb->state != CLOSED);
+ LWIP_ASSERT("tcp_slowtmr: active pcb->state != LISTEN\n", pcb->state != LISTEN);
+ LWIP_ASSERT("tcp_slowtmr: active pcb->state != TIME-WAIT\n", pcb->state != TIME_WAIT);
+
+ pcb_remove = 0;
+ pcb_reset = 0;
+
+ if (pcb->state == SYN_SENT && pcb->nrtx == TCP_SYNMAXRTX) {
+ ++pcb_remove;
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max SYN retries reached\n"));
+ }
+ else if (pcb->nrtx == TCP_MAXRTX) {
+ ++pcb_remove;
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max DATA retries reached\n"));
+ } else {
+ if (pcb->persist_backoff > 0) {
+ /* If snd_wnd is zero, use persist timer to send 1 byte probes
+ * instead of using the standard retransmission mechanism. */
+ pcb->persist_cnt++;
+ if (pcb->persist_cnt >= tcp_persist_backoff[pcb->persist_backoff-1]) {
+ pcb->persist_cnt = 0;
+ if (pcb->persist_backoff < sizeof(tcp_persist_backoff)) {
+ pcb->persist_backoff++;
+ }
+ tcp_zero_window_probe(pcb);
+ }
+ } else {
+ /* Increase the retransmission timer if it is running */
+ if(pcb->rtime >= 0)
+ ++pcb->rtime;
+
+ if (pcb->unacked != NULL && pcb->rtime >= pcb->rto) {
+ /* Time for a retransmission. */
+ LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_slowtmr: rtime %"S16_F
+ " pcb->rto %"S16_F"\n",
+ pcb->rtime, pcb->rto));
+
+ /* Double retransmission time-out unless we are trying to
+ * connect to somebody (i.e., we are in SYN_SENT). */
+ if (pcb->state != SYN_SENT) {
+ pcb->rto = ((pcb->sa >> 3) + pcb->sv) << tcp_backoff[pcb->nrtx];
+// if (pcb->rto >= TCP_MAXRTO)
+// pcb->rto >>= 1;
+ }
+
+ /* Reset the retransmission timer. */
+ pcb->rtime = 0;
+
+ /* Reduce congestion window and ssthresh. */
+ eff_wnd = LWIP_MIN(pcb->cwnd, pcb->snd_wnd);
+ pcb->ssthresh = eff_wnd >> 1;
+ if (pcb->ssthresh < (pcb->mss << 1)) {
+ pcb->ssthresh = (pcb->mss << 1);
+ }
+ pcb->cwnd = pcb->mss;
+ LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: cwnd %"U16_F
+ " ssthresh %"U16_F"\n",
+ pcb->cwnd, pcb->ssthresh));
+
+ /* The following needs to be called AFTER cwnd is set to one
+ mss - STJ */
+ tcp_rexmit_rto(pcb);
+ }
+ }
+ }
+ /* Check if this PCB has stayed too long in FIN-WAIT-2 */
+ if (pcb->state == FIN_WAIT_2) {
+ if ((u32_t)(tcp_ticks - pcb->tmr) >
+ TCP_FIN_WAIT_TIMEOUT / TCP_SLOW_INTERVAL) {
+ ++pcb_remove;
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in FIN-WAIT-2\n"));
+ }
+ }
+
+ /* Check if KEEPALIVE should be sent */
+ if((pcb->so_options & SOF_KEEPALIVE) &&
+ ((pcb->state == ESTABLISHED) ||
+ (pcb->state == CLOSE_WAIT))) {
+#if LWIP_TCP_KEEPALIVE
+ if((u32_t)(tcp_ticks - pcb->tmr) >
+ (pcb->keep_idle + (pcb->keep_cnt*pcb->keep_intvl))
+ / TCP_SLOW_INTERVAL)
+#else
+ if((u32_t)(tcp_ticks - pcb->tmr) >
+ (pcb->keep_idle + TCP_MAXIDLE) / TCP_SLOW_INTERVAL)
+#endif /* LWIP_TCP_KEEPALIVE */
+ {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: KEEPALIVE timeout. Aborting connection to %"U16_F".%"U16_F".%"U16_F".%"U16_F".\n",
+ ip4_addr1_16(&pcb->remote_ip), ip4_addr2_16(&pcb->remote_ip),
+ ip4_addr3_16(&pcb->remote_ip), ip4_addr4_16(&pcb->remote_ip)));
+
+ ++pcb_remove;
+ ++pcb_reset;
+ }
+#if LWIP_TCP_KEEPALIVE
+ else if((u32_t)(tcp_ticks - pcb->tmr) >
+ (pcb->keep_idle + pcb->keep_cnt_sent * pcb->keep_intvl)
+ / TCP_SLOW_INTERVAL)
+#else
+ else if((u32_t)(tcp_ticks - pcb->tmr) >
+ (pcb->keep_idle + pcb->keep_cnt_sent * TCP_KEEPINTVL_DEFAULT)
+ / TCP_SLOW_INTERVAL)
+#endif /* LWIP_TCP_KEEPALIVE */
+ {
+ tcp_keepalive(pcb);
+ pcb->keep_cnt_sent++;
+ }
+ }
+
+ /* If this PCB has queued out of sequence data, but has been
+ inactive for too long, will drop the data (it will eventually
+ be retransmitted). */
+#if TCP_QUEUE_OOSEQ
+ if (pcb->ooseq != NULL &&
+ (u32_t)tcp_ticks - pcb->tmr >= pcb->rto * TCP_OOSEQ_TIMEOUT) {
+ tcp_segs_free(pcb->ooseq);
+ pcb->ooseq = NULL;
+ LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: dropping OOSEQ queued data\n"));
+ }
+#endif /* TCP_QUEUE_OOSEQ */
+
+ /* Check if this PCB has stayed too long in SYN-RCVD */
+ if (pcb->state == SYN_RCVD) {
+ if ((u32_t)(tcp_ticks - pcb->tmr) >
+ TCP_SYN_RCVD_TIMEOUT / TCP_SLOW_INTERVAL) {
+ ++pcb_remove;
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in SYN-RCVD\n"));
+ }
+ }
+
+ /* Check if this PCB has stayed too long in LAST-ACK */
+ if (pcb->state == LAST_ACK) {
+ if ((u32_t)(tcp_ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) {
+ ++pcb_remove;
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in LAST-ACK\n"));
+ }
+ }
+
+ /* If the PCB should be removed, do it. */
+ if (pcb_remove) {
+ struct tcp_pcb *pcb2;
+ tcp_pcb_purge(pcb);
+ /* Remove PCB from tcp_active_pcbs list. */
+ if (prev != NULL) {
+ LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_active_pcbs", pcb != tcp_active_pcbs);
+ prev->next = pcb->next;
+ } else {
+ /* This PCB was the first. */
+ LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_active_pcbs", tcp_active_pcbs == pcb);
+ tcp_active_pcbs = pcb->next;
+ }
+
+ if (pcb_reset) {
+ tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip,
+ pcb->local_port, pcb->remote_port);
+ }
+
+ TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_ABRT);
+ pcb2 = pcb;
+ pcb = pcb->next;
+ memp_free(MEMP_TCP_PCB, pcb2);
+ } else {
+ /* get the 'next' element now and work with 'prev' below (in case of abort) */
+ prev = pcb;
+ pcb = pcb->next;
+
+ /* We check if we should poll the connection. */
+ ++prev->polltmr;
+ if (prev->polltmr >= prev->pollinterval) {
+ prev->polltmr = 0;
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: polling application\n"));
+ TCP_EVENT_POLL(prev, err);
+ /* if err == ERR_ABRT, 'prev' is already deallocated */
+ if (err == ERR_OK) {
+ tcp_output(prev);
+ }
+ }
+ }
+ }
+
+
+ /* Steps through all of the TIME-WAIT PCBs. */
+ prev = NULL;
+ pcb = tcp_tw_pcbs;
+ while (pcb != NULL) {
+ LWIP_ASSERT("tcp_slowtmr: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT);
+ pcb_remove = 0;
+
+ /* Check if this PCB has stayed long enough in TIME-WAIT */
+ if ((u32_t)(tcp_ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) {
+ ++pcb_remove;
+ }
+
+
+
+ /* If the PCB should be removed, do it. */
+ if (pcb_remove) {
+ struct tcp_pcb *pcb2;
+ tcp_pcb_purge(pcb);
+ /* Remove PCB from tcp_tw_pcbs list. */
+ if (prev != NULL) {
+ LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_tw_pcbs", pcb != tcp_tw_pcbs);
+ prev->next = pcb->next;
+ } else {
+ /* This PCB was the first. */
+ LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_tw_pcbs", tcp_tw_pcbs == pcb);
+ tcp_tw_pcbs = pcb->next;
+ }
+ pcb2 = pcb;
+ pcb = pcb->next;
+ memp_free(MEMP_TCP_PCB, pcb2);
+ } else {
+ prev = pcb;
+ pcb = pcb->next;
+ }
+ }
+}
+
+/**
+ * Is called every TCP_FAST_INTERVAL (250 ms) and process data previously
+ * "refused" by upper layer (application) and sends delayed ACKs.
+ *
+ * Automatically called from tcp_tmr().
+ */
+void
+tcp_fasttmr(void)
+{
+ struct tcp_pcb *pcb = tcp_active_pcbs;
+
+ while(pcb != NULL) {
+ struct tcp_pcb *next = pcb->next;
+ /* If there is data which was previously "refused" by upper layer */
+ if (pcb->refused_data != NULL) {
+ /* Notify again application with data previously received. */
+ err_t err;
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_fasttmr: notify kept packet\n"));
+ TCP_EVENT_RECV(pcb, pcb->refused_data, ERR_OK, err);
+ if (err == ERR_OK) {
+ pcb->refused_data = NULL;
+ } else if (err == ERR_ABRT) {
+ /* if err == ERR_ABRT, 'pcb' is already deallocated */
+ pcb = NULL;
+ }
+ }
+
+ /* send delayed ACKs */
+ if (pcb && (pcb->flags & TF_ACK_DELAY)) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_fasttmr: delayed ACK\n"));
+ tcp_ack_now(pcb);
+ tcp_output(pcb);
+ pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
+ }
+
+ pcb = next;
+ }
+}
+
+/**
+ * Deallocates a list of TCP segments (tcp_seg structures).
+ *
+ * @param seg tcp_seg list of TCP segments to free
+ */
+void
+tcp_segs_free(struct tcp_seg *seg)
+{
+ while (seg != NULL) {
+ struct tcp_seg *next = seg->next;
+ tcp_seg_free(seg);
+ seg = next;
+ }
+}
+
+/**
+ * Frees a TCP segment (tcp_seg structure).
+ *
+ * @param seg single tcp_seg to free
+ */
+void
+tcp_seg_free(struct tcp_seg *seg)
+{
+ if (seg != NULL) {
+ if (seg->p != NULL) {
+ pbuf_free(seg->p);
+#if TCP_DEBUG
+ seg->p = NULL;
+#endif /* TCP_DEBUG */
+ }
+ memp_free(MEMP_TCP_SEG, seg);
+ }
+}
+
+/**
+ * Sets the priority of a connection.
+ *
+ * @param pcb the tcp_pcb to manipulate
+ * @param prio new priority
+ */
+void
+tcp_setprio(struct tcp_pcb *pcb, u8_t prio)
+{
+ pcb->prio = prio;
+}
+
+#if TCP_QUEUE_OOSEQ
+/**
+ * Returns a copy of the given TCP segment.
+ * The pbuf and data are not copied, only the pointers
+ *
+ * @param seg the old tcp_seg
+ * @return a copy of seg
+ */
+struct tcp_seg *
+tcp_seg_copy(struct tcp_seg *seg)
+{
+ struct tcp_seg *cseg;
+
+ cseg = (struct tcp_seg *)memp_malloc(MEMP_TCP_SEG);
+ if (cseg == NULL) {
+ return NULL;
+ }
+ SMEMCPY((u8_t *)cseg, (const u8_t *)seg, sizeof(struct tcp_seg));
+ pbuf_ref(cseg->p);
+ return cseg;
+}
+#endif /* TCP_QUEUE_OOSEQ */
+
+#if LWIP_CALLBACK_API
+/**
+ * Default receive callback that is called if the user didn't register
+ * a recv callback for the pcb.
+ */
+err_t
+tcp_recv_null(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
+{
+ LWIP_UNUSED_ARG(arg);
+ if (p != NULL) {
+ tcp_recved(pcb, p->tot_len);
+ pbuf_free(p);
+ } else if (err == ERR_OK) {
+ return tcp_close(pcb);
+ }
+ return ERR_OK;
+}
+#endif /* LWIP_CALLBACK_API */
+
+/**
+ * Kills the oldest active connection that has lower priority than prio.
+ *
+ * @param prio minimum priority
+ */
+static void ICACHE_FLASH_ATTR
+tcp_kill_prio(u8_t prio)
+{
+ struct tcp_pcb *pcb, *inactive;
+ u32_t inactivity;
+ u8_t mprio;
+
+
+ mprio = TCP_PRIO_MAX;
+
+ /* We kill the oldest active connection that has lower priority than prio. */
+ inactivity = 0;
+ inactive = NULL;
+ for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
+ if (pcb->prio <= prio &&
+ pcb->prio <= mprio &&
+ (u32_t)(tcp_ticks - pcb->tmr) >= inactivity) {
+ inactivity = tcp_ticks - pcb->tmr;
+ inactive = pcb;
+ mprio = pcb->prio;
+ }
+ }
+ if (inactive != NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_prio: killing oldest PCB %p (%"S32_F")\n",
+ (void *)inactive, inactivity));
+ tcp_abort(inactive);
+ }
+}
+
+/**
+ * Kills the oldest connection that is in TIME_WAIT state.
+ * Called from tcp_alloc() if no more connections are available.
+ */
+static void ICACHE_FLASH_ATTR
+tcp_kill_timewait(void)
+{
+ struct tcp_pcb *pcb, *inactive;
+ u32_t inactivity;
+
+ inactivity = 0;
+ inactive = NULL;
+ /* Go through the list of TIME_WAIT pcbs and get the oldest pcb. */
+ for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
+ if ((u32_t)(tcp_ticks - pcb->tmr) >= inactivity) {
+ inactivity = tcp_ticks - pcb->tmr;
+ inactive = pcb;
+ }
+ }
+ if (inactive != NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_timewait: killing oldest TIME-WAIT PCB %p (%"S32_F")\n",
+ (void *)inactive, inactivity));
+ tcp_abort(inactive);
+ }
+}
+
+/**
+ * Allocate a new tcp_pcb structure.
+ *����һ��TCP���ƿ�ṹ������ʼ������ֶ�
+ * @param prio priority for the new pcb �¿��ƿ�����ȼ�
+ * @return a new tcp_pcb that initially is in state CLOSED ָ���¿��ƿ��ָ��
+ */
+struct tcp_pcb *
+tcp_alloc(u8_t prio)
+{
+ struct tcp_pcb *pcb;
+ u32_t iss;
+
+ pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB);//�����ڴ�ؿռ�
+ if (pcb == NULL) {
+ //os_printf("tcp_pcb memory is fail\n");
+ /* Try killing oldest connection in TIME-WAIT. */
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing off oldest TIME-WAIT connection\n"));
+ tcp_kill_timewait();
+ /* Try to allocate a tcp_pcb again. */
+ pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB);
+ if (pcb == NULL) {
+ /* Try killing active connections with lower priority than the new one. */
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing connection with prio lower than %d\n", prio));
+ tcp_kill_prio(prio);
+ /* Try to allocate a tcp_pcb again. */
+ pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB);
+ if (pcb != NULL) {
+ /* adjust err stats: memp_malloc failed twice before */
+ MEMP_STATS_DEC(err, MEMP_TCP_PCB);
+ }
+ }
+ if (pcb != NULL) {
+ /* adjust err stats: timewait PCB was freed above */
+ MEMP_STATS_DEC(err, MEMP_TCP_PCB);
+ }
+ }
+ if (pcb != NULL) {
+ memset(pcb, 0, sizeof(struct tcp_pcb)); //��0
+ pcb->prio = prio; //�������ȼ�
+ pcb->snd_buf = TCP_SND_BUF; //��ʹ�õķ��ͻ������С
+ pcb->snd_queuelen = 0; //��������ռ�õ�pbuf����
+ pcb->rcv_wnd = TCP_WND; //���մ���
+ pcb->rcv_ann_wnd = TCP_WND; //ͨ����մ���
+ pcb->tos = 0; //��������
+ pcb->ttl = TCP_TTL; //ttl�ֶ�
+ /* As initial send MSS, we use TCP_MSS but limit it to 536.
+ The send MSS is updated when an MSS option is received. */
+ pcb->mss = (TCP_MSS > 536) ? 536 : TCP_MSS; //��ʼ������Ķ�
+ pcb->rto = 1000 / TCP_SLOW_INTERVAL; //��ʼ����ʱʱ��
+ pcb->sa = 0; //��ʼ����RTT��صIJ���
+ pcb->sv = 1000 / TCP_SLOW_INTERVAL;
+ pcb->rtime = -1;
+ pcb->cwnd = 1; //��ʼ�������
+ iss = tcp_next_iss(); //��ó�ʼ���к�
+ pcb->snd_wl2 = iss; //��ʼ�����ʹ��ڸ����ֶ�
+ pcb->snd_nxt = iss;
+ pcb->lastack = iss;
+ pcb->snd_lbb = iss;
+ pcb->tmr = tcp_ticks; //��¼���ƿ鴴��ϵͳʱ��
+
+ pcb->polltmr = 0; //����������¼���ʱ��
+
+#if LWIP_CALLBACK_API
+ pcb->recv = tcp_recv_null; //ע�������ݵ�Ĭ���ϲ㺯��
+#endif /* LWIP_CALLBACK_API */
+
+ /* Init KEEPALIVE timer */
+ pcb->keep_idle = TCP_KEEPIDLE_DEFAULT;
+
+#if LWIP_TCP_KEEPALIVE
+ pcb->keep_intvl = TCP_KEEPINTVL_DEFAULT;
+ pcb->keep_cnt = TCP_KEEPCNT_DEFAULT;
+#endif /* LWIP_TCP_KEEPALIVE */
+
+ pcb->keep_cnt_sent = 0; //���ķ��ʹ���
+ }
+ return pcb;
+}
+
+/**
+ * Creates a new TCP protocol control block but doesn't place it on
+ * any of the TCP PCB lists.
+ * The pcb is not put on any list until binding using tcp_bind().
+ *
+ * @internal: Maybe there should be a idle TCP PCB list where these
+ * PCBs are put on. Port reservation using tcp_bind() is implemented but
+ * allocated pcbs that are not bound can't be killed automatically if wanting
+ * to allocate a pcb with higher prio (@see tcp_kill_prio())
+ *
+ * @return a new tcp_pcb that initially is in state CLOSED
+ */
+struct tcp_pcb *
+tcp_new(void)
+{
+ return tcp_alloc(TCP_PRIO_NORMAL);
+}
+
+/**
+ * Used to specify the argument that should be passed callback
+ * functions.
+ *����ƿ��callback_arg�ֶ�ע���û���ݣ���tcp_recv�Ⱥ���ص�ʱ��
+* ���ֶν���Ϊ����ݸ��������
+ * @param pcb tcp_pcb to set the callback argument
+ * @param arg void pointer argument to pass to callback functions
+ */
+void
+tcp_arg(struct tcp_pcb *pcb, void *arg)
+{
+ pcb->callback_arg = arg;
+}
+#if LWIP_CALLBACK_API
+
+/**
+ * Used to specify the function that should be called when a TCP
+ * connection receives data.
+ *����ƿ��recv�ֶ�ע�ắ���յ����ʱ�ص�
+ * @param pcb tcp_pcb to set the recv callback
+ * @param recv callback function to call for this pcb when data is received
+ */
+void
+tcp_recv(struct tcp_pcb *pcb, tcp_recv_fn recv)
+{
+ pcb->recv = recv;
+}
+
+/**
+ * Used to specify the function that should be called when TCP data
+ * has been successfully delivered to the remote host.
+ *����ƿ�send �ֶ�ע�ắ����ݷ��ͳɹ���ص�
+ * @param pcb tcp_pcb to set the sent callback
+ * @param sent callback function to call for this pcb when data is successfully sent
+ */
+void
+tcp_sent(struct tcp_pcb *pcb, tcp_sent_fn sent)
+{
+ pcb->sent = sent;
+}
+
+/**
+ * Used to specify the function that should be called when a fatal error
+ * has occured on the connection.
+ *����ƿ�err �ֶ�ע�ắ�����������ص�
+ * @param pcb tcp_pcb to set the err callback
+ * @param err callback function to call for this pcb when a fatal error
+ * has occured on the connection
+ */
+void
+tcp_err(struct tcp_pcb *pcb, tcp_err_fn err)
+{
+ pcb->errf = err;
+}
+
+/**
+ * Used for specifying the function that should be called when a
+ * LISTENing connection has been connected to another host.
+ *����ƿ��accept�ֶ�ע�ắ����������ʱ�ص�
+ * @param pcb tcp_pcb to set the accept callback
+ * @param accept callback function to call for this pcb when LISTENing
+ * connection has been connected to another host
+ */
+void
+tcp_accept(struct tcp_pcb *pcb, tcp_accept_fn accept)
+{
+ pcb->accept = accept;
+}
+#endif /* LWIP_CALLBACK_API */
+
+
+/**
+ * Used to specify the function that should be called periodically
+ * from TCP. The interval is specified in terms of the TCP coarse
+ * timer interval, which is called twice a second.
+ *����ƿ��POLL�ֶ�ע�ắ��ú��������Ա�����
+ */
+void
+tcp_poll(struct tcp_pcb *pcb, tcp_poll_fn poll, u8_t interval)
+{
+#if LWIP_CALLBACK_API
+ pcb->poll = poll;
+#else /* LWIP_CALLBACK_API */
+ LWIP_UNUSED_ARG(poll);
+#endif /* LWIP_CALLBACK_API */
+ pcb->pollinterval = interval;
+}
+
+/**
+ * Purges a TCP PCB. Removes any buffered data and frees the buffer memory
+ * (pcb->ooseq, pcb->unsent and pcb->unacked are freed).
+ *
+ * @param pcb tcp_pcb to purge. The pcb itself is not deallocated!
+ */
+void
+tcp_pcb_purge(struct tcp_pcb *pcb)
+{
+ if (pcb->state != CLOSED &&
+ pcb->state != TIME_WAIT &&
+ pcb->state != LISTEN) {
+
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge\n"));
+
+#if TCP_LISTEN_BACKLOG
+ if (pcb->state == SYN_RCVD) {
+ /* Need to find the corresponding listen_pcb and decrease its accepts_pending */
+ struct tcp_pcb_listen *lpcb;
+ LWIP_ASSERT("tcp_pcb_purge: pcb->state == SYN_RCVD but tcp_listen_pcbs is NULL",
+ tcp_listen_pcbs.listen_pcbs != NULL);
+ for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
+ if ((lpcb->local_port == pcb->local_port) &&
+ (ip_addr_isany(&lpcb->local_ip) ||
+ ip_addr_cmp(&pcb->local_ip, &lpcb->local_ip))) {
+ /* port and address of the listen pcb match the timed-out pcb */
+ LWIP_ASSERT("tcp_pcb_purge: listen pcb does not have accepts pending",
+ lpcb->accepts_pending > 0);
+ lpcb->accepts_pending--;
+ break;
+ }
+ }
+ }
+#endif /* TCP_LISTEN_BACKLOG */
+
+
+ if (pcb->refused_data != NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->refused_data\n"));
+ pbuf_free(pcb->refused_data);
+ pcb->refused_data = NULL;
+ }
+ if (pcb->unsent != NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: not all data sent\n"));
+ }
+ if (pcb->unacked != NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->unacked\n"));
+ }
+#if TCP_QUEUE_OOSEQ
+ if (pcb->ooseq != NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->ooseq\n"));
+ }
+ tcp_segs_free(pcb->ooseq);
+ pcb->ooseq = NULL;
+#endif /* TCP_QUEUE_OOSEQ */
+
+ /* Stop the retransmission timer as it will expect data on unacked
+ queue if it fires */
+ pcb->rtime = -1;
+
+ tcp_segs_free(pcb->unsent);
+ tcp_segs_free(pcb->unacked);
+ pcb->unacked = pcb->unsent = NULL;
+#if TCP_OVERSIZE
+ pcb->unsent_oversize = 0;
+#endif /* TCP_OVERSIZE */
+ }
+}
+
+/**
+ * Purges the PCB and removes it from a PCB list. Any delayed ACKs are sent first.
+ *
+ * @param pcblist PCB list to purge.
+ * @param pcb tcp_pcb to purge. The pcb itself is NOT deallocated!
+ */
+void
+tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb)
+{
+ TCP_RMV(pcblist, pcb);
+
+ tcp_pcb_purge(pcb);
+
+ /* if there is an outstanding delayed ACKs, send it */
+ if (pcb->state != TIME_WAIT &&
+ pcb->state != LISTEN &&
+ pcb->flags & TF_ACK_DELAY) {
+ pcb->flags |= TF_ACK_NOW;
+ tcp_output(pcb);
+ }
+
+ if (pcb->state != LISTEN) {
+ LWIP_ASSERT("unsent segments leaking", pcb->unsent == NULL);
+ LWIP_ASSERT("unacked segments leaking", pcb->unacked == NULL);
+#if TCP_QUEUE_OOSEQ
+ LWIP_ASSERT("ooseq segments leaking", pcb->ooseq == NULL);
+#endif /* TCP_QUEUE_OOSEQ */
+ }
+
+ pcb->state = CLOSED;
+
+ LWIP_ASSERT("tcp_pcb_remove: tcp_pcbs_sane()", tcp_pcbs_sane());
+}
+
+/**
+ * Calculates a new initial sequence number for new connections.
+ *
+ * @return u32_t pseudo random sequence number
+ */
+u32_t
+tcp_next_iss(void)
+{
+ static u32_t iss = 6510;
+
+ iss += tcp_ticks; /* XXX */
+ return iss;
+}
+
+#if TCP_CALCULATE_EFF_SEND_MSS
+/**
+ * Calcluates the effective send mss that can be used for a specific IP address
+ * by using ip_route to determin the netif used to send to the address and
+ * calculating the minimum of TCP_MSS and that netif's mtu (if set).
+ */
+u16_t
+tcp_eff_send_mss(u16_t sendmss, ip_addr_t *addr)
+{
+ u16_t mss_s;
+ struct netif *outif;
+
+ outif = ip_route(addr);
+ if ((outif != NULL) && (outif->mtu != 0)) {
+ mss_s = outif->mtu - IP_HLEN - TCP_HLEN;
+ /* RFC 1122, chap 4.2.2.6:
+ * Eff.snd.MSS = min(SendMSS+20, MMS_S) - TCPhdrsize - IPoptionsize
+ * We correct for TCP options in tcp_write(), and don't support IP options.
+ */
+ sendmss = LWIP_MIN(sendmss, mss_s);
+ }
+ return sendmss;
+}
+#endif /* TCP_CALCULATE_EFF_SEND_MSS */
+
+const char*
+tcp_debug_state_str(enum tcp_state s)
+{
+ return tcp_state_str[s];
+}
+
+#if TCP_DEBUG || TCP_INPUT_DEBUG || TCP_OUTPUT_DEBUG
+/**
+ * Print a tcp header for debugging purposes.
+ *
+ * @param tcphdr pointer to a struct tcp_hdr
+ */
+void
+tcp_debug_print(struct tcp_hdr *tcphdr)
+{
+ LWIP_DEBUGF(TCP_DEBUG, ("TCP header:\n"));
+ LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(TCP_DEBUG, ("| %5"U16_F" | %5"U16_F" | (src port, dest port)\n",
+ ntohs(tcphdr->src), ntohs(tcphdr->dest)));
+ LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(TCP_DEBUG, ("| %010"U32_F" | (seq no)\n",
+ ntohl(tcphdr->seqno)));
+ LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(TCP_DEBUG, ("| %010"U32_F" | (ack no)\n",
+ ntohl(tcphdr->ackno)));
+ LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(TCP_DEBUG, ("| %2"U16_F" | |%"U16_F"%"U16_F"%"U16_F"%"U16_F"%"U16_F"%"U16_F"| %5"U16_F" | (hdrlen, flags (",
+ TCPH_HDRLEN(tcphdr),
+ TCPH_FLAGS(tcphdr) >> 5 & 1,
+ TCPH_FLAGS(tcphdr) >> 4 & 1,
+ TCPH_FLAGS(tcphdr) >> 3 & 1,
+ TCPH_FLAGS(tcphdr) >> 2 & 1,
+ TCPH_FLAGS(tcphdr) >> 1 & 1,
+ TCPH_FLAGS(tcphdr) & 1,
+ ntohs(tcphdr->wnd)));
+ tcp_debug_print_flags(TCPH_FLAGS(tcphdr));
+ LWIP_DEBUGF(TCP_DEBUG, ("), win)\n"));
+ LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(TCP_DEBUG, ("| 0x%04"X16_F" | %5"U16_F" | (chksum, urgp)\n",
+ ntohs(tcphdr->chksum), ntohs(tcphdr->urgp)));
+ LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+}
+
+/**
+ * Print a tcp state for debugging purposes.
+ *
+ * @param s enum tcp_state to print
+ */
+void
+tcp_debug_print_state(enum tcp_state s)
+{
+ LWIP_DEBUGF(TCP_DEBUG, ("State: %s\n", tcp_state_str[s]));
+}
+
+/**
+ * Print tcp flags for debugging purposes.
+ *
+ * @param flags tcp flags, all active flags are printed
+ */
+void
+tcp_debug_print_flags(u8_t flags)
+{
+ if (flags & TCP_FIN) {
+ LWIP_DEBUGF(TCP_DEBUG, ("FIN "));
+ }
+ if (flags & TCP_SYN) {
+ LWIP_DEBUGF(TCP_DEBUG, ("SYN "));
+ }
+ if (flags & TCP_RST) {
+ LWIP_DEBUGF(TCP_DEBUG, ("RST "));
+ }
+ if (flags & TCP_PSH) {
+ LWIP_DEBUGF(TCP_DEBUG, ("PSH "));
+ }
+ if (flags & TCP_ACK) {
+ LWIP_DEBUGF(TCP_DEBUG, ("ACK "));
+ }
+ if (flags & TCP_URG) {
+ LWIP_DEBUGF(TCP_DEBUG, ("URG "));
+ }
+ if (flags & TCP_ECE) {
+ LWIP_DEBUGF(TCP_DEBUG, ("ECE "));
+ }
+ if (flags & TCP_CWR) {
+ LWIP_DEBUGF(TCP_DEBUG, ("CWR "));
+ }
+ LWIP_DEBUGF(TCP_DEBUG, ("\n"));
+}
+
+/**
+ * Print all tcp_pcbs in every list for debugging purposes.
+ */
+void
+tcp_debug_print_pcbs(void)
+{
+ struct tcp_pcb *pcb;
+ LWIP_DEBUGF(TCP_DEBUG, ("Active PCB states:\n"));
+ for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
+ LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ",
+ pcb->local_port, pcb->remote_port,
+ pcb->snd_nxt, pcb->rcv_nxt));
+ tcp_debug_print_state(pcb->state);
+ }
+ LWIP_DEBUGF(TCP_DEBUG, ("Listen PCB states:\n"));
+ for(pcb = (struct tcp_pcb *)tcp_listen_pcbs.pcbs; pcb != NULL; pcb = pcb->next) {
+ LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ",
+ pcb->local_port, pcb->remote_port,
+ pcb->snd_nxt, pcb->rcv_nxt));
+ tcp_debug_print_state(pcb->state);
+ }
+ LWIP_DEBUGF(TCP_DEBUG, ("TIME-WAIT PCB states:\n"));
+ for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
+ LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ",
+ pcb->local_port, pcb->remote_port,
+ pcb->snd_nxt, pcb->rcv_nxt));
+ tcp_debug_print_state(pcb->state);
+ }
+}
+
+/**
+ * Check state consistency of the tcp_pcb lists.
+ */
+s16_t
+tcp_pcbs_sane(void)
+{
+ struct tcp_pcb *pcb;
+ for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
+ LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != CLOSED", pcb->state != CLOSED);
+ LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != LISTEN", pcb->state != LISTEN);
+ LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT);
+ }
+ for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
+ LWIP_ASSERT("tcp_pcbs_sane: tw pcb->state == TIME-WAIT", pcb->state == TIME_WAIT);
+ }
+ return 1;
+}
+#endif /* TCP_DEBUG */
+
+#endif /* LWIP_TCP */
diff --git a/app/lwip/core/tcp_in.c b/app/lwip/core/tcp_in.c
new file mode 100644
index 00000000..2f4a1c42
--- /dev/null
+++ b/app/lwip/core/tcp_in.c
@@ -0,0 +1,1618 @@
+/**
+ * @file
+ * Transmission Control Protocol, incoming traffic
+ *
+ * The input processing functions of the TCP layer.
+ *
+ * These functions are generally called in the order (ip_input() ->)
+ * tcp_input() -> * tcp_process() -> tcp_receive() (-> application).
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/tcp_impl.h"
+#include "lwip/def.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/stats.h"
+#include "lwip/snmp.h"
+#include "arch/perf.h"
+
+/* These variables are global to all functions involved in the input
+ processing of TCP segments. They are set by the tcp_input()
+ function. */
+static struct tcp_seg inseg; //tcp_segṹıĶ
+static struct tcp_hdr *tcphdr; //ĶTCPײ
+static struct ip_hdr *iphdr; //IPݰײ
+static u32_t seqno, ackno; //TCPײֶȷϺֶ
+static u8_t flags; //ײ־ֶ
+static u16_t tcplen; //TCPij
+
+static u8_t recv_flags; //ǰĴ
+static struct pbuf *recv_data; //Ķpbuf
+
+struct tcp_pcb *tcp_input_pcb; //ǰĿƿ
+
+/* Forward declarations. */
+static err_t tcp_process(struct tcp_pcb *pcb)ICACHE_FLASH_ATTR;
+static void tcp_receive(struct tcp_pcb *pcb)ICACHE_FLASH_ATTR;
+static void tcp_parseopt(struct tcp_pcb *pcb)ICACHE_FLASH_ATTR;
+
+static err_t tcp_listen_input(struct tcp_pcb_listen *pcb)ICACHE_FLASH_ATTR;
+static err_t tcp_timewait_input(struct tcp_pcb *pcb)ICACHE_FLASH_ATTR;
+
+/**
+ * The initial input processing of TCP. It verifies the TCP header, demultiplexes
+ * the segment between the PCBs and passes it on to tcp_process(), which implements
+ * the TCP finite state machine. This function is called by the IP layer (in
+ * ip_input()).
+ *
+ * @param p received TCP segment to process (p->payload pointing to the IP header)
+ * @param inp network interface on which this segment was received
+ */
+ /**
+ * TCPʼ봦֤TCPͷIP
+
+ * @p:յTCP(ָIPͷĸ)
+ * @inp:նεӿ
+ */
+void
+tcp_input(struct pbuf *p, struct netif *inp)
+{
+ struct tcp_pcb *pcb, *prev;
+ struct tcp_pcb_listen *lpcb;
+#if SO_REUSE
+ struct tcp_pcb *lpcb_prev = NULL;
+ struct tcp_pcb_listen *lpcb_any = NULL;
+#endif /* SO_REUSE */
+ u8_t hdrlen;
+ err_t err;
+
+ PERF_START;
+
+ TCP_STATS_INC(tcp.recv); //״̬1
+ snmp_inc_tcpinsegs(); //tcpμ1
+
+ iphdr = (struct ip_hdr *)p->payload;// pointer to the actual data in the buffer
+ /*
+ *ͷ(IHL)4λIPЭͷijȣָIPv4Эͷȵֽٸ32λ
+ *IPv4İͷܰɱĿѡ ֶοȷIPv4ݱݲֵƫ
+ *IPv4ͷС20ֽڣIHLֶεСֵʮƱʾ5 (5x4 = 20ֽ)
+ *˵ʾİͷֽ4ֽڵı
+ */
+ tcphdr = (struct tcp_hdr *)((u8_t *)p->payload + IPH_HL(iphdr) * 4);
+
+#if TCP_INPUT_DEBUG
+ tcp_debug_print(tcphdr);
+#endif
+
+ /* remove header from payload */
+ if (pbuf_header(p, -((s16_t)(IPH_HL(iphdr) * 4))) || (p->tot_len < sizeof(struct tcp_hdr))) {
+ /* drop short packets */
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet (%"U16_F" bytes) discarded\n", p->tot_len));
+ TCP_STATS_INC(tcp.lenerr);//ȼ
+ TCP_STATS_INC(tcp.drop);//ֹ
+ snmp_inc_tcpinerrs();
+ pbuf_free(p);//ͷbuffer
+ return;
+ }
+
+ /* Don't even process incoming broadcasts/multicasts. */
+ if (ip_addr_isbroadcast(¤t_iphdr_dest, inp) ||
+ ip_addr_ismulticast(¤t_iphdr_dest)) {
+ TCP_STATS_INC(tcp.proterr);//Э
+ TCP_STATS_INC(tcp.drop);
+ snmp_inc_tcpinerrs();
+ pbuf_free(p);
+ return;
+ }
+
+#if CHECKSUM_CHECK_TCP
+ /* Verify TCP checksum. */
+ if (inet_chksum_pseudo(p, ip_current_src_addr(), ip_current_dest_addr(),
+ IP_PROTO_TCP, p->tot_len) != 0) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packet discarded due to failing checksum 0x%04"X16_F"\n",
+ inet_chksum_pseudo(p, ip_current_src_addr(), ip_current_dest_addr(),
+ IP_PROTO_TCP, p->tot_len)));
+#if TCP_DEBUG
+ tcp_debug_print(tcphdr);
+#endif /* TCP_DEBUG */
+ TCP_STATS_INC(tcp.chkerr);//У
+ TCP_STATS_INC(tcp.drop);
+ snmp_inc_tcpinerrs();
+ pbuf_free(p);
+ return;
+ }
+#endif
+
+ /* Move the payload pointer in the pbuf so that it points to the
+ TCP data instead of the TCP header. */
+ hdrlen = TCPH_HDRLEN(tcphdr);//ͷij
+ if(pbuf_header(p, -(hdrlen * 4))){//TCPͷ0Ϊɹ
+ /* drop short packets */
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet\n"));
+ TCP_STATS_INC(tcp.lenerr);//tcpȴ
+ TCP_STATS_INC(tcp.drop);
+ snmp_inc_tcpinerrs();
+ pbuf_free(p);
+ return;
+ }
+
+ /* Convert fields in TCP header to host byte order. */
+ tcphdr->src = ntohs(tcphdr->src); //תԴַ
+ tcphdr->dest = ntohs(tcphdr->dest); //תĿĵַ
+ seqno = tcphdr->seqno = ntohl(tcphdr->seqno); //תк
+ ackno = tcphdr->ackno = ntohl(tcphdr->ackno); //תӦ
+ tcphdr->wnd = ntohs(tcphdr->wnd); //תtcp
+
+ flags = TCPH_FLAGS(tcphdr);//õtcp headerı־
+ /*
+ *־3λֶΣ
+ * λ1λ
+ * ֶλ1λȡֵ0ݱֶΣ1ݱֶܷΣ
+ * λ1λȡֵ0ݰûа1ݰиİ
+ */
+ tcplen = p->tot_len + ((flags & (TCP_FIN | TCP_SYN)) ? 1 : 0);//TCP_FIN TCP_SYN λ10
+
+ /* Demultiplex an incoming segment. First, we check if it is destined
+ for an active connection. ȣǷһҪһ*/
+ ////////////////////////////////////////////////////////////////////////////////////////
+ prev = NULL;
+ for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {//б
+ LWIP_ASSERT("tcp_input: active pcb->state != CLOSED", pcb->state != CLOSED);
+ LWIP_ASSERT("tcp_input: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT);
+ LWIP_ASSERT("tcp_input: active pcb->state != LISTEN", pcb->state != LISTEN);
+ if (pcb->remote_port == tcphdr->src &&
+ pcb->local_port == tcphdr->dest &&
+ ip_addr_cmp(&(pcb->remote_ip), ¤t_iphdr_src) &&
+ ip_addr_cmp(&(pcb->local_ip), ¤t_iphdr_dest)) {//صĵַ
+
+ /* Move this PCB to the front of the list so that subsequent
+ lookups will be faster (we exploit locality in TCP segment
+ arrivals). */
+ LWIP_ASSERT("tcp_input: pcb->next != pcb (before cache)", pcb->next != pcb);
+ if (prev != NULL) {//ǰһڵ㲻Ϊ
+ prev->next = pcb->next;
+ pcb->next = tcp_active_pcbs;
+ tcp_active_pcbs = pcb;//pcbǰ
+ }
+ LWIP_ASSERT("tcp_input: pcb->next != pcb (after cache)", pcb->next != pcb);
+ break;
+ }
+ prev = pcb;//prevָpcb
+ }
+
+ if (pcb == NULL) {
+ /* If it did not go to an active connection, we check the connections
+ in the TIME-WAIT state. */
+ for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {//ȴ״̬µpcb
+ LWIP_ASSERT("tcp_input: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT);
+ if (pcb->remote_port == tcphdr->src &&
+ pcb->local_port == tcphdr->dest &&
+ ip_addr_cmp(&(pcb->remote_ip), ¤t_iphdr_src) &&
+ ip_addr_cmp(&(pcb->local_ip), ¤t_iphdr_dest)) {
+ /* We don't really care enough to move this PCB to the front
+ of the list since we are not very likely to receive that
+ many segments for connections in TIME-WAIT. */
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for TIME_WAITing connection.\n"));
+ tcp_timewait_input(pcb);//tcp timewait İ
+ pbuf_free(p);
+ return;
+ }
+ }
+
+ /* Finally, if we still did not get a match, we check all PCBs that
+ are LISTENing for incoming connections. */
+ prev = NULL;
+ for(lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {//״̬еpcb
+ if (lpcb->local_port == tcphdr->dest) {
+#if SO_REUSE
+ if (ip_addr_cmp(&(lpcb->local_ip), ¤t_iphdr_dest)) {
+ /* found an exact match */
+ break;
+ } else if(ip_addr_isany(&(lpcb->local_ip))) {
+ /* found an ANY-match */
+ lpcb_any = lpcb;
+ lpcb_prev = prev;
+ }
+#else /* SO_REUSE */
+ if (ip_addr_cmp(&(lpcb->local_ip), ¤t_iphdr_dest) ||
+ ip_addr_isany(&(lpcb->local_ip))) {
+ /* found a match */
+ break;
+ }
+#endif /* SO_REUSE */
+ }
+ prev = (struct tcp_pcb *)lpcb;
+ }
+#if SO_REUSE
+ /* first try specific local IP */
+ if (lpcb == NULL) {
+ /* only pass to ANY if no specific local IP has been found */
+ lpcb = lpcb_any;
+ prev = lpcb_prev;
+ }
+#endif /* SO_REUSE */
+ if (lpcb != NULL) {
+ /* Move this PCB to the front of the list so that subsequent
+ lookups will be faster (we exploit locality in TCP segment
+ arrivals). */
+ if (prev != NULL) {
+ ((struct tcp_pcb_listen *)prev)->next = lpcb->next;
+ /* our successor is the remainder of the listening list */
+ lpcb->next = tcp_listen_pcbs.listen_pcbs;
+ /* put this listening pcb at the head of the listening list */
+ tcp_listen_pcbs.listen_pcbs = lpcb;
+ }
+
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for LISTENing connection.\n"));
+ tcp_listen_input(lpcb);//tcpݰ
+ pbuf_free(p);
+ return;
+ }
+ }
+
+#if TCP_INPUT_DEBUG
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("+-+-+-+-+-+-+-+-+-+-+-+-+-+- tcp_input: flags "));
+ tcp_debug_print_flags(TCPH_FLAGS(tcphdr));
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n"));
+#endif /* TCP_INPUT_DEBUG */
+
+
+ if (pcb != NULL) {
+ /* The incoming segment belongs to a connection. */
+#if TCP_INPUT_DEBUG
+#if TCP_DEBUG
+ tcp_debug_print_state(pcb->state);
+#endif /* TCP_DEBUG */
+#endif /* TCP_INPUT_DEBUG */
+
+ /* Set up a tcp_seg structure. */
+ inseg.next = NULL;
+ inseg.len = p->tot_len;
+ inseg.p = p;
+ inseg.tcphdr = tcphdr;
+
+ recv_data = NULL;
+ recv_flags = 0;
+
+ /* If there is data which was previously "refused" by upper layer */
+ if (pcb->refused_data != NULL) {
+ /* Notify again application with data previously received. */
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: notify kept packet\n"));
+ TCP_EVENT_RECV(pcb, pcb->refused_data, ERR_OK, err);//pcb
+ if (err == ERR_OK) {
+ pcb->refused_data = NULL;
+ } else if ((err == ERR_ABRT) || (tcplen > 0)) {
+ /* if err == ERR_ABRT, 'pcb' is already deallocated */
+ /* Drop incoming packets because pcb is "full" (only if the incoming
+ segment contains data). */
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: drop incoming packets, because pcb is \"full\"\n"));
+ TCP_STATS_INC(tcp.drop);//tcp
+ snmp_inc_tcpinerrs();
+ pbuf_free(p);
+ return;
+ }
+ }
+ tcp_input_pcb = pcb;//¼ǰĴĿƿ
+ err = tcp_process(pcb);//
+ /* A return value of ERR_ABRT means that tcp_abort() was called
+ and that the pcb has been freed. If so, we don't do anything. */
+ if (err != ERR_ABRT) {
+ if (recv_flags & TF_RESET) {
+ /* TF_RESET means that the connection was reset by the other
+ end. We then call the error callback to inform the
+ application that the connection is dead before we
+ deallocate the PCB. */
+ TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_RST);
+ tcp_pcb_remove(&tcp_active_pcbs, pcb);//ɾpcbбеpcb
+ memp_free(MEMP_TCP_PCB, pcb);
+ } else if (recv_flags & TF_CLOSED) {
+ /* The connection has been closed and we will deallocate the
+ PCB. */
+ if (!(pcb->flags & TF_RXCLOSED)) {
+ /* Connection closed although the application has only shut down the
+ tx side: call the PCB's err callback and indicate the closure to
+ ensure the application doesn't continue using the PCB. */
+ TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_CLSD);
+ }
+ tcp_pcb_remove(&tcp_active_pcbs, pcb);
+ memp_free(MEMP_TCP_PCB, pcb);
+ } else {
+ err = ERR_OK;
+ /* If the application has registered a "sent" function to be
+ called when new send buffer space is available, we call it
+ now. */
+ if (pcb->acked > 0) {
+ TCP_EVENT_SENT(pcb, pcb->acked, err);//ݱȷϣصûsend
+ if (err == ERR_ABRT) {
+ goto aborted;
+ }
+ }
+
+ if (recv_data != NULL) {//ݽյ
+ LWIP_ASSERT("pcb->refused_data == NULL", pcb->refused_data == NULL);
+ if (pcb->flags & TF_RXCLOSED) {
+ /* received data although already closed -> abort (send RST) to
+ notify the remote host that not all data has been processed */
+ pbuf_free(recv_data);
+ tcp_abort(pcb);
+ goto aborted;
+ }
+
+ //PSH־ PSH λ
+ //PSH=1ʱҪͷϷ÷ֶΣ
+ //շĽĽӦò㣬д
+
+ if (flags & TCP_PSH) {
+ recv_data->flags |= PBUF_FLAG_PUSH;//bufferӦ
+ }
+
+ /* Notify application that data has been received. */
+ TCP_EVENT_RECV(pcb, recv_data, ERR_OK, err);
+ if (err == ERR_ABRT) {
+ goto aborted;
+ }
+
+ /* If the upper layer can't receive this data, store it */
+ if (err != ERR_OK) {
+ pcb->refused_data = recv_data;
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: keep incoming packet, because pcb is \"full\"\n"));
+ }
+ }
+
+ /* If a FIN segment was received, we call the callback
+ function with a NULL buffer to indicate EOF. */
+ if (recv_flags & TF_GOT_FIN) {
+ /* correct rcv_wnd as the application won't call tcp_recved()
+ for the FIN's seqno */
+ if (pcb->rcv_wnd != TCP_WND) {
+ pcb->rcv_wnd++;
+ }
+
+ TCP_EVENT_CLOSED(pcb, err);
+ if (err == ERR_ABRT) {
+ goto aborted;
+ }
+ }
+
+ tcp_input_pcb = NULL;//ȫֱ
+ /* Try to send something out. */
+ tcp_output(pcb);//
+#if TCP_INPUT_DEBUG
+#if TCP_DEBUG
+ tcp_debug_print_state(pcb->state);
+#endif /* TCP_DEBUG */
+#endif /* TCP_INPUT_DEBUG */
+ }
+ }
+ /* Jump target if pcb has been aborted in a callback (by calling tcp_abort()).
+ Below this line, 'pcb' may not be dereferenced! */
+aborted:
+ tcp_input_pcb = NULL;
+ recv_data = NULL;
+
+ /* give up our reference to inseg.p */
+ if (inseg.p != NULL)
+ {
+ pbuf_free(inseg.p);//ͷbuffer
+ inseg.p = NULL;
+ }
+ } else {
+
+ /* If no matching PCB was found, send a TCP RST (reset) to the
+ sender. */
+ LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_input: no PCB match found, resetting.\n"));
+ if (!(TCPH_FLAGS(tcphdr) & TCP_RST)) {
+ TCP_STATS_INC(tcp.proterr);//Э
+ TCP_STATS_INC(tcp.drop);//tcp
+ tcp_rst(ackno, seqno + tcplen,
+ ip_current_dest_addr(), ip_current_src_addr(),
+ tcphdr->dest, tcphdr->src);//TCPλ
+ }
+ pbuf_free(p);
+ }
+
+ LWIP_ASSERT("tcp_input: tcp_pcbs_sane()", tcp_pcbs_sane());
+ PERF_STOP("tcp_input");
+}
+
+/**
+ * Called by tcp_input() when a segment arrives for a listening
+ * connection (from tcp_input()).
+ *
+ * @param pcb the tcp_pcb_listen for which a segment arrived
+ * @return ERR_OK if the segment was processed
+ * another err_t on error
+ *
+ * @note the return value is not (yet?) used in tcp_input()
+ * @note the segment which arrived is saved in global variables, therefore only the pcb
+ * involved is passed as a parameter to this function
+ */
+ /*
+*LISTEN״̬Ŀƿøú
+*ͨǷһ˿ڲͻSYN
+*
+*/
+static err_t
+tcp_listen_input(struct tcp_pcb_listen *pcb)
+{
+ struct tcp_pcb *npcb;
+ struct tcp_pcb *pactive_pcb;
+ u8_t active_pcb_num = 0;
+ err_t rc;
+
+ /* In the LISTEN state, we check for incoming SYN segments,
+ creates a new PCB, and responds with a SYN|ACK. */
+ if (flags & TCP_ACK) {
+ /* For incoming segments with the ACK flag set, respond with a
+ RST. */
+ LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_listen_input: ACK in LISTEN, sending reset\n"));
+ tcp_rst(ackno + 1, seqno + tcplen,
+ ip_current_dest_addr(), ip_current_src_addr(),
+ tcphdr->dest, tcphdr->src);
+ } else if (flags & TCP_SYN) {//յSYN
+ LWIP_DEBUGF(TCP_DEBUG, ("TCP connection request %"U16_F" -> %"U16_F".\n", tcphdr->src, tcphdr->dest));
+#if TCP_LISTEN_BACKLOG
+ if (pcb->accepts_pending >= pcb->backlog) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: listen backlog exceeded for port %"U16_F"\n", tcphdr->dest));
+ return ERR_ABRT;
+ }
+#endif /* TCP_LISTEN_BACKLOG */
+ for(pactive_pcb = tcp_active_pcbs; pactive_pcb != NULL; pactive_pcb = pactive_pcb->next)
+ active_pcb_num ++;
+ if (active_pcb_num == MEMP_NUM_TCP_PCB){
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: exceed the number of active TCP connections\n"));
+ TCP_STATS_INC(tcp.memerr);
+ return ERR_MEM;
+ }
+ npcb = tcp_alloc(pcb->prio);//ƿ
+ /* If a new PCB could not be created (probably due to lack of memory),
+ we don't do anything, but rely on the sender will retransmit the
+ SYN at a time when we have more memory available. */
+ if (npcb == NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: could not allocate PCB\n"));
+ TCP_STATS_INC(tcp.memerr);//TCPڴ
+ return ERR_MEM;
+ }
+#if TCP_LISTEN_BACKLOG
+ pcb->accepts_pending++;
+#endif /* TCP_LISTEN_BACKLOG */
+ /* Set up the new PCB. */
+ //ƿص4ֶ
+ ip_addr_copy(npcb->local_ip, current_iphdr_dest);
+ npcb->local_port = pcb->local_port;
+ ip_addr_copy(npcb->remote_ip, current_iphdr_src);
+ npcb->remote_port = tcphdr->src;
+
+ //ƿֶ
+ npcb->state = SYN_RCVD;//״̬
+ npcb->rcv_nxt = seqno + 1;//һ
+ npcb->rcv_ann_right_edge = npcb->rcv_nxt;
+ npcb->snd_wnd = tcphdr->wnd;//÷ʹ
+ npcb->ssthresh = npcb->snd_wnd;
+ npcb->snd_wl1 = seqno - 1;/* initialise to seqno-1 to force window update */
+ npcb->callback_arg = pcb->callback_arg;
+#if LWIP_CALLBACK_API
+ npcb->accept = pcb->accept;
+#endif /* LWIP_CALLBACK_API */
+ /* inherit socket options */
+ npcb->so_options = pcb->so_options & SOF_INHERITED;
+ /* Register the new PCB so that we can begin receiving segments
+ for it. */
+ TCP_REG(&tcp_active_pcbs, npcb);
+
+ /* Parse any options in the SYN. */
+ tcp_parseopt(npcb);
+#if TCP_CALCULATE_EFF_SEND_MSS
+ npcb->mss = tcp_eff_send_mss(npcb->mss, &(npcb->remote_ip));
+#endif /* TCP_CALCULATE_EFF_SEND_MSS */
+
+ snmp_inc_tcppassiveopens();
+
+ /* Send a SYN|ACK together with the MSS option. */
+ rc = tcp_enqueue_flags(npcb, TCP_SYN | TCP_ACK);
+ if (rc != ERR_OK) {//ͷ¿ƿ
+ tcp_abandon(npcb, 0);
+ return rc;
+ }
+ return tcp_output(npcb);//ͱ
+ }
+ return ERR_OK;
+}
+
+/**
+ * Called by tcp_input() when a segment arrives for a connection in
+ * TIME_WAIT.
+ *
+ * @param pcb the tcp_pcb for which a segment arrived
+ *
+ * @note the segment which arrived is saved in global variables, therefore only the pcb
+ * involved is passed as a parameter to this function
+ */
+ /*
+*TIME_WAIT״̬ĿƿøúյıĶΣ
+*״̬£رӵֹѾڵȴ2MSLʱ
+*״̬µıĶеľݣֱɾɡ
+*ҪͷACK
+*/
+static err_t
+tcp_timewait_input(struct tcp_pcb *pcb)
+{
+
+ if (flags & TCP_RST) { //RSTλֱӷ
+ return ERR_OK;
+ }
+
+ if (flags & TCP_SYN) { //SYNϢݱڽմڣͷRST
+
+ if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt+pcb->rcv_wnd)) {
+
+ tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(), ip_current_src_addr(),
+ tcphdr->dest, tcphdr->src);
+ return ERR_OK;
+ }
+ } else if (flags & TCP_FIN) { //İFINϢ
+
+ pcb->tmr = tcp_ticks; //λȴ2MSLʱ䣬ƿµȴ2MSL
+ }
+
+ if ((tcplen > 0)) { //ݵıĻڽմSYN
+ pcb->flags |= TF_ACK_NOW;//һACK
+ return tcp_output(pcb);
+ }
+ return ERR_OK;
+}
+
+/**
+ * Implements the TCP state machine. Called by tcp_input. In some
+ * states tcp_receive() is called to receive data. The tcp_seg
+ * argument will be freed by the caller (tcp_input()) unless the
+ * recv_data pointer in the pcb is set.
+ *
+ * @param pcb the tcp_pcb for which a segment arrived
+ *
+ * @note the segment which arrived is saved in global variables, therefore only the pcb
+ * involved is passed as a parameter to this function
+ */
+static err_t
+tcp_process(struct tcp_pcb *pcb)
+{
+ struct tcp_seg *rseg;
+ u8_t acceptable = 0;
+ err_t err;
+
+ err = ERR_OK;
+
+ /* Process incoming RST segments. */
+ if (flags & TCP_RST) {
+ /* First, determine if the reset is acceptable. */
+ if (pcb->state == SYN_SENT) {
+ if (ackno == pcb->snd_nxt) {
+ acceptable = 1;
+ }
+ } else {
+ if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt,
+ pcb->rcv_nxt+pcb->rcv_wnd)) {
+ acceptable = 1;
+ }
+ }
+
+ if (acceptable) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: Connection RESET\n"));
+ LWIP_ASSERT("tcp_input: pcb->state != CLOSED", pcb->state != CLOSED);
+ recv_flags |= TF_RESET;
+ pcb->flags &= ~TF_ACK_DELAY;
+ return ERR_RST;
+ } else {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n",
+ seqno, pcb->rcv_nxt));
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n",
+ seqno, pcb->rcv_nxt));
+ return ERR_OK;
+ }
+ }
+
+ if ((flags & TCP_SYN) && (pcb->state != SYN_SENT && pcb->state != SYN_RCVD)) {
+ /* Cope with new connection attempt after remote end crashed */
+ tcp_ack_now(pcb);
+ return ERR_OK;
+ }
+
+ if ((pcb->flags & TF_RXCLOSED) == 0) {
+ /* Update the PCB (in)activity timer unless rx is closed (see tcp_shutdown) */
+ pcb->tmr = tcp_ticks;
+ }
+ pcb->keep_cnt_sent = 0;
+
+ tcp_parseopt(pcb);
+
+ /* Do different things depending on the TCP state. */
+ switch (pcb->state) {
+ case SYN_SENT:
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("SYN-SENT: ackno %"U32_F" pcb->snd_nxt %"U32_F" unacked %"U32_F"\n", ackno,
+ pcb->snd_nxt, ntohl(pcb->unacked->tcphdr->seqno)));
+ /* received SYN ACK with expected sequence number? */
+ if ((flags & TCP_ACK) && (flags & TCP_SYN)
+ && ackno == ntohl(pcb->unacked->tcphdr->seqno) + 1) {
+ pcb->snd_buf++;
+ pcb->rcv_nxt = seqno + 1;
+ pcb->rcv_ann_right_edge = pcb->rcv_nxt;
+ pcb->lastack = ackno;
+ pcb->snd_wnd = tcphdr->wnd;
+ pcb->snd_wl1 = seqno - 1; /* initialise to seqno - 1 to force window update */
+ pcb->state = ESTABLISHED;
+
+#if TCP_CALCULATE_EFF_SEND_MSS
+ pcb->mss = tcp_eff_send_mss(pcb->mss, &(pcb->remote_ip));
+#endif /* TCP_CALCULATE_EFF_SEND_MSS */
+
+ /* Set ssthresh again after changing pcb->mss (already set in tcp_connect
+ * but for the default value of pcb->mss) */
+ pcb->ssthresh = pcb->mss * 10;
+
+ pcb->cwnd = ((pcb->cwnd == 1) ? (pcb->mss * 2) : pcb->mss);
+ LWIP_ASSERT("pcb->snd_queuelen > 0", (pcb->snd_queuelen > 0));
+ --pcb->snd_queuelen;
+ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_process: SYN-SENT --queuelen %"U16_F"\n", (u16_t)pcb->snd_queuelen));
+ rseg = pcb->unacked;
+ pcb->unacked = rseg->next;
+
+ /* If there's nothing left to acknowledge, stop the retransmit
+ timer, otherwise reset it to start again */
+ if(pcb->unacked == NULL)
+ pcb->rtime = -1;
+ else {
+ pcb->rtime = 0;
+ pcb->nrtx = 0;
+ }
+
+ tcp_seg_free(rseg);
+
+ /* Call the user specified function to call when sucessfully
+ * connected. */
+ TCP_EVENT_CONNECTED(pcb, ERR_OK, err);
+ if (err == ERR_ABRT) {
+ return ERR_ABRT;
+ }
+ tcp_ack_now(pcb);
+ }
+ /* received ACK? possibly a half-open connection */
+ else if (flags & TCP_ACK) {
+ /* send a RST to bring the other side in a non-synchronized state. */
+ tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(), ip_current_src_addr(),
+ tcphdr->dest, tcphdr->src);
+ }
+ break;
+ case SYN_RCVD:
+ if (flags & TCP_ACK) {
+ /* expected ACK number? */
+ if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)) {
+ u16_t old_cwnd;
+ pcb->state = ESTABLISHED;
+ LWIP_DEBUGF(TCP_DEBUG, ("TCP connection established %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+#if LWIP_CALLBACK_API
+ LWIP_ASSERT("pcb->accept != NULL", pcb->accept != NULL);
+#endif
+ /* Call the accept function. */
+ TCP_EVENT_ACCEPT(pcb, ERR_OK, err);
+ if (err != ERR_OK) {
+ /* If the accept function returns with an error, we abort
+ * the connection. */
+ /* Already aborted? */
+ if (err != ERR_ABRT) {
+ tcp_abort(pcb);
+ }
+ return ERR_ABRT;
+ }
+ old_cwnd = pcb->cwnd;
+ /* If there was any data contained within this ACK,
+ * we'd better pass it on to the application as well. */
+ tcp_receive(pcb);
+
+ /* Prevent ACK for SYN to generate a sent event */
+ if (pcb->acked != 0) {
+ pcb->acked--;
+ }
+
+ pcb->cwnd = ((old_cwnd == 1) ? (pcb->mss * 2) : pcb->mss);
+
+ if (recv_flags & TF_GOT_FIN) {
+ tcp_ack_now(pcb);
+ pcb->state = CLOSE_WAIT;
+ }
+ } else {
+ /* incorrect ACK number, send RST */
+ tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(), ip_current_src_addr(),
+ tcphdr->dest, tcphdr->src);
+ }
+ } else if ((flags & TCP_SYN) && (seqno == pcb->rcv_nxt - 1)) {
+ /* Looks like another copy of the SYN - retransmit our SYN-ACK */
+ tcp_rexmit(pcb);
+ }
+ break;
+ case CLOSE_WAIT:
+ /* FALLTHROUGH */
+ case ESTABLISHED:
+ tcp_receive(pcb);
+ if (recv_flags & TF_GOT_FIN) { /* passive close */
+ tcp_ack_now(pcb);
+ pcb->state = CLOSE_WAIT;
+ }
+ break;
+ case FIN_WAIT_1:
+ tcp_receive(pcb);
+ if (recv_flags & TF_GOT_FIN) {
+ if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt)) {
+ LWIP_DEBUGF(TCP_DEBUG,
+ ("TCP connection closed: FIN_WAIT_1 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+ tcp_ack_now(pcb);
+ tcp_pcb_purge(pcb);
+ TCP_RMV(&tcp_active_pcbs, pcb);
+ pcb->state = TIME_WAIT;
+ TCP_REG(&tcp_tw_pcbs, pcb);
+ } else {
+ tcp_ack_now(pcb);
+ pcb->state = CLOSING;
+ }
+ } else if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt)) {
+ pcb->state = FIN_WAIT_2;
+ }
+ break;
+ case FIN_WAIT_2:
+ tcp_receive(pcb);
+ if (recv_flags & TF_GOT_FIN) {
+ LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: FIN_WAIT_2 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+ tcp_ack_now(pcb);
+ tcp_pcb_purge(pcb);
+ TCP_RMV(&tcp_active_pcbs, pcb);
+ pcb->state = TIME_WAIT;
+ TCP_REG(&tcp_tw_pcbs, pcb);
+ }
+ break;
+ case CLOSING:
+ tcp_receive(pcb);
+ if (flags & TCP_ACK && ackno == pcb->snd_nxt) {
+ LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: CLOSING %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+ tcp_pcb_purge(pcb);
+ TCP_RMV(&tcp_active_pcbs, pcb);
+ pcb->state = TIME_WAIT;
+ TCP_REG(&tcp_tw_pcbs, pcb);
+ }
+ break;
+ case LAST_ACK:
+ tcp_receive(pcb);
+ if (flags & TCP_ACK && ackno == pcb->snd_nxt) {
+ LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: LAST_ACK %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+ /* bugfix #21699: don't set pcb->state to CLOSED here or we risk leaking segments */
+ recv_flags |= TF_CLOSED;
+ }
+ break;
+ default:
+ break;
+ }
+ return ERR_OK;
+}
+
+#if TCP_QUEUE_OOSEQ
+/**
+ * Insert segment into the list (segments covered with new one will be deleted)
+ *
+ * Called from tcp_receive()
+ */
+static void ICACHE_FLASH_ATTR
+tcp_oos_insert_segment(struct tcp_seg *cseg, struct tcp_seg *next)
+{
+ struct tcp_seg *old_seg;
+
+ if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) {
+ /* received segment overlaps all following segments */
+ tcp_segs_free(next);
+ next = NULL;
+ }
+ else {
+ /* delete some following segments
+ oos queue may have segments with FIN flag */
+ while (next &&
+ TCP_SEQ_GEQ((seqno + cseg->len),
+ (next->tcphdr->seqno + next->len))) {
+ /* cseg with FIN already processed */
+ if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) {
+ TCPH_SET_FLAG(cseg->tcphdr, TCP_FIN);
+ }
+ old_seg = next;
+ next = next->next;
+ tcp_seg_free(old_seg);
+ }
+ if (next &&
+ TCP_SEQ_GT(seqno + cseg->len, next->tcphdr->seqno)) {
+ /* We need to trim the incoming segment. */
+ cseg->len = (u16_t)(next->tcphdr->seqno - seqno);
+ pbuf_realloc(cseg->p, cseg->len);
+ }
+ }
+ cseg->next = next;
+}
+#endif /* TCP_QUEUE_OOSEQ */
+
+/**
+ * Called by tcp_process. Checks if the given segment is an ACK for outstanding
+ * data, and if so frees the memory of the buffered data. Next, is places the
+ * segment on any of the receive queues (pcb->recved or pcb->ooseq). If the segment
+ * is buffered, the pbuf is referenced by pbuf_ref so that it will not be freed until
+ * i it has been removed from the buffer.
+ *
+ * If the incoming segment constitutes an ACK for a segment that was used for RTT
+ * estimation, the RTT is estimated here as well.
+ *
+ * Called from tcp_process().
+ */
+static void
+tcp_receive(struct tcp_pcb *pcb)
+{
+ struct tcp_seg *next;
+#if TCP_QUEUE_OOSEQ
+ struct tcp_seg *prev, *cseg;
+#endif /* TCP_QUEUE_OOSEQ */
+ struct pbuf *p;
+ s32_t off;
+ s16_t m;
+ u32_t right_wnd_edge;
+ u16_t new_tot_len;
+ int found_dupack = 0;
+
+ if (flags & TCP_ACK) {//İACK
+ right_wnd_edge = pcb->snd_wnd + pcb->snd_wl2;//ʹ + Ӧڸ
+
+ // first /* Update window. */
+ /*seqno > snd_wl1ֹ̲ôָ;
+ *seqno = snd_wl1ackno > snd_wl2;ʱԷûзݣֻյݵȷ;
+ *ackno = snd_wl2ұײбsnd_wndĴ.Ӧֵ
+ */
+ if (TCP_SEQ_LT(pcb->snd_wl1, seqno) ||
+ (pcb->snd_wl1 == seqno && TCP_SEQ_LT(pcb->snd_wl2, ackno)) ||
+ (pcb->snd_wl2 == ackno && tcphdr->wnd > pcb->snd_wnd)) {
+ pcb->snd_wnd = tcphdr->wnd;
+ pcb->snd_wl1 = seqno;
+ pcb->snd_wl2 = ackno;
+ if (pcb->snd_wnd > 0 && pcb->persist_backoff > 0) {
+ pcb->persist_backoff = 0;//ʱ˳
+ }
+ LWIP_DEBUGF(TCP_WND_DEBUG, ("tcp_receive: window update %"U16_F"\n", pcb->snd_wnd));
+#if TCP_WND_DEBUG
+ } else {
+ if (pcb->snd_wnd != tcphdr->wnd) {
+ LWIP_DEBUGF(TCP_WND_DEBUG,
+ ("tcp_receive: no window update lastack %"U32_F" ackno %"
+ U32_F" wl1 %"U32_F" seqno %"U32_F" wl2 %"U32_F"\n",
+ pcb->lastack, ackno, pcb->snd_wl1, seqno, pcb->snd_wl2));
+ }
+#endif /* TCP_WND_DEBUG */
+ }
+
+ /* (From Stevens TCP/IP Illustrated Vol II, p970.) Its only a
+ * duplicate ack if:
+ * 1) It doesn't ACK new data ûȷ
+ * 2) length of received packet is zero (i.e. no payload) Ķκ
+ * 3) the advertised window hasn't changed شûи
+ * 4) There is outstanding unacknowledged data (retransmission timer running)ݵȴȷ
+ * 5) The ACK is == biggest ACK sequence number so far seen (snd_una) ackno = lastack
+ *
+ * If it passes all five, should process as a dupack:
+ * a) dupacks < 3: do nothing
+ * b) dupacks == 3: fast retransmit
+ * c) dupacks > 3: increase cwnd
+ *
+ * If it only passes 1-3, should reset dupack counter (and add to
+ * stats, which we don't do in lwIP)
+ *
+ * If it only passes 1, should reset dupack counter
+ *
+ */
+
+ /* Clause 1 */
+ if (TCP_SEQ_LEQ(ackno, pcb->lastack)) {//ظACK?
+ pcb->acked = 0;
+ /* Clause 2 */
+ if (tcplen == 0) {
+ /* Clause 3 */
+ if (pcb->snd_wl2 + pcb->snd_wnd == right_wnd_edge){
+ /* Clause 4 */
+ if (pcb->rtime >= 0) {
+ /* Clause 5 */
+ if (pcb->lastack == ackno) {
+ found_dupack = 1;
+ if (pcb->dupacks + 1 > pcb->dupacks)
+ ++pcb->dupacks;
+ if (pcb->dupacks > 3) {
+ /* Inflate the congestion window, but not if it means that
+ the value overflows. */
+ if ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) {
+ pcb->cwnd += pcb->mss;
+ }
+ } else if (pcb->dupacks == 3) {//ظACK
+ /* Do fast retransmit */
+ tcp_rexmit_fast(pcb);
+ }
+ }
+ }
+ }
+ }
+ /* If Clause (1) or more is true, but not a duplicate ack, reset
+ * count of consecutive duplicate acks */
+ if (!found_dupack) {
+ pcb->dupacks = 0;
+ }
+ } else if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)){//acknolastack+1snd_nxt֮䣬жϷʹ
+ /* We come here when the ACK acknowledges new data. */
+
+ if (pcb->flags & TF_INFR) {
+ pcb->flags &= ~TF_INFR;// Reset the "IN Fast Retransmit" flag,since we are no longer in fast retransmit
+ pcb->cwnd = pcb->ssthresh;//Reset the congestion window to the "slow start threshold".
+ }
+
+ /* Reset the number of retransmissions. */
+ pcb->nrtx = 0;
+
+ /* Reset the retransmission time-out. */
+ pcb->rto = (pcb->sa >> 3) + pcb->sv;
+
+ /* Update the send buffer space. Diff between the two can never exceed 64K? */
+ pcb->acked = (u16_t)(ackno - pcb->lastack);
+
+ pcb->snd_buf += pcb->acked;
+
+ /* Reset the fast retransmit variables. */
+ pcb->dupacks = 0;
+ pcb->lastack = ackno;
+
+ /* Update the congestion control variables (cwnd and
+ ssthresh). */
+ if (pcb->state >= ESTABLISHED) {//״̬Ϊӱ־
+ if (pcb->cwnd < pcb->ssthresh) {
+ if ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) {
+ pcb->cwnd += pcb->mss;
+ }
+ LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: slow start cwnd %"U16_F"\n", pcb->cwnd));
+ } else {
+ u16_t new_cwnd = (pcb->cwnd + pcb->mss * pcb->mss / pcb->cwnd);
+ if (new_cwnd > pcb->cwnd) {
+ pcb->cwnd = new_cwnd;
+ }
+ LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: congestion avoidance cwnd %"U16_F"\n", pcb->cwnd));
+ }
+ }
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: ACK for %"U32_F", unacked->seqno %"U32_F":%"U32_F"\n",
+ ackno,
+ pcb->unacked != NULL?
+ ntohl(pcb->unacked->tcphdr->seqno): 0,
+ pcb->unacked != NULL?
+ ntohl(pcb->unacked->tcphdr->seqno) + TCP_TCPLEN(pcb->unacked): 0));
+
+ /* Remove segment from the unacknowledged list if the incoming
+ ACK acknowlegdes them.
+ *ͷunackedϱȷϵıĶΣ
+ *ֱunackedΪֹͣ*/
+ while (pcb->unacked != NULL &&
+ TCP_SEQ_LEQ(ntohl(pcb->unacked->tcphdr->seqno) +
+ TCP_TCPLEN(pcb->unacked), ackno)) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->unacked\n",
+ ntohl(pcb->unacked->tcphdr->seqno),
+ ntohl(pcb->unacked->tcphdr->seqno) +
+ TCP_TCPLEN(pcb->unacked)));
+
+ next = pcb->unacked;//pcb unacked־
+ pcb->unacked = pcb->unacked->next;//pcb unacked һ־
+
+ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"U16_F" ... ", (u16_t)pcb->snd_queuelen));
+ LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= pbuf_clen(next->p)));
+ /* Prevent ACK for FIN to generate a sent event */
+ if ((pcb->acked != 0) && ((TCPH_FLAGS(next->tcphdr) & TCP_FIN) != 0)) {
+ pcb->acked--;
+ }
+
+ pcb->snd_queuelen -= pbuf_clen(next->p);//pbufs
+ tcp_seg_free(next);//ͷtcp
+
+ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"U16_F" (after freeing unacked)\n", (u16_t)pcb->snd_queuelen));
+ if (pcb->snd_queuelen != 0) {
+ LWIP_ASSERT("tcp_receive: valid queue length", pcb->unacked != NULL ||
+ pcb->unsent != NULL);
+ }
+ }
+
+ /* If there's nothing left to acknowledge, stop the retransmit
+ timer, otherwise reset it to start again */
+ if(pcb->unacked == NULL) //ݵȴȷ
+ pcb->rtime = -1; //ֹͣشʱ
+ else
+ pcb->rtime = 0; //λشʱ
+
+ pcb->polltmr = 0;
+ } else {
+ /* Fix bug bug #21582: out of sequence ACK, didn't really ack anything */
+ pcb->acked = 0;
+ }
+
+ /* We go through the ->unsent list to see if any of the segments
+ on the list are acknowledged by the ACK. This may seem
+ strange since an "unsent" segment shouldn't be acked. The
+ rationale is that lwIP puts all outstanding segments on the
+ ->unsent list after a retransmission, so these segments may
+ in fact have been sent once. */
+ /** unsentǷܱacknoȷϵıĶΣͷ**/
+ while (pcb->unsent != NULL &&
+ TCP_SEQ_BETWEEN(ackno, ntohl(pcb->unsent->tcphdr->seqno) +
+ TCP_TCPLEN(pcb->unsent), pcb->snd_nxt)) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->unsent\n",
+ ntohl(pcb->unsent->tcphdr->seqno), ntohl(pcb->unsent->tcphdr->seqno) +
+ TCP_TCPLEN(pcb->unsent)));
+
+ next = pcb->unsent;//pcbδͱ־
+ pcb->unsent = pcb->unsent->next;//δ͵һ
+ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"U16_F" ... ", (u16_t)pcb->snd_queuelen));
+ LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= pbuf_clen(next->p)));
+ /* Prevent ACK for FIN to generate a sent event */
+ if ((pcb->acked != 0) && ((TCPH_FLAGS(next->tcphdr) & TCP_FIN) != 0)) {
+ pcb->acked--;
+ }
+ pcb->snd_queuelen -= pbuf_clen(next->p);//pbufĸ
+ tcp_seg_free(next);//ͷŶ
+ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"U16_F" (after freeing unsent)\n", (u16_t)pcb->snd_queuelen));
+ if (pcb->snd_queuelen != 0) {//г
+ LWIP_ASSERT("tcp_receive: valid queue length",
+ pcb->unacked != NULL || pcb->unsent != NULL);
+ }
+ }
+ /* End of ACK for new data processing. */
+
+ LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: pcb->rttest %"U32_F" rtseq %"U32_F" ackno %"U32_F"\n",
+ pcb->rttest, pcb->rtseq, ackno));
+
+ /* RTT estimation calculations. This is done by checking if the
+ incoming segment acknowledges the segment we use to take a
+ round-trip time measurement. */
+ if (pcb->rttest && TCP_SEQ_LT(pcb->rtseq, ackno)) {//RTTڽҸñĶαȷ
+ /* diff between this shouldn't exceed 32K since this are tcp timer ticks
+ and a round-trip shouldn't be that long... */
+ m = (s16_t)(tcp_ticks - pcb->rttest);//Mֵ
+
+ LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: experienced rtt %"U16_F" ticks (%"U16_F" msec).\n",
+ m, m * TCP_SLOW_INTERVAL));
+
+ /* This is taken directly from VJs original code in his paper RTT㹫ʽ*/
+ m = m - (pcb->sa >> 3);
+ pcb->sa += m;
+ if (m < 0) {
+ m = -m;
+ }
+ m = m - (pcb->sv >> 2);
+ pcb->sv += m;
+ pcb->rto = (pcb->sa >> 3) + pcb->sv;
+
+ LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: RTO %"U16_F" (%"U16_F" milliseconds)\n",
+ pcb->rto, pcb->rto * TCP_SLOW_INTERVAL));
+
+ pcb->rttest = 0;
+ }
+ }
+
+ /* If the incoming segment contains data, we must process it
+ further. */
+ if (tcplen > 0) {
+ /* This code basically does three things:
+
+ +) If the incoming segment contains data that is the next
+ in-sequence data, this data is passed to the application. This
+ might involve trimming the first edge of the data. The rcv_nxt
+ variable and the advertised window are adjusted.
+
+ +) If the incoming segment has data that is above the next
+ sequence number expected (->rcv_nxt), the segment is placed on
+ the ->ooseq queue. This is done by finding the appropriate
+ place in the ->ooseq queue (which is ordered by sequence
+ number) and trim the segment in both ends if needed. An
+ immediate ACK is sent to indicate that we received an
+ out-of-sequence segment.
+
+ +) Finally, we check if the first segment on the ->ooseq queue
+ now is in sequence (i.e., if rcv_nxt >= ooseq->seqno). If
+ rcv_nxt > ooseq->seqno, we must trim the first edge of the
+ segment on ->ooseq before we adjust rcv_nxt. The data in the
+ segments that are now on sequence are chained onto the
+ incoming segment so that we only need to call the application
+ once.
+ */
+
+ /* First, we check if we must trim the first edge. We have to do
+ this if the sequence number of the incoming segment is less
+ than rcv_nxt, and the sequence number plus the length of the
+ segment is larger than rcv_nxt. */
+ /* if (TCP_SEQ_LT(seqno, pcb->rcv_nxt)){
+ if (TCP_SEQ_LT(pcb->rcv_nxt, seqno + tcplen)) {*/
+ if (TCP_SEQ_BETWEEN(pcb->rcv_nxt, seqno + 1, seqno + tcplen - 1)){// seqno < rcv_nxt < seqno + tcplen
+ /* Trimming the first edge is done by pushing the payload
+ pointer in the pbuf downwards. This is somewhat tricky since
+ we do not want to discard the full contents of the pbuf up to
+ the new starting point of the data since we have to keep the
+ TCP header which is present in the first pbuf in the chain.
+
+ What is done is really quite a nasty hack: the first pbuf in
+ the pbuf chain is pointed to by inseg.p. Since we need to be
+ able to deallocate the whole pbuf, we cannot change this
+ inseg.p pointer to point to any of the later pbufs in the
+ chain. Instead, we point the ->payload pointer in the first
+ pbuf to data in one of the later pbufs. We also set the
+ inseg.data pointer to point to the right place. This way, the
+ ->p pointer will still point to the first pbuf, but the
+ ->p->payload pointer will point to data in another pbuf.
+
+ After we are done with adjusting the pbuf pointers we must
+ adjust the ->data pointer in the seg and the segment
+ length.*/
+ //ȥĶݱŵrcv_nxt
+ off = pcb->rcv_nxt - seqno;
+ p = inseg.p;
+ LWIP_ASSERT("inseg.p != NULL", inseg.p);
+ LWIP_ASSERT("insane offset!", (off < 0x7fff));
+ if (inseg.p->len < off) {
+ LWIP_ASSERT("pbuf too short!", (((s32_t)inseg.p->tot_len) >= off));
+ new_tot_len = (u16_t)(inseg.p->tot_len - off);
+ while (p->len < off) {
+ off -= p->len;
+ /* KJM following line changed (with addition of new_tot_len var)
+ to fix bug #9076
+ inseg.p->tot_len -= p->len; */
+ p->tot_len = new_tot_len;
+ p->len = 0;
+ p = p->next;
+ }
+ if(pbuf_header(p, (s16_t)-off)) {
+ /* Do we need to cope with this failing? Assert for now */
+ LWIP_ASSERT("pbuf_header failed", 0);
+ }
+ } else {
+ if(pbuf_header(inseg.p, (s16_t)-off)) {
+ /* Do we need to cope with this failing? Assert for now */
+ LWIP_ASSERT("pbuf_header failed", 0);
+ }
+ }
+ inseg.len -= (u16_t)(pcb->rcv_nxt - seqno);
+ inseg.tcphdr->seqno = seqno = pcb->rcv_nxt;
+ }
+ else {
+ if (TCP_SEQ_LT(seqno, pcb->rcv_nxt)){//seqno < rcv_nxt
+ /* the whole segment is < rcv_nxt */
+ /* must be a duplicate of a packet that has already been correctly handled */
+ //ĶݱžСrcv_nxt˱ظģ
+ //ֱԴӦACKĴ
+
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: duplicate seqno %"U32_F"\n", seqno));
+ tcp_ack_now(pcb);
+ }
+ }
+
+ /* The sequence number must be within the window (above rcv_nxt
+ and below rcv_nxt + rcv_wnd) in order to be further
+ processed. */
+ if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt,
+ pcb->rcv_nxt + pcb->rcv_wnd - 1)){//rcv_nxt < seqno < rcv_nxt + rcv_wnd - 1,ڽշΧ
+ if (pcb->rcv_nxt == seqno) {
+ /* The incoming segment is the next in sequence. We check if
+ we have to trim the end of the segment and update rcv_nxt
+ and pass the data to the application. */
+ tcplen = TCP_TCPLEN(&inseg);//㱨Ķγ
+
+ if (tcplen > pcb->rcv_wnd) {//մڴСβض
+ LWIP_DEBUGF(TCP_INPUT_DEBUG,
+ ("tcp_receive: other end overran receive window"
+ "seqno %"U32_F" len %"U16_F" right edge %"U32_F"\n",
+ seqno, tcplen, pcb->rcv_nxt + pcb->rcv_wnd));
+ if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) {
+ /* Must remove the FIN from the header as we're trimming
+ * that byte of sequence-space from the packet */
+ TCPH_FLAGS_SET(inseg.tcphdr, TCPH_FLAGS(inseg.tcphdr) &~ TCP_FIN);
+ }
+ /* Adjust length of segment to fit in the window. */
+ inseg.len = pcb->rcv_wnd;
+ if (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) {
+ inseg.len -= 1;
+ }
+ pbuf_realloc(inseg.p, inseg.len);
+ tcplen = TCP_TCPLEN(&inseg);
+ LWIP_ASSERT("tcp_receive: segment not trimmed correctly to rcv_wnd\n",
+ (seqno + tcplen) == (pcb->rcv_nxt + pcb->rcv_wnd));
+ }
+#if TCP_QUEUE_OOSEQ
+ /* Received in-sequence data, adjust ooseq data if:
+ - FIN has been received or
+ - inseq overlaps with ooseq */
+ if (pcb->ooseq != NULL) {
+ if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG,
+ ("tcp_receive: received in-order FIN, binning ooseq queue\n"));
+ /* Received in-order FIN means anything that was received
+ * out of order must now have been received in-order, so
+ * bin the ooseq queue */
+ while (pcb->ooseq != NULL) {
+ struct tcp_seg *old_ooseq = pcb->ooseq;
+ pcb->ooseq = pcb->ooseq->next;
+ tcp_seg_free(old_ooseq);
+ }
+ }
+ else {
+ next = pcb->ooseq;
+ /* Remove all segments on ooseq that are covered by inseg already.
+ * FIN is copied from ooseq to inseg if present. */
+ while (next &&
+ TCP_SEQ_GEQ(seqno + tcplen,
+ next->tcphdr->seqno + next->len)) {
+ /* inseg cannot have FIN here (already processed above) */
+ if (TCPH_FLAGS(next->tcphdr) & TCP_FIN &&
+ (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) == 0) {
+ TCPH_SET_FLAG(inseg.tcphdr, TCP_FIN);
+ tcplen = TCP_TCPLEN(&inseg);
+ }
+ prev = next;
+ next = next->next;
+ tcp_seg_free(prev);
+ }
+ /* Now trim right side of inseg if it overlaps with the first
+ * segment on ooseq */
+ if (next &&
+ TCP_SEQ_GT(seqno + tcplen,
+ next->tcphdr->seqno)) {
+ /* inseg cannot have FIN here (already processed above) */
+ inseg.len = (u16_t)(next->tcphdr->seqno - seqno);
+ if (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) {
+ inseg.len -= 1;
+ }
+ pbuf_realloc(inseg.p, inseg.len);
+ tcplen = TCP_TCPLEN(&inseg);
+ LWIP_ASSERT("tcp_receive: segment not trimmed correctly to ooseq queue\n",
+ (seqno + tcplen) == next->tcphdr->seqno);
+ }
+ pcb->ooseq = next;
+ }
+ }
+#endif /* TCP_QUEUE_OOSEQ */
+
+ pcb->rcv_nxt = seqno + tcplen;
+
+ /* Update the receiver's (our) window. */
+ LWIP_ASSERT("tcp_receive: tcplen > rcv_wnd\n", pcb->rcv_wnd >= tcplen);
+ pcb->rcv_wnd -= tcplen;
+
+ tcp_update_rcv_ann_wnd(pcb);
+
+ /* If there is data in the segment, we make preparations to
+ pass this up to the application. The ->recv_data variable
+ is used for holding the pbuf that goes to the
+ application. The code for reassembling out-of-sequence data
+ chains its data on this pbuf as well.
+
+ If the segment was a FIN, we set the TF_GOT_FIN flag that will
+ be used to indicate to the application that the remote side has
+ closed its end of the connection. */
+ if (inseg.p->tot_len > 0) {
+ recv_data = inseg.p;
+ /* Since this pbuf now is the responsibility of the
+ application, we delete our reference to it so that we won't
+ (mistakingly) deallocate it. */
+ inseg.p = NULL;
+ }
+ if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: received FIN.\n"));
+ recv_flags |= TF_GOT_FIN;
+ }
+
+#if TCP_QUEUE_OOSEQ
+ /* We now check if we have segments on the ->ooseq queue that
+ are now in sequence. */
+ while (pcb->ooseq != NULL &&
+ pcb->ooseq->tcphdr->seqno == pcb->rcv_nxt) {
+
+ cseg = pcb->ooseq;
+ seqno = pcb->ooseq->tcphdr->seqno;
+
+ pcb->rcv_nxt += TCP_TCPLEN(cseg);
+ LWIP_ASSERT("tcp_receive: ooseq tcplen > rcv_wnd\n",
+ pcb->rcv_wnd >= TCP_TCPLEN(cseg));
+ pcb->rcv_wnd -= TCP_TCPLEN(cseg);
+
+ tcp_update_rcv_ann_wnd(pcb);
+
+ if (cseg->p->tot_len > 0) {
+ /* Chain this pbuf onto the pbuf that we will pass to
+ the application. */
+ if (recv_data) {
+ pbuf_cat(recv_data, cseg->p);
+ } else {
+ recv_data = cseg->p;
+ }
+ cseg->p = NULL;
+ }
+ if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: dequeued FIN.\n"));
+ recv_flags |= TF_GOT_FIN;
+ if (pcb->state == ESTABLISHED) { /* force passive close or we can move to active close */
+ pcb->state = CLOSE_WAIT;
+ }
+ }
+
+ pcb->ooseq = cseg->next;
+ tcp_seg_free(cseg);
+ }
+#endif /* TCP_QUEUE_OOSEQ */
+
+
+ /* Acknowledge the segment(s). */
+ tcp_ack(pcb);
+
+ } else {
+ /* We get here if the incoming segment is out-of-sequence. */
+ tcp_send_empty_ack(pcb);
+#if TCP_QUEUE_OOSEQ
+ /* We queue the segment on the ->ooseq queue. */
+ if (pcb->ooseq == NULL) {
+ pcb->ooseq = tcp_seg_copy(&inseg);
+ } else {
+ /* If the queue is not empty, we walk through the queue and
+ try to find a place where the sequence number of the
+ incoming segment is between the sequence numbers of the
+ previous and the next segment on the ->ooseq queue. That is
+ the place where we put the incoming segment. If needed, we
+ trim the second edges of the previous and the incoming
+ segment so that it will fit into the sequence.
+
+ If the incoming segment has the same sequence number as a
+ segment on the ->ooseq queue, we discard the segment that
+ contains less data. */
+
+ prev = NULL;
+ for(next = pcb->ooseq; next != NULL; next = next->next) {//ooseqȡµMĶΣñĶηǿգM++
+ if (seqno == next->tcphdr->seqno) {//ñĶʼ== ҪıĶα
+ /* The sequence number of the incoming segment is the
+ same as the sequence number of the segment on
+ ->ooseq. We check the lengths to see which one to
+ discard. */
+ if (inseg.len > next->len) {//ҪıĶαŸ
+ /* The incoming segment is larger than the old
+ segment. We replace some segments with the new
+ one. */
+ cseg = tcp_seg_copy(&inseg);//ҪıĶδMĶ
+ if (cseg != NULL) {
+ if (prev != NULL) {
+ prev->next = cseg;
+ } else {
+ pcb->ooseq = cseg;
+ }
+ tcp_oos_insert_segment(cseg, next);
+ }
+ break;
+ } else {
+ /* Either the lenghts are the same or the incoming
+ segment was smaller than the old one; in either
+ case, we ditch the incoming segment. */
+ break;
+ }
+ } else {
+ if (prev == NULL) {
+ if (TCP_SEQ_LT(seqno, next->tcphdr->seqno)) {
+ /* The sequence number of the incoming segment is lower
+ than the sequence number of the first segment on the
+ queue. We put the incoming segment first on the
+ queue. */
+ cseg = tcp_seg_copy(&inseg);
+ if (cseg != NULL) {
+ pcb->ooseq = cseg;
+ tcp_oos_insert_segment(cseg, next);
+ }
+ break;
+ }
+ } else {
+ /*if (TCP_SEQ_LT(prev->tcphdr->seqno, seqno) &&
+ TCP_SEQ_LT(seqno, next->tcphdr->seqno)) {*/
+ if (TCP_SEQ_BETWEEN(seqno, prev->tcphdr->seqno+1, next->tcphdr->seqno-1)) {
+ /* The sequence number of the incoming segment is in
+ between the sequence numbers of the previous and
+ the next segment on ->ooseq. We trim trim the previous
+ segment, delete next segments that included in received segment
+ and trim received, if needed. */
+ cseg = tcp_seg_copy(&inseg);
+ if (cseg != NULL) {
+ if (TCP_SEQ_GT(prev->tcphdr->seqno + prev->len, seqno)) {
+ /* We need to trim the prev segment. */
+ prev->len = (u16_t)(seqno - prev->tcphdr->seqno);
+ pbuf_realloc(prev->p, prev->len);
+ }
+ prev->next = cseg;
+ tcp_oos_insert_segment(cseg, next);
+ }
+ break;
+ }
+ }
+ /* If the "next" segment is the last segment on the
+ ooseq queue, we add the incoming segment to the end
+ of the list. */
+ if (next->next == NULL &&
+ TCP_SEQ_GT(seqno, next->tcphdr->seqno)) {
+ if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) {
+ /* segment "next" already contains all data */
+ break;
+ }
+ next->next = tcp_seg_copy(&inseg);
+ if (next->next != NULL) {
+ if (TCP_SEQ_GT(next->tcphdr->seqno + next->len, seqno)) {
+ /* We need to trim the last segment. */
+ next->len = (u16_t)(seqno - next->tcphdr->seqno);
+ pbuf_realloc(next->p, next->len);
+ }
+ /* check if the remote side overruns our receive window */
+ if ((u32_t)tcplen + seqno > pcb->rcv_nxt + (u32_t)pcb->rcv_wnd) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG,
+ ("tcp_receive: other end overran receive window"
+ "seqno %"U32_F" len %"U16_F" right edge %"U32_F"\n",
+ seqno, tcplen, pcb->rcv_nxt + pcb->rcv_wnd));
+ if (TCPH_FLAGS(next->next->tcphdr) & TCP_FIN) {
+ /* Must remove the FIN from the header as we're trimming
+ * that byte of sequence-space from the packet */
+ TCPH_FLAGS_SET(next->next->tcphdr, TCPH_FLAGS(next->next->tcphdr) &~ TCP_FIN);
+ }
+ /* Adjust length of segment to fit in the window. */
+ next->next->len = pcb->rcv_nxt + pcb->rcv_wnd - seqno;
+ pbuf_realloc(next->next->p, next->next->len);
+ tcplen = TCP_TCPLEN(next->next);
+ LWIP_ASSERT("tcp_receive: segment not trimmed correctly to rcv_wnd\n",
+ (seqno + tcplen) == (pcb->rcv_nxt + pcb->rcv_wnd));
+ }
+ }
+ break;
+ }
+ }
+ prev = next;
+ }
+ }
+#endif /* TCP_QUEUE_OOSEQ */
+
+ }
+ } else {
+ /* The incoming segment is not withing the window. */
+ tcp_send_empty_ack(pcb);
+ }
+ } else {
+ /* Segments with length 0 is taken care of here. Segments that
+ fall out of the window are ACKed. */
+ /*if (TCP_SEQ_GT(pcb->rcv_nxt, seqno) ||
+ TCP_SEQ_GEQ(seqno, pcb->rcv_nxt + pcb->rcv_wnd)) {*/
+ if(!TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt + pcb->rcv_wnd-1)){
+ tcp_ack_now(pcb);//Դ˷һȷϱ
+ }
+ }
+}
+
+/**
+ * Parses the options contained in the incoming segment.
+ *
+ * Called from tcp_listen_input() and tcp_process().
+ * Currently, only the MSS option is supported!
+ *
+ * @param pcb the tcp_pcb for which a segment arrived
+ */
+static void
+tcp_parseopt(struct tcp_pcb *pcb)
+{
+ u16_t c, max_c;
+ u16_t mss;
+ u8_t *opts, opt;
+#if LWIP_TCP_TIMESTAMPS
+ u32_t tsval;
+#endif
+
+ opts = (u8_t *)tcphdr + TCP_HLEN;
+
+ /* Parse the TCP MSS option, if present. */
+ if(TCPH_HDRLEN(tcphdr) > 0x5) {
+ max_c = (TCPH_HDRLEN(tcphdr) - 5) << 2;
+ for (c = 0; c < max_c; ) {
+ opt = opts[c];
+ switch (opt) {
+ case 0x00:
+ /* End of options. */
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: EOL\n"));
+ return;
+ case 0x01:
+ /* NOP option. */
+ ++c;
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: NOP\n"));
+ break;
+ case 0x02:
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: MSS\n"));
+ if (opts[c + 1] != 0x04 || c + 0x04 > max_c) {
+ /* Bad length */
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
+ return;
+ }
+ /* An MSS option with the right option length. */
+ mss = (opts[c + 2] << 8) | opts[c + 3];
+ /* Limit the mss to the configured TCP_MSS and prevent division by zero */
+ pcb->mss = ((mss > TCP_MSS) || (mss == 0)) ? TCP_MSS : mss;
+ /* Advance to next option */
+ c += 0x04;
+ break;
+#if LWIP_TCP_TIMESTAMPS
+ case 0x08:
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: TS\n"));
+ if (opts[c + 1] != 0x0A || c + 0x0A > max_c) {
+ /* Bad length */
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
+ return;
+ }
+ /* TCP timestamp option with valid length */
+ tsval = (opts[c+2]) | (opts[c+3] << 8) |
+ (opts[c+4] << 16) | (opts[c+5] << 24);
+ if (flags & TCP_SYN) {
+ pcb->ts_recent = ntohl(tsval);
+ pcb->flags |= TF_TIMESTAMP;
+ } else if (TCP_SEQ_BETWEEN(pcb->ts_lastacksent, seqno, seqno+tcplen)) {
+ pcb->ts_recent = ntohl(tsval);
+ }
+ /* Advance to next option */
+ c += 0x0A;
+ break;
+#endif
+ default:
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: other\n"));
+ if (opts[c + 1] == 0) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
+ /* If the length field is zero, the options are malformed
+ and we don't process them further. */
+ return;
+ }
+ /* All other options have a length field, so that we easily
+ can skip past them. */
+ c += opts[c + 1];
+ }
+ }
+ }
+}
+
+#endif /* LWIP_TCP */
diff --git a/app/lwip/core/tcp_out.c b/app/lwip/core/tcp_out.c
new file mode 100644
index 00000000..9227fec5
--- /dev/null
+++ b/app/lwip/core/tcp_out.c
@@ -0,0 +1,1472 @@
+/**
+ * @file
+ * Transmission Control Protocol, outgoing traffic
+ *
+ * The output functions of TCP.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/tcp_impl.h"
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/sys.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/stats.h"
+#include "lwip/snmp.h"
+
+#include
+
+/* Define some copy-macros for checksum-on-copy so that the code looks
+ nicer by preventing too many ifdef's. */
+#if TCP_CHECKSUM_ON_COPY
+#define TCP_DATA_COPY(dst, src, len, seg) do { \
+ tcp_seg_add_chksum(LWIP_CHKSUM_COPY(dst, src, len), \
+ len, &seg->chksum, &seg->chksum_swapped); \
+ seg->flags |= TF_SEG_DATA_CHECKSUMMED; } while(0)
+#define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped) \
+ tcp_seg_add_chksum(LWIP_CHKSUM_COPY(dst, src, len), len, chksum, chksum_swapped);
+#else /* TCP_CHECKSUM_ON_COPY*/
+#define TCP_DATA_COPY(dst, src, len, seg) MEMCPY(dst, src, len)
+#define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped) MEMCPY(dst, src, len)
+#endif /* TCP_CHECKSUM_ON_COPY*/
+
+/** Define this to 1 for an extra check that the output checksum is valid
+ * (usefule when the checksum is generated by the application, not the stack) */
+#ifndef TCP_CHECKSUM_ON_COPY_SANITY_CHECK
+#define TCP_CHECKSUM_ON_COPY_SANITY_CHECK 0
+#endif
+
+/* Forward declarations.*/
+static void tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb);
+
+/** Allocate a pbuf and create a tcphdr at p->payload, used for output
+ * functions other than the default tcp_output -> tcp_output_segment
+ * (e.g. tcp_send_empty_ack, etc.)
+ *
+ * @param pcb tcp pcb for which to send a packet (used to initialize tcp_hdr)
+ * @param optlen length of header-options
+ * @param datalen length of tcp data to reserve in pbuf
+ * @param seqno_be seqno in network byte order (big-endian)
+ * @return pbuf with p->payload being the tcp_hdr
+ */
+static struct pbuf *ICACHE_FLASH_ATTR
+tcp_output_alloc_header(struct tcp_pcb *pcb, u16_t optlen, u16_t datalen,
+ u32_t seqno_be /* already in network byte order */)
+{
+ struct tcp_hdr *tcphdr;
+ struct pbuf *p = pbuf_alloc(PBUF_IP, TCP_HLEN + optlen + datalen, PBUF_RAM);
+ if (p != NULL) {
+ LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr",
+ (p->len >= TCP_HLEN + optlen));
+ tcphdr = (struct tcp_hdr *)p->payload;
+ tcphdr->src = htons(pcb->local_port);
+ tcphdr->dest = htons(pcb->remote_port);
+ tcphdr->seqno = seqno_be;
+ tcphdr->ackno = htonl(pcb->rcv_nxt);
+ TCPH_HDRLEN_FLAGS_SET(tcphdr, (5 + optlen / 4), TCP_ACK);
+ tcphdr->wnd = htons(pcb->rcv_ann_wnd);
+ tcphdr->chksum = 0;
+ tcphdr->urgp = 0;
+
+ /* If we're sending a packet, update the announced right window edge */
+ pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd;
+ }
+ return p;
+}
+
+/**
+ * Called by tcp_close() to send a segment including FIN flag but not data.
+ *
+ * @param pcb the tcp_pcb over which to send a segment
+ * @return ERR_OK if sent, another err_t otherwise
+ */
+err_t
+tcp_send_fin(struct tcp_pcb *pcb)
+{
+ /* first, try to add the fin to the last unsent segment */
+ if (pcb->unsent != NULL) {
+ struct tcp_seg *last_unsent;
+ for (last_unsent = pcb->unsent; last_unsent->next != NULL;
+ last_unsent = last_unsent->next);
+
+ if ((TCPH_FLAGS(last_unsent->tcphdr) & (TCP_SYN | TCP_FIN | TCP_RST)) == 0) {
+ /* no SYN/FIN/RST flag in the header, we can add the FIN flag */
+ TCPH_SET_FLAG(last_unsent->tcphdr, TCP_FIN);
+ return ERR_OK;
+ }
+ }
+ /* no data, no length, flags, copy=1, no optdata */
+ return tcp_enqueue_flags(pcb, TCP_FIN);
+}
+
+/**
+ * Create a TCP segment with prefilled header.
+ *
+ * Called by tcp_write and tcp_enqueue_flags.
+ *
+ * @param pcb Protocol control block for the TCP connection.
+ * @param p pbuf that is used to hold the TCP header.
+ * @param flags TCP flags for header.
+ * @param seqno TCP sequence number of this packet
+ * @param optflags options to include in TCP header
+ * @return a new tcp_seg pointing to p, or NULL.
+ * The TCP header is filled in except ackno and wnd.
+ * p is freed on failure.
+ */
+static struct tcp_seg *ICACHE_FLASH_ATTR
+tcp_create_segment(struct tcp_pcb *pcb, struct pbuf *p, u8_t flags, u32_t seqno, u8_t optflags)
+{
+ struct tcp_seg *seg;
+ u8_t optlen = LWIP_TCP_OPT_LENGTH(optflags);
+
+ if ((seg = (struct tcp_seg *)memp_malloc(MEMP_TCP_SEG)) == NULL) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_create_segment: no memory.\n"));
+ pbuf_free(p);
+ return NULL;
+ }
+ seg->flags = optflags;
+ seg->next = NULL;
+ seg->p = p;
+ seg->len = p->tot_len - optlen;
+#if TCP_OVERSIZE_DBGCHECK
+ seg->oversize_left = 0;
+#endif /* TCP_OVERSIZE_DBGCHECK */
+#if TCP_CHECKSUM_ON_COPY
+ seg->chksum = 0;
+ seg->chksum_swapped = 0;
+ /* check optflags */
+ LWIP_ASSERT("invalid optflags passed: TF_SEG_DATA_CHECKSUMMED",
+ (optflags & TF_SEG_DATA_CHECKSUMMED) == 0);
+#endif /* TCP_CHECKSUM_ON_COPY */
+
+ /* build TCP header */
+ if (pbuf_header(p, TCP_HLEN)) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_create_segment: no room for TCP header in pbuf.\n"));
+ TCP_STATS_INC(tcp.err);
+ tcp_seg_free(seg);
+ return NULL;
+ }
+ seg->tcphdr = (struct tcp_hdr *)seg->p->payload;
+ seg->tcphdr->src = htons(pcb->local_port);
+ seg->tcphdr->dest = htons(pcb->remote_port);
+ seg->tcphdr->seqno = htonl(seqno);
+ /* ackno is set in tcp_output */
+ TCPH_HDRLEN_FLAGS_SET(seg->tcphdr, (5 + optlen / 4), flags);
+ /* wnd and chksum are set in tcp_output */
+ seg->tcphdr->urgp = 0;
+ return seg;
+}
+
+/**
+ * Allocate a PBUF_RAM pbuf, perhaps with extra space at the end.
+ *
+ * This function is like pbuf_alloc(layer, length, PBUF_RAM) except
+ * there may be extra bytes available at the end.
+ *
+ * @param layer flag to define header size.
+ * @param length size of the pbuf's payload.
+ * @param max_length maximum usable size of payload+oversize.
+ * @param oversize pointer to a u16_t that will receive the number of usable tail bytes.
+ * @param pcb The TCP connection that willo enqueue the pbuf.
+ * @param apiflags API flags given to tcp_write.
+ * @param first_seg true when this pbuf will be used in the first enqueued segment.
+ * @param
+ */
+#if TCP_OVERSIZE
+static struct pbuf *ICACHE_FLASH_ATTR
+tcp_pbuf_prealloc(pbuf_layer layer, u16_t length, u16_t max_length,
+ u16_t *oversize, struct tcp_pcb *pcb, u8_t apiflags,
+ u8_t first_seg)
+{
+ struct pbuf *p;
+ u16_t alloc = length;
+
+#if LWIP_NETIF_TX_SINGLE_PBUF
+ LWIP_UNUSED_ARG(max_length);
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_UNUSED_ARG(apiflags);
+ LWIP_UNUSED_ARG(first_seg);
+ /* always create MSS-sized pbufs */
+ alloc = TCP_MSS;
+#else /* LWIP_NETIF_TX_SINGLE_PBUF */
+ if (length < max_length) {
+ /* Should we allocate an oversized pbuf, or just the minimum
+ * length required? If tcp_write is going to be called again
+ * before this segment is transmitted, we want the oversized
+ * buffer. If the segment will be transmitted immediately, we can
+ * save memory by allocating only length. We use a simple
+ * heuristic based on the following information:
+ *
+ * Did the user set TCP_WRITE_FLAG_MORE?
+ *
+ * Will the Nagle algorithm defer transmission of this segment?
+ */
+ if ((apiflags & TCP_WRITE_FLAG_MORE) ||
+ (!(pcb->flags & TF_NODELAY) &&
+ (!first_seg ||
+ pcb->unsent != NULL ||
+ pcb->unacked != NULL))) {
+ alloc = LWIP_MIN(max_length, LWIP_MEM_ALIGN_SIZE(length + TCP_OVERSIZE));
+ }
+ }
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+ p = pbuf_alloc(layer, alloc, PBUF_RAM);
+ if (p == NULL) {
+ return NULL;
+ }
+ LWIP_ASSERT("need unchained pbuf", p->next == NULL);
+ *oversize = p->len - length;
+ /* trim p->len to the currently used size */
+ p->len = p->tot_len = length;
+ return p;
+}
+#else /* TCP_OVERSIZE */
+#define tcp_pbuf_prealloc(layer, length, mx, os, pcb, api, fst) pbuf_alloc((layer), (length), PBUF_RAM)
+#endif /* TCP_OVERSIZE */
+
+#if TCP_CHECKSUM_ON_COPY
+/** Add a checksum of newly added data to the segment */
+static void ICACHE_FLASH_ATTR
+tcp_seg_add_chksum(u16_t chksum, u16_t len, u16_t *seg_chksum,
+ u8_t *seg_chksum_swapped)
+{
+ u32_t helper;
+ /* add chksum to old chksum and fold to u16_t */
+ helper = chksum + *seg_chksum;
+ chksum = FOLD_U32T(helper);
+ if ((len & 1) != 0) {
+ *seg_chksum_swapped = 1 - *seg_chksum_swapped;
+ chksum = SWAP_BYTES_IN_WORD(chksum);
+ }
+ *seg_chksum = chksum;
+}
+#endif /* TCP_CHECKSUM_ON_COPY */
+
+/** Checks if tcp_write is allowed or not (checks state, snd_buf and snd_queuelen).
+ *
+ * @param pcb the tcp pcb to check for
+ * @param len length of data to send (checked agains snd_buf)
+ * @return ERR_OK if tcp_write is allowed to proceed, another err_t otherwise
+ */
+static err_t ICACHE_FLASH_ATTR
+tcp_write_checks(struct tcp_pcb *pcb, u16_t len)
+{
+ /* connection is in invalid state for data transmission? */
+ if ((pcb->state != ESTABLISHED) &&
+ (pcb->state != CLOSE_WAIT) &&
+ (pcb->state != SYN_SENT) &&
+ (pcb->state != SYN_RCVD)) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_STATE | LWIP_DBG_LEVEL_SEVERE, ("tcp_write() called in invalid state\n"));
+ return ERR_CONN;
+ } else if (len == 0) {
+ return ERR_OK;
+ }
+
+ /* fail on too much data */
+ if (len > pcb->snd_buf) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_write: too much data (len=%"U16_F" > snd_buf=%"U16_F")\n",
+ len, pcb->snd_buf));
+ pcb->flags |= TF_NAGLEMEMERR;
+ return ERR_MEM;
+ }
+
+ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: queuelen: %"U16_F"\n", (u16_t)pcb->snd_queuelen));
+
+ /* If total number of pbufs on the unsent/unacked queues exceeds the
+ * configured maximum, return an error */
+ /* check for configured max queuelen and possible overflow */
+ if ((pcb->snd_queuelen >= TCP_SND_QUEUELEN) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_write: too long queue %"U16_F" (max %"U16_F")\n",
+ pcb->snd_queuelen, TCP_SND_QUEUELEN));
+ TCP_STATS_INC(tcp.memerr);
+ pcb->flags |= TF_NAGLEMEMERR;
+ return ERR_MEM;
+ }
+ if (pcb->snd_queuelen != 0) {
+ LWIP_ASSERT("tcp_write: pbufs on queue => at least one queue non-empty",
+ pcb->unacked != NULL || pcb->unsent != NULL);
+ } else {
+ LWIP_ASSERT("tcp_write: no pbufs on queue => both queues empty",
+ pcb->unacked == NULL && pcb->unsent == NULL);
+ }
+ return ERR_OK;
+}
+
+/**
+ * Write data for sending (but does not send it immediately).
+ *һݣúһĶβڿƿ黺
+ * It waits in the expectation of more data being sent soon (as
+ * it can send them more efficiently by combining them together).
+ * To prompt the system to send data now, call tcp_output() after
+ * calling tcp_write().
+ *
+ * @param pcb Protocol control block for the TCP connection to enqueue data for.Ӧӿƿ
+ * @param arg Pointer to the data to be enqueued for sending.ʼַ
+ * @param len Data length in bytesݳ
+ * @param apiflags combination of following flags :ǷпԼĶײǷPSH־
+ * - TCP_WRITE_FLAG_COPY (0x01) data will be copied into memory belonging to the stack
+ * - TCP_WRITE_FLAG_MORE (0x02) for TCP connection, PSH flag will be set on last segment sent,
+ * @return ERR_OK if enqueued, another err_t on error
+ */
+err_t
+tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
+{
+ struct pbuf *concat_p = NULL;
+ struct tcp_seg *last_unsent = NULL, *seg = NULL, *prev_seg = NULL, *queue = NULL;
+ u16_t pos = 0; /* position in 'arg' data */
+ u16_t queuelen;
+ u8_t optlen = 0;
+ u8_t optflags = 0;
+#if TCP_OVERSIZE
+ u16_t oversize = 0;
+ u16_t oversize_used = 0;
+#endif /* TCP_OVERSIZE */
+#if TCP_CHECKSUM_ON_COPY
+ u16_t concat_chksum = 0;
+ u8_t concat_chksum_swapped = 0;
+ u16_t concat_chksummed = 0;
+#endif /* TCP_CHECKSUM_ON_COPY */
+ err_t err;
+
+#if LWIP_NETIF_TX_SINGLE_PBUF
+ /* Always copy to try to create single pbufs for TX */
+ apiflags |= TCP_WRITE_FLAG_COPY;
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_write(pcb=%p, data=%p, len=%"U16_F", apiflags=%"U16_F")\n",
+ (void *)pcb, arg, len, (u16_t)apiflags));
+ LWIP_ERROR("tcp_write: arg == NULL (programmer violates API)",
+ arg != NULL, return ERR_ARG;);
+
+ err = tcp_write_checks(pcb, len);
+ if (err != ERR_OK) {
+ return err;
+ }
+ queuelen = pcb->snd_queuelen;
+
+#if LWIP_TCP_TIMESTAMPS
+ if ((pcb->flags & TF_TIMESTAMP)) {
+ optflags = TF_SEG_OPTS_TS;
+ optlen = LWIP_TCP_OPT_LENGTH(TF_SEG_OPTS_TS);
+ }
+#endif /* LWIP_TCP_TIMESTAMPS */
+
+
+ /*
+ * TCP segmentation is done in three phases with increasing complexity:
+ *
+ * 1. Copy data directly into an oversized pbuf.
+ * 2. Chain a new pbuf to the end of pcb->unsent.
+ * 3. Create new segments.
+ *
+ * We may run out of memory at any point. In that case we must
+ * return ERR_MEM and not change anything in pcb. Therefore, all
+ * changes are recorded in local variables and committed at the end
+ * of the function. Some pcb fields are maintained in local copies:
+ *
+ * queuelen = pcb->snd_queuelen
+ * oversize = pcb->unsent_oversize
+ *
+ * These variables are set consistently by the phases:
+ *
+ * seg points to the last segment tampered with.
+ *
+ * pos records progress as data is segmented.
+ */
+
+ /* Find the tail of the unsent queue. */
+ if (pcb->unsent != NULL) {
+ u16_t space;
+ u16_t unsent_optlen;
+
+ /* @todo: this could be sped up by keeping last_unsent in the pcb */
+ for (last_unsent = pcb->unsent; last_unsent->next != NULL;
+ last_unsent = last_unsent->next);
+
+ /* Usable space at the end of the last unsent segment */
+ unsent_optlen = LWIP_TCP_OPT_LENGTH(last_unsent->flags);
+ space = pcb->mss - (last_unsent->len + unsent_optlen);
+
+ /*
+ * Phase 1: Copy data directly into an oversized pbuf.
+ *
+ * The number of bytes copied is recorded in the oversize_used
+ * variable. The actual copying is done at the bottom of the
+ * function.
+ */
+#if TCP_OVERSIZE
+#if TCP_OVERSIZE_DBGCHECK
+ /* check that pcb->unsent_oversize matches last_unsent->unsent_oversize */
+ LWIP_ASSERT("unsent_oversize mismatch (pcb vs. last_unsent)",
+ pcb->unsent_oversize == last_unsent->oversize_left);
+#endif /* TCP_OVERSIZE_DBGCHECK */
+ oversize = pcb->unsent_oversize;
+ if (oversize > 0) {
+ LWIP_ASSERT("inconsistent oversize vs. space", oversize_used <= space);
+ seg = last_unsent;
+ oversize_used = oversize < len ? oversize : len;
+ pos += oversize_used;
+ oversize -= oversize_used;
+ space -= oversize_used;
+ }
+ /* now we are either finished or oversize is zero */
+ LWIP_ASSERT("inconsistend oversize vs. len", (oversize == 0) || (pos == len));
+#endif /* TCP_OVERSIZE */
+
+ /*
+ * Phase 2: Chain a new pbuf to the end of pcb->unsent.
+ *
+ * We don't extend segments containing SYN/FIN flags or options
+ * (len==0). The new pbuf is kept in concat_p and pbuf_cat'ed at
+ * the end.
+ */
+ if ((pos < len) && (space > 0) && (last_unsent->len > 0)) {
+ u16_t seglen = space < len - pos ? space : len - pos;
+ seg = last_unsent;
+
+ /* Create a pbuf with a copy or reference to seglen bytes. We
+ * can use PBUF_RAW here since the data appears in the middle of
+ * a segment. A header will never be prepended. */
+ if (apiflags & TCP_WRITE_FLAG_COPY) {
+ /* Data is copied */
+ if ((concat_p = tcp_pbuf_prealloc(PBUF_RAW, seglen, space, &oversize, pcb, apiflags, 1)) == NULL) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2,
+ ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n",
+ seglen));
+ goto memerr;
+ }
+#if TCP_OVERSIZE_DBGCHECK
+ last_unsent->oversize_left = oversize;
+#endif /* TCP_OVERSIZE_DBGCHECK */
+ TCP_DATA_COPY2(concat_p->payload, (u8_t*)arg + pos, seglen, &concat_chksum, &concat_chksum_swapped);
+#if TCP_CHECKSUM_ON_COPY
+ concat_chksummed += seglen;
+#endif /* TCP_CHECKSUM_ON_COPY */
+ } else {
+ /* Data is not copied */
+ if ((concat_p = pbuf_alloc(PBUF_RAW, seglen, PBUF_ROM)) == NULL) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2,
+ ("tcp_write: could not allocate memory for zero-copy pbuf\n"));
+ goto memerr;
+ }
+#if TCP_CHECKSUM_ON_COPY
+ /* calculate the checksum of nocopy-data */
+ tcp_seg_add_chksum(~inet_chksum((u8_t*)arg + pos, seglen), seglen,
+ &concat_chksum, &concat_chksum_swapped);
+ concat_chksummed += seglen;
+#endif /* TCP_CHECKSUM_ON_COPY */
+ /* reference the non-volatile payload data */
+ concat_p->payload = (u8_t*)arg + pos;
+ }
+
+ pos += seglen;
+ queuelen += pbuf_clen(concat_p);
+ }
+ } else {
+#if TCP_OVERSIZE
+ LWIP_ASSERT("unsent_oversize mismatch (pcb->unsent is NULL)",
+ pcb->unsent_oversize == 0);
+#endif /* TCP_OVERSIZE */
+ }
+
+ /*
+ * Phase 3: Create new segments.
+ *
+ * The new segments are chained together in the local 'queue'
+ * variable, ready to be appended to pcb->unsent.
+ */
+ while (pos < len) {
+ struct pbuf *p;
+ u16_t left = len - pos;
+ u16_t max_len = pcb->mss - optlen;
+ u16_t seglen = left > max_len ? max_len : left;
+#if TCP_CHECKSUM_ON_COPY
+ u16_t chksum = 0;
+ u8_t chksum_swapped = 0;
+#endif /* TCP_CHECKSUM_ON_COPY */
+
+ if (apiflags & TCP_WRITE_FLAG_COPY) {
+ /* If copy is set, memory should be allocated and data copied
+ * into pbuf */
+ if ((p = tcp_pbuf_prealloc(PBUF_TRANSPORT, seglen + optlen, pcb->mss, &oversize, pcb, apiflags, queue == NULL)) == NULL) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n", seglen));
+ goto memerr;
+ }
+ LWIP_ASSERT("tcp_write: check that first pbuf can hold the complete seglen",
+ (p->len >= seglen));
+ TCP_DATA_COPY2((char *)p->payload + optlen, (u8_t*)arg + pos, seglen, &chksum, &chksum_swapped);
+ } else {
+ /* Copy is not set: First allocate a pbuf for holding the data.
+ * Since the referenced data is available at least until it is
+ * sent out on the link (as it has to be ACKed by the remote
+ * party) we can safely use PBUF_ROM instead of PBUF_REF here.
+ */
+ struct pbuf *p2;
+#if TCP_OVERSIZE
+ LWIP_ASSERT("oversize == 0", oversize == 0);
+#endif /* TCP_OVERSIZE */
+ if ((p2 = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_ROM)) == NULL) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: could not allocate memory for zero-copy pbuf\n"));
+ goto memerr;
+ }
+#if TCP_CHECKSUM_ON_COPY
+ /* calculate the checksum of nocopy-data */
+ chksum = ~inet_chksum((u8_t*)arg + pos, seglen);
+#endif /* TCP_CHECKSUM_ON_COPY */
+ /* reference the non-volatile payload data */
+ p2->payload = (u8_t*)arg + pos;
+
+ /* Second, allocate a pbuf for the headers. */
+ if ((p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) {
+ /* If allocation fails, we have to deallocate the data pbuf as
+ * well. */
+ pbuf_free(p2);
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: could not allocate memory for header pbuf\n"));
+ goto memerr;
+ }
+ /* Concatenate the headers and data pbufs together. */
+ pbuf_cat(p/*header*/, p2/*data*/);
+ }
+
+ queuelen += pbuf_clen(p);
+
+ /* Now that there are more segments queued, we check again if the
+ * length of the queue exceeds the configured maximum or
+ * overflows. */
+ if ((queuelen > TCP_SND_QUEUELEN) || (queuelen > TCP_SNDQUEUELEN_OVERFLOW)) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: queue too long %"U16_F" (%"U16_F")\n", queuelen, TCP_SND_QUEUELEN));
+ pbuf_free(p);
+ goto memerr;
+ }
+
+ if ((seg = tcp_create_segment(pcb, p, 0, pcb->snd_lbb + pos, optflags)) == NULL) {
+ goto memerr;
+ }
+#if TCP_OVERSIZE_DBGCHECK
+ seg->oversize_left = oversize;
+#endif /* TCP_OVERSIZE_DBGCHECK */
+#if TCP_CHECKSUM_ON_COPY
+ seg->chksum = chksum;
+ seg->chksum_swapped = chksum_swapped;
+ seg->flags |= TF_SEG_DATA_CHECKSUMMED;
+#endif /* TCP_CHECKSUM_ON_COPY */
+
+ /* first segment of to-be-queued data? */
+ if (queue == NULL) {
+ queue = seg;
+ } else {
+ /* Attach the segment to the end of the queued segments */
+ LWIP_ASSERT("prev_seg != NULL", prev_seg != NULL);
+ prev_seg->next = seg;
+ }
+ /* remember last segment of to-be-queued data for next iteration */
+ prev_seg = seg;
+
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE, ("tcp_write: queueing %"U32_F":%"U32_F"\n",
+ ntohl(seg->tcphdr->seqno),
+ ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg)));
+
+ pos += seglen;
+ }
+
+ /*
+ * All three segmentation phases were successful. We can commit the
+ * transaction.
+ */
+
+ /*
+ * Phase 1: If data has been added to the preallocated tail of
+ * last_unsent, we update the length fields of the pbuf chain.
+ */
+#if TCP_OVERSIZE
+ if (oversize_used > 0) {
+ struct pbuf *p;
+ /* Bump tot_len of whole chain, len of tail */
+ for (p = last_unsent->p; p; p = p->next) {
+ p->tot_len += oversize_used;
+ if (p->next == NULL) {
+ TCP_DATA_COPY((char *)p->payload + p->len, arg, oversize_used, last_unsent);
+ p->len += oversize_used;
+ }
+ }
+ last_unsent->len += oversize_used;
+#if TCP_OVERSIZE_DBGCHECK
+ last_unsent->oversize_left -= oversize_used;
+#endif /* TCP_OVERSIZE_DBGCHECK */
+ }
+ pcb->unsent_oversize = oversize;
+#endif /* TCP_OVERSIZE */
+
+ /*
+ * Phase 2: concat_p can be concatenated onto last_unsent->p
+ */
+ if (concat_p != NULL) {
+ LWIP_ASSERT("tcp_write: cannot concatenate when pcb->unsent is empty",
+ (last_unsent != NULL));
+ pbuf_cat(last_unsent->p, concat_p);
+ last_unsent->len += concat_p->tot_len;
+#if TCP_CHECKSUM_ON_COPY
+ if (concat_chksummed) {
+ tcp_seg_add_chksum(concat_chksum, concat_chksummed, &last_unsent->chksum,
+ &last_unsent->chksum_swapped);
+ last_unsent->flags |= TF_SEG_DATA_CHECKSUMMED;
+ }
+#endif /* TCP_CHECKSUM_ON_COPY */
+ }
+
+ /*
+ * Phase 3: Append queue to pcb->unsent. Queue may be NULL, but that
+ * is harmless
+ */
+ if (last_unsent == NULL) {
+ pcb->unsent = queue;
+ } else {
+ last_unsent->next = queue;
+ }
+
+ /*
+ * Finally update the pcb state.
+ */
+ pcb->snd_lbb += len;
+ pcb->snd_buf -= len;
+ pcb->snd_queuelen = queuelen;
+
+ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: %"S16_F" (after enqueued)\n",
+ pcb->snd_queuelen));
+ if (pcb->snd_queuelen != 0) {
+ LWIP_ASSERT("tcp_write: valid queue length",
+ pcb->unacked != NULL || pcb->unsent != NULL);
+ }
+
+ /* Set the PSH flag in the last segment that we enqueued. */
+ if (seg != NULL && seg->tcphdr != NULL && ((apiflags & TCP_WRITE_FLAG_MORE)==0)) {
+ TCPH_SET_FLAG(seg->tcphdr, TCP_PSH);
+ }
+
+ return ERR_OK;
+memerr:
+ pcb->flags |= TF_NAGLEMEMERR;
+ TCP_STATS_INC(tcp.memerr);
+
+ if (concat_p != NULL) {
+ pbuf_free(concat_p);
+ }
+ if (queue != NULL) {
+ tcp_segs_free(queue);
+ }
+ if (pcb->snd_queuelen != 0) {
+ LWIP_ASSERT("tcp_write: valid queue length", pcb->unacked != NULL ||
+ pcb->unsent != NULL);
+ }
+ LWIP_DEBUGF(TCP_QLEN_DEBUG | LWIP_DBG_STATE, ("tcp_write: %"S16_F" (with mem err)\n", pcb->snd_queuelen));
+ return ERR_MEM;
+}
+
+/**
+ * Enqueue TCP options for transmission.
+ *
+ * Called by tcp_connect(), tcp_listen_input(), and tcp_send_ctrl().
+ *
+ * @param pcb Protocol control block for the TCP connection.
+ * @param flags TCP header flags to set in the outgoing segment.
+ * @param optdata pointer to TCP options, or NULL.
+ * @param optlen length of TCP options in bytes.
+ */
+err_t
+tcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags)
+{
+ struct pbuf *p;
+ struct tcp_seg *seg;
+ u8_t optflags = 0;
+ u8_t optlen = 0;
+
+ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue_flags: queuelen: %"U16_F"\n", (u16_t)pcb->snd_queuelen));
+
+ LWIP_ASSERT("tcp_enqueue_flags: need either TCP_SYN or TCP_FIN in flags (programmer violates API)",
+ (flags & (TCP_SYN | TCP_FIN)) != 0);
+
+ /* check for configured max queuelen and possible overflow */
+ if ((pcb->snd_queuelen >= TCP_SND_QUEUELEN) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue_flags: too long queue %"U16_F" (max %"U16_F")\n",
+ pcb->snd_queuelen, TCP_SND_QUEUELEN));
+ TCP_STATS_INC(tcp.memerr);
+ pcb->flags |= TF_NAGLEMEMERR;
+ return ERR_MEM;
+ }
+
+ if (flags & TCP_SYN) {
+ optflags = TF_SEG_OPTS_MSS;
+ }
+#if LWIP_TCP_TIMESTAMPS
+ if ((pcb->flags & TF_TIMESTAMP)) {
+ optflags |= TF_SEG_OPTS_TS;
+ }
+#endif /* LWIP_TCP_TIMESTAMPS */
+ optlen = LWIP_TCP_OPT_LENGTH(optflags);
+
+ /* tcp_enqueue_flags is always called with either SYN or FIN in flags.
+ * We need one available snd_buf byte to do that.
+ * This means we can't send FIN while snd_buf==0. A better fix would be to
+ * not include SYN and FIN sequence numbers in the snd_buf count. */
+ if (pcb->snd_buf == 0) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue_flags: no send buffer available\n"));
+ TCP_STATS_INC(tcp.memerr);
+ return ERR_MEM;
+ }
+
+ /* Allocate pbuf with room for TCP header + options */
+ if ((p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) {
+ pcb->flags |= TF_NAGLEMEMERR;
+ TCP_STATS_INC(tcp.memerr);
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("tcp_enqueue_flags: check that first pbuf can hold optlen",
+ (p->len >= optlen));
+
+ /* Allocate memory for tcp_seg, and fill in fields. */
+ if ((seg = tcp_create_segment(pcb, p, flags, pcb->snd_lbb, optflags)) == NULL) {
+ pcb->flags |= TF_NAGLEMEMERR;
+ TCP_STATS_INC(tcp.memerr);
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("seg->tcphdr not aligned", ((mem_ptr_t)seg->tcphdr % MEM_ALIGNMENT) == 0);
+ LWIP_ASSERT("tcp_enqueue_flags: invalid segment length", seg->len == 0);
+
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE,
+ ("tcp_enqueue_flags: queueing %"U32_F":%"U32_F" (0x%"X16_F")\n",
+ ntohl(seg->tcphdr->seqno),
+ ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg),
+ (u16_t)flags));
+
+ /* Now append seg to pcb->unsent queue */
+ if (pcb->unsent == NULL) {
+ pcb->unsent = seg;
+ } else {
+ struct tcp_seg *useg;
+ for (useg = pcb->unsent; useg->next != NULL; useg = useg->next);
+ useg->next = seg;
+ }
+#if TCP_OVERSIZE
+ /* The new unsent tail has no space */
+ pcb->unsent_oversize = 0;
+#endif /* TCP_OVERSIZE */
+
+ /* SYN and FIN bump the sequence number */
+ if ((flags & TCP_SYN) || (flags & TCP_FIN)) {
+ pcb->snd_lbb++;
+ /* optlen does not influence snd_buf */
+ pcb->snd_buf--;
+ }
+ if (flags & TCP_FIN) {
+ pcb->flags |= TF_FIN;
+ }
+
+ /* update number of segments on the queues */
+ pcb->snd_queuelen += pbuf_clen(seg->p);
+ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue_flags: %"S16_F" (after enqueued)\n", pcb->snd_queuelen));
+ if (pcb->snd_queuelen != 0) {
+ LWIP_ASSERT("tcp_enqueue_flags: invalid queue length",
+ pcb->unacked != NULL || pcb->unsent != NULL);
+ }
+
+ return ERR_OK;
+}
+
+
+#if LWIP_TCP_TIMESTAMPS
+/* Build a timestamp option (12 bytes long) at the specified options pointer)
+ *
+ * @param pcb tcp_pcb
+ * @param opts option pointer where to store the timestamp option
+ */
+static void ICACHE_FLASH_ATTR
+tcp_build_timestamp_option(struct tcp_pcb *pcb, u32_t *opts)
+{
+ /* Pad with two NOP options to make everything nicely aligned */
+ opts[0] = PP_HTONL(0x0101080A);
+ opts[1] = htonl(sys_now());
+ opts[2] = htonl(pcb->ts_recent);
+}
+#endif
+
+/** Send an ACK without data.
+ *
+ * @param pcb Protocol control block for the TCP connection to send the ACK
+ */
+err_t
+tcp_send_empty_ack(struct tcp_pcb *pcb)
+{
+ struct pbuf *p;
+ struct tcp_hdr *tcphdr;
+ u8_t optlen = 0;
+
+#if LWIP_TCP_TIMESTAMPS
+ if (pcb->flags & TF_TIMESTAMP) {
+ optlen = LWIP_TCP_OPT_LENGTH(TF_SEG_OPTS_TS);
+ }
+#endif
+
+ p = tcp_output_alloc_header(pcb, optlen, 0, htonl(pcb->snd_nxt));
+ if (p == NULL) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: (ACK) could not allocate pbuf\n"));
+ return ERR_BUF;
+ }
+ tcphdr = (struct tcp_hdr *)p->payload;
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG,
+ ("tcp_output: sending ACK for %"U32_F"\n", pcb->rcv_nxt));
+ /* remove ACK flags from the PCB, as we send an empty ACK now */
+ pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
+
+ /* NB. MSS option is only sent on SYNs, so ignore it here */
+#if LWIP_TCP_TIMESTAMPS
+ pcb->ts_lastacksent = pcb->rcv_nxt;
+
+ if (pcb->flags & TF_TIMESTAMP) {
+ tcp_build_timestamp_option(pcb, (u32_t *)(tcphdr + 1));
+ }
+#endif
+
+#if CHECKSUM_GEN_TCP
+ tcphdr->chksum = inet_chksum_pseudo(p, &(pcb->local_ip), &(pcb->remote_ip),
+ IP_PROTO_TCP, p->tot_len);
+#endif
+#if LWIP_NETIF_HWADDRHINT
+ ip_output_hinted(p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos,
+ IP_PROTO_TCP, &(pcb->addr_hint));
+#else /* LWIP_NETIF_HWADDRHINT*/
+ ip_output(p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos,
+ IP_PROTO_TCP);
+#endif /* LWIP_NETIF_HWADDRHINT*/
+ pbuf_free(p);
+
+ return ERR_OK;
+}
+
+/**
+ * Find out what we can send and send it
+ *Ϳƿ黺еıĶ
+ * @param pcb Protocol control block for the TCP connection to send data
+ * @return ERR_OK if data has been sent or nothing to send
+ * another err_t on error
+ */
+err_t ICACHE_FLASH_ATTR
+tcp_output(struct tcp_pcb *pcb)
+{
+ struct tcp_seg *seg, *useg;
+ u32_t wnd, snd_nxt;
+#if TCP_CWND_DEBUG
+ s16_t i = 0;
+#endif /* TCP_CWND_DEBUG */
+ /* First, check if we are invoked by the TCP input processing
+ code. If so, we do not output anything. Instead, we rely on the
+ input processing code to call us when input processing is done
+ with. ƿ鵱ǰݱֱӷ*/
+ if (tcp_input_pcb == pcb) {
+ return ERR_OK;
+ }
+
+ wnd = LWIP_MIN(pcb->snd_wnd, pcb->cwnd);//ӷʹںȡСߵõЧʹ
+
+ seg = pcb->unsent;
+
+ /* If the TF_ACK_NOW flag is set and no data will be sent (either
+ * because the ->unsent queue is empty or because the window does
+ * not allow it), construct an empty ACK segment and send it.
+ *
+ * If data is to be sent, we will just piggyback the ACK (see below).
+ */
+ if (pcb->flags & TF_ACK_NOW &&
+ (seg == NULL ||
+ ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd)) {
+ return tcp_send_empty_ack(pcb);//ֻACKıĶ
+ }
+
+ /* useg should point to last segment on unacked queue */
+ useg = pcb->unacked;
+ if (useg != NULL) {
+ for (; useg->next != NULL; useg = useg->next);//õβ
+ }
+
+#if TCP_OUTPUT_DEBUG
+ if (seg == NULL) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: nothing to send (%p)\n",
+ (void*)pcb->unsent));
+ }
+#endif /* TCP_OUTPUT_DEBUG */
+#if TCP_CWND_DEBUG
+ if (seg == NULL) {
+ LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U16_F
+ ", cwnd %"U16_F", wnd %"U32_F
+ ", seg == NULL, ack %"U32_F"\n",
+ pcb->snd_wnd, pcb->cwnd, wnd, pcb->lastack));
+ } else {
+ LWIP_DEBUGF(TCP_CWND_DEBUG,
+ ("tcp_output: snd_wnd %"U16_F", cwnd %"U16_F", wnd %"U32_F
+ ", effwnd %"U32_F", seq %"U32_F", ack %"U32_F"\n",
+ pcb->snd_wnd, pcb->cwnd, wnd,
+ ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len,
+ ntohl(seg->tcphdr->seqno), pcb->lastack));
+ }
+#endif /* TCP_CWND_DEBUG */
+ /* data available and window allows it to be sent?
+ *ǰЧķͣѭͱģֱ*/
+ while (seg != NULL &&
+ ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) {
+ LWIP_ASSERT("RST not expected here!",
+ (TCPH_FLAGS(seg->tcphdr) & TCP_RST) == 0);
+ /* Stop sending if the nagle algorithm would prevent it
+ * Don't stop:
+ * - if tcp_write had a memory error before (prevent delayed ACK timeout) or
+ * - if FIN was already enqueued for this PCB (SYN is always alone in a segment -
+ * either seg->next != NULL or pcb->unacked == NULL;
+ * RST is no sent using tcp_write/tcp_output.
+ */
+ if((tcp_do_output_nagle(pcb) == 0) &&
+ ((pcb->flags & (TF_NAGLEMEMERR | TF_FIN)) == 0)){
+ break;
+ }
+#if TCP_CWND_DEBUG
+ LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U16_F", cwnd %"U16_F", wnd %"U32_F", effwnd %"U32_F", seq %"U32_F", ack %"U32_F", i %"S16_F"\n",
+ pcb->snd_wnd, pcb->cwnd, wnd,
+ ntohl(seg->tcphdr->seqno) + seg->len -
+ pcb->lastack,
+ ntohl(seg->tcphdr->seqno), pcb->lastack, i));
+ ++i;
+#endif /* TCP_CWND_DEBUG */
+
+ pcb->unsent = seg->next;
+
+ if (pcb->state != SYN_SENT) {
+ TCPH_SET_FLAG(seg->tcphdr, TCP_ACK);//дײACK־
+ pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);//־λ
+ }
+
+ tcp_output_segment(seg, pcb);//úͱĶ
+
+ snd_nxt = ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg);//snd_nxt
+ if (TCP_SEQ_LT(pcb->snd_nxt, snd_nxt)) {
+ pcb->snd_nxt = snd_nxt;//Ҫ͵ݱ
+ }
+ /* put segment on unacknowledged list if length > 0 ȥıĶݳȲΪ0ߴ
+ * SYNFIN־ñĶμ뵽δȷ϶УԱ㳬ʱش*/
+ if (TCP_TCPLEN(seg) > 0) {
+ seg->next = NULL;
+ /* unacked list is empty? ֱӹҽ*/
+ if (pcb->unacked == NULL) {
+ pcb->unacked = seg;
+ useg = seg;
+ /* unacked list is not empty?ǰİ˳֯ڶ */
+ } else {
+ /* In the case of fast retransmit, the packet should not go to the tail
+ * of the unacked queue, but rather somewhere before it. We need to check for
+ * this case. -STJ Jul 27, 2004 */ //ǰĵкŵڶβкţ
+ //Ӷײʼ
+ if (TCP_SEQ_LT(ntohl(seg->tcphdr->seqno), ntohl(useg->tcphdr->seqno))) {
+ /* add segment to before tail of unacked list, keeping the list sorted */
+ struct tcp_seg **cur_seg = &(pcb->unacked);
+ while (*cur_seg &&
+ TCP_SEQ_LT(ntohl((*cur_seg)->tcphdr->seqno), ntohl(seg->tcphdr->seqno))) {
+ cur_seg = &((*cur_seg)->next );
+ }
+ seg->next = (*cur_seg);
+ (*cur_seg) = seg;
+ } else {//ߣδȷ϶ĩβ
+ /* add segment to tail of unacked list */
+ useg->next = seg;
+ useg = useg->next;
+ }
+ }
+ /* do not queue empty segments on the unacked list */
+ } else {//ĶγΪ0ֱɾش
+ tcp_seg_free(seg);
+ }
+ seg = pcb->unsent;//һĶ
+ }
+#if TCP_OVERSIZE
+ if (pcb->unsent == NULL) {
+ /* last unsent has been removed, reset unsent_oversize */
+ pcb->unsent_oversize = 0;
+ }
+#endif /* TCP_OVERSIZE */
+
+//ʹ±IJܷͣ㴰̽⡣
+ if (seg != NULL && pcb->persist_backoff == 0 &&
+ ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > pcb->snd_wnd) {
+ /* prepare for persist timer */
+ pcb->persist_cnt = 0;
+ pcb->persist_backoff = 1;
+ }
+
+ pcb->flags &= ~TF_NAGLEMEMERR;//ڴ־
+ return ERR_OK;
+}
+
+/**
+ * Called by tcp_output() to actually send a TCP segment over IP.
+ *
+ * @param seg the tcp_seg to send
+ * @param pcb the tcp_pcb for the TCP connection used to send the segment
+ */
+
+static void
+tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb)
+{
+ u16_t len;
+ struct netif *netif;
+ u32_t *opts;
+ /** @bug Exclude retransmitted segments from this count. */
+ snmp_inc_tcpoutsegs();
+
+ /* The TCP header has already been constructed, but the ackno and
+ wnd fields remain. */
+ seg->tcphdr->ackno = htonl(pcb->rcv_nxt);
+
+ /* advertise our receive window size in this TCP segment */
+ seg->tcphdr->wnd = htons(pcb->rcv_ann_wnd);
+
+ pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd;
+
+ /* Add any requested options. NB MSS option is only set on SYN
+ packets, so ignore it here */
+ LWIP_ASSERT("seg->tcphdr not aligned", ((mem_ptr_t)seg->tcphdr % MEM_ALIGNMENT) == 0);
+ opts = (u32_t *)(void *)(seg->tcphdr + 1);
+ if (seg->flags & TF_SEG_OPTS_MSS) {
+ TCP_BUILD_MSS_OPTION(*opts);
+ opts += 1;
+ }
+#if LWIP_TCP_TIMESTAMPS
+ pcb->ts_lastacksent = pcb->rcv_nxt;
+
+ if (seg->flags & TF_SEG_OPTS_TS) {
+ tcp_build_timestamp_option(pcb, opts);
+ opts += 3;
+ }
+#endif
+
+ /* Set retransmission timer running if it is not currently enabled
+ This must be set before checking the route. modify by ives at 2014.4.24*/
+ if (pcb->rtime == -1) {
+ pcb->rtime = 0;
+ }
+
+ /* If we don't have a local IP address, we get one by
+ calling ip_route(). */
+ if (ip_addr_isany(&(pcb->local_ip))) {
+ netif = ip_route(&(pcb->remote_ip));
+ if (netif == NULL) {
+ return;
+ }
+ ip_addr_copy(pcb->local_ip, netif->ip_addr);
+ }
+
+ if (pcb->rttest == 0) {
+ pcb->rttest = tcp_ticks;
+ pcb->rtseq = ntohl(seg->tcphdr->seqno);
+
+ LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_output_segment: rtseq %"U32_F"\n", pcb->rtseq));
+ }
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output_segment: %"U32_F":%"U32_F"\n",
+ htonl(seg->tcphdr->seqno), htonl(seg->tcphdr->seqno) +
+ seg->len));
+
+ len = (u16_t)((u8_t *)seg->tcphdr - (u8_t *)seg->p->payload);
+
+ seg->p->len -= len;
+ seg->p->tot_len -= len;
+
+ seg->p->payload = seg->tcphdr;
+
+ seg->tcphdr->chksum = 0;
+#if CHECKSUM_GEN_TCP
+#if TCP_CHECKSUM_ON_COPY
+ {
+ u32_t acc;
+#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK
+ u16_t chksum_slow = inet_chksum_pseudo(seg->p, &(pcb->local_ip),
+ &(pcb->remote_ip),
+ IP_PROTO_TCP, seg->p->tot_len);
+#endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */
+ if ((seg->flags & TF_SEG_DATA_CHECKSUMMED) == 0) {
+ LWIP_ASSERT("data included but not checksummed",
+ seg->p->tot_len == (TCPH_HDRLEN(seg->tcphdr) * 4));
+ }
+
+ /* rebuild TCP header checksum (TCP header changes for retransmissions!) */
+ acc = inet_chksum_pseudo_partial(seg->p, &(pcb->local_ip),
+ &(pcb->remote_ip),
+ IP_PROTO_TCP, seg->p->tot_len, TCPH_HDRLEN(seg->tcphdr) * 4);
+ /* add payload checksum */
+ if (seg->chksum_swapped) {
+ seg->chksum = SWAP_BYTES_IN_WORD(seg->chksum);
+ seg->chksum_swapped = 0;
+ }
+ acc += (u16_t)~(seg->chksum);
+ seg->tcphdr->chksum = FOLD_U32T(acc);
+#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK
+ if (chksum_slow != seg->tcphdr->chksum) {
+ LWIP_DEBUGF(TCP_DEBUG | LWIP_DBG_LEVEL_WARNING,
+ ("tcp_output_segment: calculated checksum is %"X16_F" instead of %"X16_F"\n",
+ seg->tcphdr->chksum, chksum_slow));
+ seg->tcphdr->chksum = chksum_slow;
+ }
+#endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */
+ }
+#else /* TCP_CHECKSUM_ON_COPY */
+ seg->tcphdr->chksum = inet_chksum_pseudo(seg->p, &(pcb->local_ip),
+ &(pcb->remote_ip),
+ IP_PROTO_TCP, seg->p->tot_len);
+#endif /* TCP_CHECKSUM_ON_COPY */
+#endif /* CHECKSUM_GEN_TCP */
+ TCP_STATS_INC(tcp.xmit);
+
+#if LWIP_NETIF_HWADDRHINT
+ ip_output_hinted(seg->p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos,
+ IP_PROTO_TCP, &(pcb->addr_hint));
+#else /* LWIP_NETIF_HWADDRHINT*/
+ ip_output(seg->p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos,
+ IP_PROTO_TCP);
+#endif /* LWIP_NETIF_HWADDRHINT*/
+}
+
+/**
+ * Send a TCP RESET packet (empty segment with RST flag set) either to
+ * abort a connection or to show that there is no matching local connection
+ * for a received segment.
+ *
+ * Called by tcp_abort() (to abort a local connection), tcp_input() (if no
+ * matching local pcb was found), tcp_listen_input() (if incoming segment
+ * has ACK flag set) and tcp_process() (received segment in the wrong state)
+ *
+ * Since a RST segment is in most cases not sent for an active connection,
+ * tcp_rst() has a number of arguments that are taken from a tcp_pcb for
+ * most other segment output functions.
+ *
+ * @param seqno the sequence number to use for the outgoing segment
+ * @param ackno the acknowledge number to use for the outgoing segment
+ * @param local_ip the local IP address to send the segment from
+ * @param remote_ip the remote IP address to send the segment to
+ * @param local_port the local TCP port to send the segment from
+ * @param remote_port the remote TCP port to send the segment to
+ */
+void
+tcp_rst(u32_t seqno, u32_t ackno,
+ ip_addr_t *local_ip, ip_addr_t *remote_ip,
+ u16_t local_port, u16_t remote_port)
+{
+ struct pbuf *p;
+ struct tcp_hdr *tcphdr;
+ p = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM);
+ if (p == NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_rst: could not allocate memory for pbuf\n"));
+ return;
+ }
+ LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr",
+ (p->len >= sizeof(struct tcp_hdr)));
+
+ tcphdr = (struct tcp_hdr *)p->payload;
+ tcphdr->src = htons(local_port);
+ tcphdr->dest = htons(remote_port);
+ tcphdr->seqno = htonl(seqno);
+ tcphdr->ackno = htonl(ackno);
+ TCPH_HDRLEN_FLAGS_SET(tcphdr, TCP_HLEN/4, TCP_RST | TCP_ACK);
+ tcphdr->wnd = PP_HTONS(TCP_WND);
+ tcphdr->chksum = 0;
+ tcphdr->urgp = 0;
+
+#if CHECKSUM_GEN_TCP
+ tcphdr->chksum = inet_chksum_pseudo(p, local_ip, remote_ip,
+ IP_PROTO_TCP, p->tot_len);
+#endif
+ TCP_STATS_INC(tcp.xmit);
+ snmp_inc_tcpoutrsts();
+ /* Send output with hardcoded TTL since we have no access to the pcb */
+ ip_output(p, local_ip, remote_ip, TCP_TTL, 0, IP_PROTO_TCP);
+ pbuf_free(p);
+ LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_rst: seqno %"U32_F" ackno %"U32_F".\n", seqno, ackno));
+}
+
+/**
+ * Requeue all unacked segments for retransmission
+ *
+ * Called by tcp_slowtmr() for slow retransmission.
+ *
+ * @param pcb the tcp_pcb for which to re-enqueue all unacked segments
+ */
+void
+tcp_rexmit_rto(struct tcp_pcb *pcb)
+{
+ struct tcp_seg *seg;
+
+ if (pcb->unacked == NULL) {
+ return;
+ }
+
+ /* Move all unacked segments to the head of the unsent queue */
+ for (seg = pcb->unacked; seg->next != NULL; seg = seg->next);
+ /* concatenate unsent queue after unacked queue */
+ seg->next = pcb->unsent;
+ /* unsent queue is the concatenated queue (of unacked, unsent) */
+ pcb->unsent = pcb->unacked;
+ /* unacked queue is now empty */
+ pcb->unacked = NULL;
+
+ /* increment number of retransmissions */
+ ++pcb->nrtx;
+
+ /* Don't take any RTT measurements after retransmitting. */
+ pcb->rttest = 0;
+
+ /* Do the actual retransmission */
+ tcp_output(pcb);
+}
+
+/**
+ * Requeue the first unacked segment for retransmission
+ *
+ * Called by tcp_receive() for fast retramsmit.
+ *
+ * @param pcb the tcp_pcb for which to retransmit the first unacked segment
+ */
+void
+tcp_rexmit(struct tcp_pcb *pcb)
+{
+ struct tcp_seg *seg;
+ struct tcp_seg **cur_seg;
+
+ if (pcb->unacked == NULL) {
+ return;
+ }
+
+ /* Move the first unacked segment to the unsent queue */
+ /* Keep the unsent queue sorted. */
+ seg = pcb->unacked;
+ pcb->unacked = seg->next;
+
+ cur_seg = &(pcb->unsent);
+ while (*cur_seg &&
+ TCP_SEQ_LT(ntohl((*cur_seg)->tcphdr->seqno), ntohl(seg->tcphdr->seqno))) {
+ cur_seg = &((*cur_seg)->next );
+ }
+ seg->next = *cur_seg;
+ *cur_seg = seg;
+
+ ++pcb->nrtx;
+
+ /* Don't take any rtt measurements after retransmitting. */
+ pcb->rttest = 0;
+
+ /* Do the actual retransmission. */
+ snmp_inc_tcpretranssegs();
+ /* No need to call tcp_output: we are always called from tcp_input()
+ and thus tcp_output directly returns. */
+}
+
+
+/**
+ * Handle retransmission after three dupacks received
+ *
+ * @param pcb the tcp_pcb for which to retransmit the first unacked segment
+ */
+void
+tcp_rexmit_fast(struct tcp_pcb *pcb)
+{
+ if (pcb->unacked != NULL && !(pcb->flags & TF_INFR)) {
+ /* This is fast retransmit. Retransmit the first unacked segment. */
+ LWIP_DEBUGF(TCP_FR_DEBUG,
+ ("tcp_receive: dupacks %"U16_F" (%"U32_F
+ "), fast retransmit %"U32_F"\n",
+ (u16_t)pcb->dupacks, pcb->lastack,
+ ntohl(pcb->unacked->tcphdr->seqno)));
+ tcp_rexmit(pcb);
+
+ /* Set ssthresh to half of the minimum of the current
+ * cwnd and the advertised window */
+ if (pcb->cwnd > pcb->snd_wnd) {
+ pcb->ssthresh = pcb->snd_wnd / 2;
+ } else {
+ pcb->ssthresh = pcb->cwnd / 2;
+ }
+
+ /* The minimum value for ssthresh should be 2 MSS */
+ if (pcb->ssthresh < 2*pcb->mss) {
+ LWIP_DEBUGF(TCP_FR_DEBUG,
+ ("tcp_receive: The minimum value for ssthresh %"U16_F
+ " should be min 2 mss %"U16_F"...\n",
+ pcb->ssthresh, 2*pcb->mss));
+ pcb->ssthresh = 2*pcb->mss;
+ }
+
+ pcb->cwnd = pcb->ssthresh + 3 * pcb->mss;
+ pcb->flags |= TF_INFR;
+ }
+}
+
+
+/**
+ * Send keepalive packets to keep a connection active although
+ * no data is sent over it.
+ *
+ * Called by tcp_slowtmr()
+ *
+ * @param pcb the tcp_pcb for which to send a keepalive packet
+ */
+void
+tcp_keepalive(struct tcp_pcb *pcb)
+{
+ struct pbuf *p;
+ struct tcp_hdr *tcphdr;
+
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: sending KEEPALIVE probe to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(&pcb->remote_ip), ip4_addr2_16(&pcb->remote_ip),
+ ip4_addr3_16(&pcb->remote_ip), ip4_addr4_16(&pcb->remote_ip)));
+
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: tcp_ticks %"U32_F" pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n",
+ tcp_ticks, pcb->tmr, pcb->keep_cnt_sent));
+
+ p = tcp_output_alloc_header(pcb, 0, 0, htonl(pcb->snd_nxt - 1));
+ if(p == NULL) {
+ LWIP_DEBUGF(TCP_DEBUG,
+ ("tcp_keepalive: could not allocate memory for pbuf\n"));
+ return;
+ }
+ tcphdr = (struct tcp_hdr *)p->payload;
+
+#if CHECKSUM_GEN_TCP
+ tcphdr->chksum = inet_chksum_pseudo(p, &pcb->local_ip, &pcb->remote_ip,
+ IP_PROTO_TCP, p->tot_len);
+#endif
+ TCP_STATS_INC(tcp.xmit);
+
+ /* Send output to IP */
+#if LWIP_NETIF_HWADDRHINT
+ ip_output_hinted(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP,
+ &(pcb->addr_hint));
+#else /* LWIP_NETIF_HWADDRHINT*/
+ ip_output(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP);
+#endif /* LWIP_NETIF_HWADDRHINT*/
+
+ pbuf_free(p);
+
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: seqno %"U32_F" ackno %"U32_F".\n",
+ pcb->snd_nxt - 1, pcb->rcv_nxt));
+}
+
+
+/**
+ * Send persist timer zero-window probes to keep a connection active
+ * when a window update is lost.
+ *
+ * Called by tcp_slowtmr()
+ *
+ * @param pcb the tcp_pcb for which to send a zero-window probe packet
+ */
+void
+tcp_zero_window_probe(struct tcp_pcb *pcb)
+{
+ struct pbuf *p;
+ struct tcp_hdr *tcphdr;
+ struct tcp_seg *seg;
+ u16_t len;
+ u8_t is_fin;
+
+ LWIP_DEBUGF(TCP_DEBUG,
+ ("tcp_zero_window_probe: sending ZERO WINDOW probe to %"
+ U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(&pcb->remote_ip), ip4_addr2_16(&pcb->remote_ip),
+ ip4_addr3_16(&pcb->remote_ip), ip4_addr4_16(&pcb->remote_ip)));
+
+ LWIP_DEBUGF(TCP_DEBUG,
+ ("tcp_zero_window_probe: tcp_ticks %"U32_F
+ " pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n",
+ tcp_ticks, pcb->tmr, pcb->keep_cnt_sent));
+
+ seg = pcb->unacked;
+
+ if(seg == NULL) {
+ seg = pcb->unsent;
+ }
+ if(seg == NULL) {
+ return;
+ }
+
+ is_fin = ((TCPH_FLAGS(seg->tcphdr) & TCP_FIN) != 0) && (seg->len == 0);
+ /* we want to send one seqno: either FIN or data (no options) */
+ len = is_fin ? 0 : 1;
+
+ p = tcp_output_alloc_header(pcb, 0, len, seg->tcphdr->seqno);
+ if(p == NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: no memory for pbuf\n"));
+ return;
+ }
+ tcphdr = (struct tcp_hdr *)p->payload;
+
+ if (is_fin) {
+ /* FIN segment, no data */
+ TCPH_FLAGS_SET(tcphdr, TCP_ACK | TCP_FIN);
+ } else {
+ /* Data segment, copy in one byte from the head of the unacked queue */
+ struct tcp_hdr *thdr = (struct tcp_hdr *)seg->p->payload;
+ char *d = ((char *)p->payload + TCP_HLEN);
+ pbuf_copy_partial(seg->p, d, 1, TCPH_HDRLEN(thdr) * 4);
+ }
+
+#if CHECKSUM_GEN_TCP
+ tcphdr->chksum = inet_chksum_pseudo(p, &pcb->local_ip, &pcb->remote_ip,
+ IP_PROTO_TCP, p->tot_len);
+#endif
+ TCP_STATS_INC(tcp.xmit);
+
+ /* Send output to IP */
+#if LWIP_NETIF_HWADDRHINT
+ ip_output_hinted(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP,
+ &(pcb->addr_hint));
+#else /* LWIP_NETIF_HWADDRHINT*/
+ ip_output(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP);
+#endif /* LWIP_NETIF_HWADDRHINT*/
+
+ pbuf_free(p);
+
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: seqno %"U32_F
+ " ackno %"U32_F".\n",
+ pcb->snd_nxt - 1, pcb->rcv_nxt));
+}
+#endif /* LWIP_TCP */
diff --git a/app/lwip/core/timers.c b/app/lwip/core/timers.c
new file mode 100644
index 00000000..b240024a
--- /dev/null
+++ b/app/lwip/core/timers.c
@@ -0,0 +1,509 @@
+/**
+ * @file
+ * Stack-internal timers implementation.
+ * This file includes timer callbacks for stack-internal timers as well as
+ * functions to set up or stop timers and check for expired timers.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ * Simon Goldschmidt
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/timers.h"
+#include "lwip/tcp_impl.h"
+
+#if LWIP_TIMERS
+
+#include "lwip/def.h"
+#include "lwip/memp.h"
+#include "lwip/tcpip.h"
+
+#include "lwip/ip_frag.h"
+#include "netif/etharp.h"
+#include "lwip/dhcp.h"
+#include "lwip/autoip.h"
+#include "lwip/igmp.h"
+#include "lwip/dns.h"
+
+
+/** The one and only timeout list */
+static struct sys_timeo *next_timeout = NULL;
+#if NO_SYS
+static u32_t timeouts_last_time;
+#endif /* NO_SYS */
+
+#if LWIP_TCP
+/** global variable that shows if the tcp timer is currently scheduled or not */
+static int tcpip_tcp_timer_active;
+
+/**
+ * Timer callback function that calls tcp_tmr() and reschedules itself.
+ *
+ * @param arg unused argument
+ */
+static void ICACHE_FLASH_ATTR
+tcpip_tcp_timer(void *arg)
+{
+ LWIP_UNUSED_ARG(arg);
+
+ /* call TCP timer handler */
+ tcp_tmr();
+ /* timer still needed? */
+ if (tcp_active_pcbs || tcp_tw_pcbs) {
+ /* restart timer */
+ sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL);
+ } else {
+ /* disable timer */
+ tcpip_tcp_timer_active = 0;
+ }
+}
+
+/**
+ * Called from TCP_REG when registering a new PCB:
+ * the reason is to have the TCP timer only running when
+ * there are active (or time-wait) PCBs.
+ */
+void
+tcp_timer_needed(void)
+{
+ /* timer is off but needed again? */
+ if (!tcpip_tcp_timer_active && (tcp_active_pcbs || tcp_tw_pcbs)) {
+ /* enable and start timer */
+ tcpip_tcp_timer_active = 1;
+ sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL);
+ }
+}
+
+/**
+ * Timer callback function that calls tcp_tmr() and reschedules itself.
+ *
+ * @param arg unused argument
+ */
+
+static void ICACHE_FLASH_ATTR
+tcp_timer_coarse(void *arg)
+{
+ LWIP_UNUSED_ARG(arg);
+ LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: tcp_tmr()\n"));
+ tcp_tmr();
+ sys_timeout(TCP_TMR_INTERVAL, tcp_timer_coarse, NULL);
+}
+
+#endif /* LWIP_TCP */
+
+#if IP_REASSEMBLY
+/**
+ * Timer callback function that calls ip_reass_tmr() and reschedules itself.
+ *
+ * @param arg unused argument
+ */
+static void ICACHE_FLASH_ATTR
+ip_reass_timer(void *arg)
+{
+ LWIP_UNUSED_ARG(arg);
+ LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: ip_reass_tmr()\n"));
+ ip_reass_tmr();
+ sys_timeout(IP_TMR_INTERVAL, ip_reass_timer, NULL);
+}
+#endif /* IP_REASSEMBLY */
+
+#if LWIP_ARP
+/**
+ * Timer callback function that calls etharp_tmr() and reschedules itself.
+ *
+ * @param arg unused argument
+ */
+static void ICACHE_FLASH_ATTR
+arp_timer(void *arg)
+{
+ LWIP_UNUSED_ARG(arg);
+ LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: etharp_tmr()\n"));
+ etharp_tmr();
+ sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL);
+}
+#endif /* LWIP_ARP */
+
+#if LWIP_DHCP
+/**
+ * Timer callback function that calls dhcp_coarse_tmr() and reschedules itself.
+ *
+ * @param arg unused argument
+ */
+extern void dhcps_coarse_tmr(void);
+static void
+dhcp_timer_coarse(void *arg)
+{
+ LWIP_UNUSED_ARG(arg);
+ LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dhcp_coarse_tmr()\n"));
+ dhcp_coarse_tmr();
+ dhcps_coarse_tmr();
+ sys_timeout(DHCP_COARSE_TIMER_MSECS, dhcp_timer_coarse, NULL);
+}
+
+/**
+ * Timer callback function that calls dhcp_fine_tmr() and reschedules itself.
+ *
+ * @param arg unused argument
+ */
+static void
+dhcp_timer_fine(void *arg)
+{
+ LWIP_UNUSED_ARG(arg);
+ LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dhcp_fine_tmr()\n"));
+ dhcp_fine_tmr();
+ sys_timeout(DHCP_FINE_TIMER_MSECS, dhcp_timer_fine, NULL);
+}
+#endif /* LWIP_DHCP */
+
+#if LWIP_AUTOIP
+/**
+ * Timer callback function that calls autoip_tmr() and reschedules itself.
+ *
+ * @param arg unused argument
+ */
+static void
+autoip_timer(void *arg)
+{
+ LWIP_UNUSED_ARG(arg);
+ LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: autoip_tmr()\n"));
+ autoip_tmr();
+ sys_timeout(AUTOIP_TMR_INTERVAL, autoip_timer, NULL);
+}
+#endif /* LWIP_AUTOIP */
+
+#if LWIP_IGMP
+/**
+ * Timer callback function that calls igmp_tmr() and reschedules itself.
+ *
+ * @param arg unused argument
+ */
+static void
+igmp_timer(void *arg)
+{
+ LWIP_UNUSED_ARG(arg);
+ LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: igmp_tmr()\n"));
+ igmp_tmr();
+ sys_timeout(IGMP_TMR_INTERVAL, igmp_timer, NULL);
+}
+#endif /* LWIP_IGMP */
+
+#if LWIP_DNS
+/**
+ * Timer callback function that calls dns_tmr() and reschedules itself.
+ *
+ * @param arg unused argument
+ */
+static void
+dns_timer(void *arg)
+{
+ LWIP_UNUSED_ARG(arg);
+ LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dns_tmr()\n"));
+ dns_tmr();
+ sys_timeout(DNS_TMR_INTERVAL, dns_timer, NULL);
+}
+#endif /* LWIP_DNS */
+
+/** Initialize this module */
+void sys_timeouts_init(void)
+{
+#if IP_REASSEMBLY
+ sys_timeout(IP_TMR_INTERVAL, ip_reass_timer, NULL);
+#endif /* IP_REASSEMBLY */
+#if LWIP_ARP
+ sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL);
+#endif /* LWIP_ARP */
+#if LWIP_DHCP
+ sys_timeout(DHCP_COARSE_TIMER_MSECS, dhcp_timer_coarse, NULL);
+ sys_timeout(DHCP_FINE_TIMER_MSECS, dhcp_timer_fine, NULL);
+#endif /* LWIP_DHCP */
+#if LWIP_AUTOIP
+ sys_timeout(AUTOIP_TMR_INTERVAL, autoip_timer, NULL);
+#endif /* LWIP_AUTOIP */
+#if LWIP_IGMP
+ sys_timeout(IGMP_TMR_INTERVAL, igmp_timer, NULL);
+#endif /* LWIP_IGMP */
+#if LWIP_DNS
+ sys_timeout(DNS_TMR_INTERVAL, dns_timer, NULL);
+#endif /* LWIP_DNS */
+
+#if LWIP_TCP
+ //sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL);
+ sys_timeout(TCP_TMR_INTERVAL, tcp_timer_coarse, NULL);
+#endif
+
+#if NO_SYS
+ /* Initialise timestamp for sys_check_timeouts */
+ timeouts_last_time = NOW();
+#endif
+}
+
+/**
+ * Create a one-shot timer (aka timeout). Timeouts are processed in the
+ * following cases:
+ * - while waiting for a message using sys_timeouts_mbox_fetch()
+ * - by calling sys_check_timeouts() (NO_SYS==1 only)
+ *
+ * @param msecs time in milliseconds after that the timer should expire
+ * @param handler callback function to call when msecs have elapsed
+ * @param arg argument to pass to the callback function
+ */
+#if LWIP_DEBUG_TIMERNAMES
+void
+sys_timeout_debug(u32_t msecs, sys_timeout_handler handler, void *arg, const char* handler_name)
+#else /* LWIP_DEBUG_TIMERNAMES */
+void
+sys_timeout(u32_t msecs, sys_timeout_handler handler, void *arg)
+#endif /* LWIP_DEBUG_TIMERNAMES */
+{
+ struct sys_timeo *timeout, *t;
+
+ timeout = (struct sys_timeo *)memp_malloc(MEMP_SYS_TIMEOUT);
+ if (timeout == NULL) {
+ LWIP_ASSERT("sys_timeout: timeout != NULL, pool MEMP_SYS_TIMEOUT is empty", timeout != NULL);
+ return;
+ }
+ timeout->next = NULL;
+ timeout->h = handler;
+ timeout->arg = arg;
+ timeout->time = msecs;
+#if LWIP_DEBUG_TIMERNAMES
+ timeout->handler_name = handler_name;
+ LWIP_DEBUGF(TIMERS_DEBUG, ("sys_timeout: %p msecs=%"U32_F" handler=%s arg=%p\n",
+ (void *)timeout, msecs, handler_name, (void *)arg));
+#endif /* LWIP_DEBUG_TIMERNAMES */
+
+ if (next_timeout == NULL) {
+ next_timeout = timeout;
+ return;
+ }
+
+ if (next_timeout->time > msecs) {
+ next_timeout->time -= msecs;
+ timeout->next = next_timeout;
+ next_timeout = timeout;
+ } else {
+ for(t = next_timeout; t != NULL; t = t->next) {
+ timeout->time -= t->time;
+ if (t->next == NULL || t->next->time > timeout->time) {
+ if (t->next != NULL) {
+ t->next->time -= timeout->time;
+ }
+ timeout->next = t->next;
+ t->next = timeout;
+ break;
+ }
+ }
+ }
+}
+
+/**
+ * Go through timeout list (for this task only) and remove the first matching
+ * entry, even though the timeout has not triggered yet.
+ *
+ * @note This function only works as expected if there is only one timeout
+ * calling 'handler' in the list of timeouts.
+ *
+ * @param handler callback function that would be called by the timeout
+ * @param arg callback argument that would be passed to handler
+*/
+void
+sys_untimeout(sys_timeout_handler handler, void *arg)
+{
+ struct sys_timeo *prev_t, *t;
+
+ if (next_timeout == NULL) {
+ return;
+ }
+
+ for (t = next_timeout, prev_t = NULL; t != NULL; prev_t = t, t = t->next) {
+ if ((t->h == handler) && (t->arg == arg)) {
+ /* We have a match */
+ /* Unlink from previous in list */
+ if (prev_t == NULL) {
+ next_timeout = t->next;
+ } else {
+ prev_t->next = t->next;
+ }
+ /* If not the last one, add time of this one back to next */
+ if (t->next != NULL) {
+ t->next->time += t->time;
+ }
+ memp_free(MEMP_SYS_TIMEOUT, t);
+ return;
+ }
+ }
+ return;
+}
+
+#if NO_SYS
+extern uint8 timer2_ms_flag;
+/** Handle timeouts for NO_SYS==1 (i.e. without using
+ * tcpip_thread/sys_timeouts_mbox_fetch(). Uses sys_now() to call timeout
+ * handler functions when timeouts expire.
+ *
+ * Must be called periodically from your main loop.
+ */
+void
+sys_check_timeouts(void)
+{
+ struct sys_timeo *tmptimeout;
+ u32_t diff;
+ sys_timeout_handler handler;
+ void *arg;
+ int had_one;
+ u32_t now;
+
+ now = NOW();
+ if (next_timeout) {
+ /* this cares for wraparounds */
+ if (timer2_ms_flag == 0) {
+ diff = LWIP_U32_DIFF(now, timeouts_last_time)/((CPU_CLK_FREQ>>4)/1000);
+ } else {
+ diff = LWIP_U32_DIFF(now, timeouts_last_time)/((CPU_CLK_FREQ>>8)/1000);
+ }
+ do
+ {
+ had_one = 0;
+ tmptimeout = next_timeout;
+ if (tmptimeout->time <= diff) {
+ /* timeout has expired */
+ had_one = 1;
+ timeouts_last_time = now;
+ diff -= tmptimeout->time;
+ next_timeout = tmptimeout->next;
+ handler = tmptimeout->h;
+ arg = tmptimeout->arg;
+#if LWIP_DEBUG_TIMERNAMES
+ if (handler != NULL) {
+ LWIP_DEBUGF(TIMERS_DEBUG, ("sct calling h=%s arg=%p\n",
+ tmptimeout->handler_name, arg));
+ }
+#endif /* LWIP_DEBUG_TIMERNAMES */
+ memp_free(MEMP_SYS_TIMEOUT, tmptimeout);
+ if (handler != NULL) {
+ handler(arg);
+ }
+ }
+ /* repeat until all expired timers have been called */
+ }while(had_one);
+ }
+}
+
+/** Set back the timestamp of the last call to sys_check_timeouts()
+ * This is necessary if sys_check_timeouts() hasn't been called for a long
+ * time (e.g. while saving energy) to prevent all timer functions of that
+ * period being called.
+ */
+void
+sys_restart_timeouts(void)
+{
+ timeouts_last_time = NOW();
+}
+
+#else /* NO_SYS */
+
+/**
+ * Wait (forever) for a message to arrive in an mbox.
+ * While waiting, timeouts are processed.
+ *
+ * @param mbox the mbox to fetch the message from
+ * @param msg the place to store the message
+ */
+void
+sys_timeouts_mbox_fetch(sys_mbox_t *mbox, void **msg)
+{
+ u32_t time_needed;
+ struct sys_timeo *tmptimeout;
+ sys_timeout_handler handler;
+ void *arg;
+
+ again:
+ if (!next_timeout) {
+ time_needed = sys_arch_mbox_fetch(mbox, msg, 0);
+ } else {
+ if (next_timeout->time > 0) {
+ time_needed = sys_arch_mbox_fetch(mbox, msg, next_timeout->time);
+ } else {
+ time_needed = SYS_ARCH_TIMEOUT;
+ }
+
+ if (time_needed == SYS_ARCH_TIMEOUT) {
+ /* If time == SYS_ARCH_TIMEOUT, a timeout occured before a message
+ could be fetched. We should now call the timeout handler and
+ deallocate the memory allocated for the timeout. */
+ tmptimeout = next_timeout;
+ next_timeout = tmptimeout->next;
+ handler = tmptimeout->h;
+ arg = tmptimeout->arg;
+#if LWIP_DEBUG_TIMERNAMES
+ if (handler != NULL) {
+ LWIP_DEBUGF(TIMERS_DEBUG, ("stmf calling h=%s arg=%p\n",
+ tmptimeout->handler_name, arg));
+ }
+#endif /* LWIP_DEBUG_TIMERNAMES */
+ memp_free(MEMP_SYS_TIMEOUT, tmptimeout);
+ if (handler != NULL) {
+ /* For LWIP_TCPIP_CORE_LOCKING, lock the core before calling the
+ timeout handler function. */
+ LOCK_TCPIP_CORE();
+ handler(arg);
+ UNLOCK_TCPIP_CORE();
+ }
+ LWIP_TCPIP_THREAD_ALIVE();
+
+ /* We try again to fetch a message from the mbox. */
+ goto again;
+ } else {
+ /* If time != SYS_ARCH_TIMEOUT, a message was received before the timeout
+ occured. The time variable is set to the number of
+ milliseconds we waited for the message. */
+ if (time_needed < next_timeout->time) {
+ next_timeout->time -= time_needed;
+ } else {
+ next_timeout->time = 0;
+ }
+ }
+ }
+}
+
+#endif /* NO_SYS */
+
+#else /* LWIP_TIMERS */
+/* Satisfy the TCP code which calls this function */
+void
+tcp_timer_needed(void)
+{
+}
+#endif /* LWIP_TIMERS */
diff --git a/app/lwip/core/udp.c b/app/lwip/core/udp.c
new file mode 100644
index 00000000..83386cc0
--- /dev/null
+++ b/app/lwip/core/udp.c
@@ -0,0 +1,964 @@
+/**
+ * @file
+ * User Datagram Protocol module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels
+ *
+ */
+
+
+/* udp.c
+ *
+ * The code for the User Datagram Protocol UDP & UDPLite (RFC 3828).
+ *
+ */
+
+/* @todo Check the use of '(struct udp_pcb).chksum_len_rx'!
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_UDP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/udp.h"
+#include "lwip/def.h"
+#include "lwip/memp.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/icmp.h"
+#include "lwip/stats.h"
+#include "lwip/snmp.h"
+#include "arch/perf.h"
+#include "lwip/dhcp.h"
+
+#include
+
+/* The list of UDP PCBs */
+/* exported in udp.h (was static) */
+struct udp_pcb *udp_pcbs;
+
+/**
+ * Process an incoming UDP datagram.
+ *
+ * Given an incoming UDP datagram (as a chain of pbufs) this function
+ * finds a corresponding UDP PCB and hands over the pbuf to the pcbs
+ * recv function. If no pcb is found or the datagram is incorrect, the
+ * pbuf is freed.
+ *
+ * @param p pbuf to be demultiplexed to a UDP PCB.
+ * @param inp network interface on which the datagram was received.
+ *
+ */
+void ICACHE_FLASH_ATTR
+udp_input(struct pbuf *p, struct netif *inp)
+{
+ struct udp_hdr *udphdr;
+ struct udp_pcb *pcb, *prev;
+ struct udp_pcb *uncon_pcb;
+ struct ip_hdr *iphdr;
+ u16_t src, dest;
+ u8_t local_match;
+ u8_t broadcast;
+
+ PERF_START;
+
+ UDP_STATS_INC(udp.recv);
+
+ iphdr = (struct ip_hdr *)p->payload;
+
+ /* Check minimum length (IP header + UDP header)
+ * and move payload pointer to UDP header */
+ if (p->tot_len < (IPH_HL(iphdr) * 4 + UDP_HLEN) || pbuf_header(p, -(s16_t)(IPH_HL(iphdr) * 4))) {
+ /* drop short packets */
+ LWIP_DEBUGF(UDP_DEBUG,
+ ("udp_input: short UDP datagram (%"U16_F" bytes) discarded\n", p->tot_len));
+ UDP_STATS_INC(udp.lenerr);
+ UDP_STATS_INC(udp.drop);
+ snmp_inc_udpinerrors();
+ pbuf_free(p);
+ goto end;
+ }
+
+ udphdr = (struct udp_hdr *)p->payload;
+
+ /* is broadcast packet ? */
+ broadcast = ip_addr_isbroadcast(¤t_iphdr_dest, inp);
+
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_input: received datagram of length %"U16_F"\n", p->tot_len));
+
+ /* convert src and dest ports to host byte order */
+ src = ntohs(udphdr->src);
+ dest = ntohs(udphdr->dest);
+
+ udp_debug_print(udphdr);
+
+ /* print the UDP source and destination */
+ LWIP_DEBUGF(UDP_DEBUG,
+ ("udp (%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F") <-- "
+ "(%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F")\n",
+ ip4_addr1_16(&iphdr->dest), ip4_addr2_16(&iphdr->dest),
+ ip4_addr3_16(&iphdr->dest), ip4_addr4_16(&iphdr->dest), ntohs(udphdr->dest),
+ ip4_addr1_16(&iphdr->src), ip4_addr2_16(&iphdr->src),
+ ip4_addr3_16(&iphdr->src), ip4_addr4_16(&iphdr->src), ntohs(udphdr->src)));
+
+#if LWIP_DHCP
+ pcb = NULL;
+ /* when LWIP_DHCP is active, packets to DHCP_CLIENT_PORT may only be processed by
+ the dhcp module, no other UDP pcb may use the local UDP port DHCP_CLIENT_PORT */
+ if (dest == DHCP_CLIENT_PORT) {
+ /* all packets for DHCP_CLIENT_PORT not coming from DHCP_SERVER_PORT are dropped! */
+ if (src == DHCP_SERVER_PORT) {
+ if ((inp->dhcp != NULL) && (inp->dhcp->pcb != NULL)) {
+ /* accept the packe if
+ (- broadcast or directed to us) -> DHCP is link-layer-addressed, local ip is always ANY!
+ - inp->dhcp->pcb->remote == ANY or iphdr->src */
+ if ((ip_addr_isany(&inp->dhcp->pcb->remote_ip) ||
+ ip_addr_cmp(&(inp->dhcp->pcb->remote_ip), ¤t_iphdr_src))) {
+ pcb = inp->dhcp->pcb;
+ }
+ }
+ }
+ } else
+#endif /* LWIP_DHCP */
+ {
+ prev = NULL;
+ local_match = 0;
+ uncon_pcb = NULL;
+ /* Iterate through the UDP pcb list for a matching pcb.
+ * 'Perfect match' pcbs (connected to the remote port & ip address) are
+ * preferred. If no perfect match is found, the first unconnected pcb that
+ * matches the local port and ip address gets the datagram. */
+ for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) {
+ local_match = 0;
+ /* print the PCB local and remote address */
+ LWIP_DEBUGF(UDP_DEBUG,
+ ("pcb (%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F") --- "
+ "(%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F")\n",
+ ip4_addr1_16(&pcb->local_ip), ip4_addr2_16(&pcb->local_ip),
+ ip4_addr3_16(&pcb->local_ip), ip4_addr4_16(&pcb->local_ip), pcb->local_port,
+ ip4_addr1_16(&pcb->remote_ip), ip4_addr2_16(&pcb->remote_ip),
+ ip4_addr3_16(&pcb->remote_ip), ip4_addr4_16(&pcb->remote_ip), pcb->remote_port));
+
+ /* compare PCB local addr+port to UDP destination addr+port */
+ if ((pcb->local_port == dest) &&
+ ((!broadcast && ip_addr_isany(&pcb->local_ip)) ||
+ ip_addr_cmp(&(pcb->local_ip), ¤t_iphdr_dest) ||
+#if LWIP_IGMP
+ ip_addr_ismulticast(¤t_iphdr_dest) ||
+#endif /* LWIP_IGMP */
+#if IP_SOF_BROADCAST_RECV
+ (broadcast && (pcb->so_options & SOF_BROADCAST)))) {
+#else /* IP_SOF_BROADCAST_RECV */
+ (broadcast))) {
+#endif /* IP_SOF_BROADCAST_RECV */
+ local_match = 1;
+ if ((uncon_pcb == NULL) &&
+ ((pcb->flags & UDP_FLAGS_CONNECTED) == 0)) {
+ /* the first unconnected matching PCB */
+ uncon_pcb = pcb;
+ }
+ }
+ /* compare PCB remote addr+port to UDP source addr+port */
+ if ((local_match != 0) &&
+ (pcb->remote_port == src) &&
+ (ip_addr_isany(&pcb->remote_ip) ||
+ ip_addr_cmp(&(pcb->remote_ip), ¤t_iphdr_src))) {
+ /* the first fully matching PCB */
+ if (prev != NULL) {
+ /* move the pcb to the front of udp_pcbs so that is
+ found faster next time */
+ prev->next = pcb->next;
+ pcb->next = udp_pcbs;
+ udp_pcbs = pcb;
+ } else {
+ UDP_STATS_INC(udp.cachehit);
+ }
+ break;
+ }
+ prev = pcb;
+ }
+ /* no fully matching pcb found? then look for an unconnected pcb */
+ if (pcb == NULL) {
+ pcb = uncon_pcb;
+ }
+ }
+
+ /* Check checksum if this is a match or if it was directed at us. */
+ if (pcb != NULL || ip_addr_cmp(&inp->ip_addr, ¤t_iphdr_dest)) {
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: calculating checksum\n"));
+#if LWIP_UDPLITE
+ if (IPH_PROTO(iphdr) == IP_PROTO_UDPLITE) {
+ /* Do the UDP Lite checksum */
+#if CHECKSUM_CHECK_UDP
+ u16_t chklen = ntohs(udphdr->len);
+ if (chklen < sizeof(struct udp_hdr)) {
+ if (chklen == 0) {
+ /* For UDP-Lite, checksum length of 0 means checksum
+ over the complete packet (See RFC 3828 chap. 3.1) */
+ chklen = p->tot_len;
+ } else {
+ /* At least the UDP-Lite header must be covered by the
+ checksum! (Again, see RFC 3828 chap. 3.1) */
+ UDP_STATS_INC(udp.chkerr);
+ UDP_STATS_INC(udp.drop);
+ snmp_inc_udpinerrors();
+ pbuf_free(p);
+ goto end;
+ }
+ }
+ if (inet_chksum_pseudo_partial(p, ¤t_iphdr_src, ¤t_iphdr_dest,
+ IP_PROTO_UDPLITE, p->tot_len, chklen) != 0) {
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("udp_input: UDP Lite datagram discarded due to failing checksum\n"));
+ UDP_STATS_INC(udp.chkerr);
+ UDP_STATS_INC(udp.drop);
+ snmp_inc_udpinerrors();
+ pbuf_free(p);
+ goto end;
+ }
+#endif /* CHECKSUM_CHECK_UDP */
+ } else
+#endif /* LWIP_UDPLITE */
+ {
+#if CHECKSUM_CHECK_UDP
+ if (udphdr->chksum != 0) {
+ if (inet_chksum_pseudo(p, ip_current_src_addr(), ip_current_dest_addr(),
+ IP_PROTO_UDP, p->tot_len) != 0) {
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("udp_input: UDP datagram discarded due to failing checksum\n"));
+ UDP_STATS_INC(udp.chkerr);
+ UDP_STATS_INC(udp.drop);
+ snmp_inc_udpinerrors();
+ pbuf_free(p);
+ goto end;
+ }
+ }
+#endif /* CHECKSUM_CHECK_UDP */
+ }
+ if(pbuf_header(p, -UDP_HLEN)) {
+ /* Can we cope with this failing? Just assert for now */
+ LWIP_ASSERT("pbuf_header failed\n", 0);
+ UDP_STATS_INC(udp.drop);
+ snmp_inc_udpinerrors();
+ pbuf_free(p);
+ goto end;
+ }
+ if (pcb != NULL) {
+ snmp_inc_udpindatagrams();
+#if SO_REUSE && SO_REUSE_RXTOALL
+ if ((broadcast || ip_addr_ismulticast(¤t_iphdr_dest)) &&
+ ((pcb->so_options & SOF_REUSEADDR) != 0)) {
+ /* pass broadcast- or multicast packets to all multicast pcbs
+ if SOF_REUSEADDR is set on the first match */
+ struct udp_pcb *mpcb;
+ u8_t p_header_changed = 0;
+ for (mpcb = udp_pcbs; mpcb != NULL; mpcb = mpcb->next) {
+ if (mpcb != pcb) {
+ /* compare PCB local addr+port to UDP destination addr+port */
+ if ((mpcb->local_port == dest) &&
+ ((!broadcast && ip_addr_isany(&mpcb->local_ip)) ||
+ ip_addr_cmp(&(mpcb->local_ip), ¤t_iphdr_dest) ||
+#if LWIP_IGMP
+ ip_addr_ismulticast(¤t_iphdr_dest) ||
+#endif /* LWIP_IGMP */
+#if IP_SOF_BROADCAST_RECV
+ (broadcast && (mpcb->so_options & SOF_BROADCAST)))) {
+#else /* IP_SOF_BROADCAST_RECV */
+ (broadcast))) {
+#endif /* IP_SOF_BROADCAST_RECV */
+ /* pass a copy of the packet to all local matches */
+ if (mpcb->recv != NULL) {
+ struct pbuf *q;
+ /* for that, move payload to IP header again */
+ if (p_header_changed == 0) {
+ pbuf_header(p, (s16_t)((IPH_HL(iphdr) * 4) + UDP_HLEN));
+ p_header_changed = 1;
+ }
+ q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
+ if (q != NULL) {
+ err_t err = pbuf_copy(q, p);
+ if (err == ERR_OK) {
+ /* move payload to UDP data */
+ pbuf_header(q, -(s16_t)((IPH_HL(iphdr) * 4) + UDP_HLEN));
+ mpcb->recv(mpcb->recv_arg, mpcb, q, ip_current_src_addr(), src);
+ }
+ }
+ }
+ }
+ }
+ }
+ if (p_header_changed) {
+ /* and move payload to UDP data again */
+ pbuf_header(p, -(s16_t)((IPH_HL(iphdr) * 4) + UDP_HLEN));
+ }
+ }
+#endif /* SO_REUSE && SO_REUSE_RXTOALL */
+ /* callback */
+ if (pcb->recv != NULL) {
+ /* now the recv function is responsible for freeing p */
+ pcb->recv(pcb->recv_arg, pcb, p, ip_current_src_addr(), src);
+ } else {
+ /* no recv function registered? then we have to free the pbuf! */
+ pbuf_free(p);
+ goto end;
+ }
+ } else {
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: not for us.\n"));
+
+#if LWIP_ICMP
+ /* No match was found, send ICMP destination port unreachable unless
+ destination address was broadcast/multicast. */
+ if (!broadcast &&
+ !ip_addr_ismulticast(¤t_iphdr_dest)) {
+ /* move payload pointer back to ip header */
+ pbuf_header(p, (IPH_HL(iphdr) * 4) + UDP_HLEN);
+ LWIP_ASSERT("p->payload == iphdr", (p->payload == iphdr));
+ icmp_dest_unreach(p, ICMP_DUR_PORT);
+ }
+#endif /* LWIP_ICMP */
+ UDP_STATS_INC(udp.proterr);
+ UDP_STATS_INC(udp.drop);
+ snmp_inc_udpnoports();
+ pbuf_free(p);
+ }
+ } else {
+ pbuf_free(p);
+ }
+end:
+ PERF_STOP("udp_input");
+}
+
+/**
+ * Send data using UDP.
+ *
+ * @param pcb UDP PCB used to send the data.
+ * @param p chain of pbuf's to be sent.
+ *
+ * The datagram will be sent to the current remote_ip & remote_port
+ * stored in pcb. If the pcb is not bound to a port, it will
+ * automatically be bound to a random port.
+ *
+ * @return lwIP error code.
+ * - ERR_OK. Successful. No error occured.
+ * - ERR_MEM. Out of memory.
+ * - ERR_RTE. Could not find route to destination address.
+ * - More errors could be returned by lower protocol layers.
+ *
+ * @see udp_disconnect() udp_sendto()
+ */
+err_t ICACHE_FLASH_ATTR
+udp_send(struct udp_pcb *pcb, struct pbuf *p)
+{
+ /* send to the packet using remote ip and port stored in the pcb */
+ return udp_sendto(pcb, p, &pcb->remote_ip, pcb->remote_port);
+}
+
+#if LWIP_CHECKSUM_ON_COPY
+/** Same as udp_send() but with checksum
+ */
+err_t ICACHE_FLASH_ATTR
+udp_send_chksum(struct udp_pcb *pcb, struct pbuf *p,
+ u8_t have_chksum, u16_t chksum)
+{
+ /* send to the packet using remote ip and port stored in the pcb */
+ return udp_sendto_chksum(pcb, p, &pcb->remote_ip, pcb->remote_port,
+ have_chksum, chksum);
+}
+#endif /* LWIP_CHECKSUM_ON_COPY */
+
+/**
+ * Send data to a specified address using UDP.
+ *
+ * @param pcb UDP PCB used to send the data.
+ * @param p chain of pbuf's to be sent.
+ * @param dst_ip Destination IP address.
+ * @param dst_port Destination UDP port.
+ *
+ * dst_ip & dst_port are expected to be in the same byte order as in the pcb.
+ *
+ * If the PCB already has a remote address association, it will
+ * be restored after the data is sent.
+ *
+ * @return lwIP error code (@see udp_send for possible error codes)
+ *
+ * @see udp_disconnect() udp_send()
+ */
+err_t ICACHE_FLASH_ATTR
+udp_sendto(struct udp_pcb *pcb, struct pbuf *p,
+ ip_addr_t *dst_ip, u16_t dst_port)
+{
+#if LWIP_CHECKSUM_ON_COPY
+ return udp_sendto_chksum(pcb, p, dst_ip, dst_port, 0, 0);
+}
+
+/** Same as udp_sendto(), but with checksum */
+err_t ICACHE_FLASH_ATTR
+udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip,
+ u16_t dst_port, u8_t have_chksum, u16_t chksum)
+{
+#endif /* LWIP_CHECKSUM_ON_COPY */
+ struct netif *netif;
+
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send\n"));
+
+ /* find the outgoing network interface for this packet */
+#if LWIP_IGMP
+ netif = ip_route((ip_addr_ismulticast(dst_ip))?(&(pcb->multicast_ip)):(dst_ip));
+#else
+ netif = ip_route(dst_ip);
+#endif /* LWIP_IGMP */
+
+ /* no outgoing network interface could be found? */
+ if (netif == NULL) {
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(dst_ip), ip4_addr2_16(dst_ip), ip4_addr3_16(dst_ip), ip4_addr4_16(dst_ip)));
+ UDP_STATS_INC(udp.rterr);
+ return ERR_RTE;
+ }
+#if LWIP_CHECKSUM_ON_COPY
+ return udp_sendto_if_chksum(pcb, p, dst_ip, dst_port, netif, have_chksum, chksum);
+#else /* LWIP_CHECKSUM_ON_COPY */
+ return udp_sendto_if(pcb, p, dst_ip, dst_port, netif);
+#endif /* LWIP_CHECKSUM_ON_COPY */
+}
+
+/**
+ * Send data to a specified address using UDP.
+ * The netif used for sending can be specified.
+ *
+ * This function exists mainly for DHCP, to be able to send UDP packets
+ * on a netif that is still down.
+ *
+ * @param pcb UDP PCB used to send the data.
+ * @param p chain of pbuf's to be sent.
+ * @param dst_ip Destination IP address.
+ * @param dst_port Destination UDP port.
+ * @param netif the netif used for sending.
+ *
+ * dst_ip & dst_port are expected to be in the same byte order as in the pcb.
+ *
+ * @return lwIP error code (@see udp_send for possible error codes)
+ *
+ * @see udp_disconnect() udp_send()
+ */
+err_t ICACHE_FLASH_ATTR
+udp_sendto_if(struct udp_pcb *pcb, struct pbuf *p,
+ ip_addr_t *dst_ip, u16_t dst_port, struct netif *netif)
+{
+#if LWIP_CHECKSUM_ON_COPY
+ return udp_sendto_if_chksum(pcb, p, dst_ip, dst_port, netif, 0, 0);
+}
+
+/** Same as udp_sendto_if(), but with checksum */
+err_t ICACHE_FLASH_ATTR
+udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip,
+ u16_t dst_port, struct netif *netif, u8_t have_chksum,
+ u16_t chksum)
+{
+#endif /* LWIP_CHECKSUM_ON_COPY */
+ struct udp_hdr *udphdr;
+ ip_addr_t *src_ip;
+ err_t err;
+ struct pbuf *q; /* q will be sent down the stack */
+
+#if IP_SOF_BROADCAST
+ /* broadcast filter? */
+ if ( ((pcb->so_options & SOF_BROADCAST) == 0) && ip_addr_isbroadcast(dst_ip, netif) ) {
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("udp_sendto_if: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb));
+ return ERR_VAL;
+ }
+#endif /* IP_SOF_BROADCAST */
+
+ /* if the PCB is not yet bound to a port, bind it here */
+ if (pcb->local_port == 0) {
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send: not yet bound to a port, binding now\n"));
+ err = udp_bind(pcb, &pcb->local_ip, pcb->local_port);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: forced port bind failed\n"));
+ return err;
+ }
+ }
+
+ /* not enough space to add an UDP header to first pbuf in given p chain? */
+ if (pbuf_header(p, UDP_HLEN)) {
+ /* allocate header in a separate new pbuf */
+ q = pbuf_alloc(PBUF_IP, UDP_HLEN, PBUF_RAM);
+ /* new header pbuf could not be allocated? */
+ if (q == NULL) {
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: could not allocate header\n"));
+ return ERR_MEM;
+ }
+ if (p->tot_len != 0) {
+ /* chain header q in front of given pbuf p (only if p contains data) */
+ pbuf_chain(q, p);
+ }
+ /* first pbuf q points to header pbuf */
+ LWIP_DEBUGF(UDP_DEBUG,
+ ("udp_send: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p));
+ } else {
+ /* adding space for header within p succeeded */
+ /* first pbuf q equals given pbuf */
+ q = p;
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: added header in given pbuf %p\n", (void *)p));
+ }
+ LWIP_ASSERT("check that first pbuf can hold struct udp_hdr",
+ (q->len >= sizeof(struct udp_hdr)));
+ /* q now represents the packet to be sent */
+ udphdr = (struct udp_hdr *)q->payload;
+ udphdr->src = htons(pcb->local_port);
+ udphdr->dest = htons(dst_port);
+ /* in UDP, 0 checksum means 'no checksum' */
+ udphdr->chksum = 0x0000;
+
+ /* Multicast Loop? */
+#if LWIP_IGMP
+ if (ip_addr_ismulticast(dst_ip) && ((pcb->flags & UDP_FLAGS_MULTICAST_LOOP) != 0)) {
+ q->flags |= PBUF_FLAG_MCASTLOOP;
+ }
+#endif /* LWIP_IGMP */
+
+
+ /* PCB local address is IP_ANY_ADDR? */
+ if (ip_addr_isany(&pcb->local_ip)) {
+ /* use outgoing network interface IP address as source address */
+ src_ip = &(netif->ip_addr);
+ } else {
+ /* check if UDP PCB local IP address is correct
+ * this could be an old address if netif->ip_addr has changed */
+ if (!ip_addr_cmp(&(pcb->local_ip), &(netif->ip_addr))) {
+ /* local_ip doesn't match, drop the packet */
+ if (q != p) {
+ /* free the header pbuf */
+ pbuf_free(q);
+ q = NULL;
+ /* p is still referenced by the caller, and will live on */
+ }
+ return ERR_VAL;
+ }
+ /* use UDP PCB local IP address as source address */
+ src_ip = &(pcb->local_ip);
+ }
+
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: sending datagram of length %"U16_F"\n", q->tot_len));
+
+#if LWIP_UDPLITE
+ /* UDP Lite protocol? */
+ if (pcb->flags & UDP_FLAGS_UDPLITE) {
+ u16_t chklen, chklen_hdr;
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE packet length %"U16_F"\n", q->tot_len));
+ /* set UDP message length in UDP header */
+ chklen_hdr = chklen = pcb->chksum_len_tx;
+ if ((chklen < sizeof(struct udp_hdr)) || (chklen > q->tot_len)) {
+ if (chklen != 0) {
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE pcb->chksum_len is illegal: %"U16_F"\n", chklen));
+ }
+ /* For UDP-Lite, checksum length of 0 means checksum
+ over the complete packet. (See RFC 3828 chap. 3.1)
+ At least the UDP-Lite header must be covered by the
+ checksum, therefore, if chksum_len has an illegal
+ value, we generate the checksum over the complete
+ packet to be safe. */
+ chklen_hdr = 0;
+ chklen = q->tot_len;
+ }
+ udphdr->len = htons(chklen_hdr);
+ /* calculate checksum */
+#if CHECKSUM_GEN_UDP
+ udphdr->chksum = inet_chksum_pseudo_partial(q, src_ip, dst_ip,
+ IP_PROTO_UDPLITE, q->tot_len,
+#if !LWIP_CHECKSUM_ON_COPY
+ chklen);
+#else /* !LWIP_CHECKSUM_ON_COPY */
+ (have_chksum ? UDP_HLEN : chklen));
+ if (have_chksum) {
+ u32_t acc;
+ acc = udphdr->chksum + (u16_t)~(chksum);
+ udphdr->chksum = FOLD_U32T(acc);
+ }
+#endif /* !LWIP_CHECKSUM_ON_COPY */
+
+ /* chksum zero must become 0xffff, as zero means 'no checksum' */
+ if (udphdr->chksum == 0x0000) {
+ udphdr->chksum = 0xffff;
+ }
+#endif /* CHECKSUM_GEN_UDP */
+ /* output to IP */
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: ip_output_if (,,,,IP_PROTO_UDPLITE,)\n"));
+#if LWIP_NETIF_HWADDRHINT
+ netif->addr_hint = &(pcb->addr_hint);
+#endif /* LWIP_NETIF_HWADDRHINT*/
+ err = ip_output_if(q, src_ip, dst_ip, pcb->ttl, pcb->tos, IP_PROTO_UDPLITE, netif);
+#if LWIP_NETIF_HWADDRHINT
+ netif->addr_hint = NULL;
+#endif /* LWIP_NETIF_HWADDRHINT*/
+ } else
+#endif /* LWIP_UDPLITE */
+ { /* UDP */
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP packet length %"U16_F"\n", q->tot_len));
+ udphdr->len = htons(q->tot_len);
+ /* calculate checksum */
+#if CHECKSUM_GEN_UDP
+ if ((pcb->flags & UDP_FLAGS_NOCHKSUM) == 0) {
+ u16_t udpchksum;
+#if LWIP_CHECKSUM_ON_COPY
+ if (have_chksum) {
+ u32_t acc;
+ udpchksum = inet_chksum_pseudo_partial(q, src_ip, dst_ip, IP_PROTO_UDP,
+ q->tot_len, UDP_HLEN);
+ acc = udpchksum + (u16_t)~(chksum);
+ udpchksum = FOLD_U32T(acc);
+ } else
+#endif /* LWIP_CHECKSUM_ON_COPY */
+ {
+ udpchksum = inet_chksum_pseudo(q, src_ip, dst_ip, IP_PROTO_UDP, q->tot_len);
+ }
+
+ /* chksum zero must become 0xffff, as zero means 'no checksum' */
+ if (udpchksum == 0x0000) {
+ udpchksum = 0xffff;
+ }
+ udphdr->chksum = udpchksum;
+ }
+#endif /* CHECKSUM_GEN_UDP */
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP checksum 0x%04"X16_F"\n", udphdr->chksum));
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: ip_output_if (,,,,IP_PROTO_UDP,)\n"));
+ /* output to IP */
+#if LWIP_NETIF_HWADDRHINT
+ netif->addr_hint = &(pcb->addr_hint);
+#endif /* LWIP_NETIF_HWADDRHINT*/
+ err = ip_output_if(q, src_ip, dst_ip, pcb->ttl, pcb->tos, IP_PROTO_UDP, netif);
+#if LWIP_NETIF_HWADDRHINT
+ netif->addr_hint = NULL;
+#endif /* LWIP_NETIF_HWADDRHINT*/
+ }
+ /* TODO: must this be increased even if error occured? */
+ snmp_inc_udpoutdatagrams();
+
+ /* did we chain a separate header pbuf earlier? */
+ if (q != p) {
+ /* free the header pbuf */
+ pbuf_free(q);
+ q = NULL;
+ /* p is still referenced by the caller, and will live on */
+ }
+
+ UDP_STATS_INC(udp.xmit);
+ return err;
+}
+
+/**
+ * Bind an UDP PCB.
+ *
+ * @param pcb UDP PCB to be bound with a local address ipaddr and port.
+ * @param ipaddr local IP address to bind with. Use IP_ADDR_ANY to
+ * bind to all local interfaces.
+ * @param port local UDP port to bind with. Use 0 to automatically bind
+ * to a random port between UDP_LOCAL_PORT_RANGE_START and
+ * UDP_LOCAL_PORT_RANGE_END.
+ *
+ * ipaddr & port are expected to be in the same byte order as in the pcb.
+ *
+ * @return lwIP error code.
+ * - ERR_OK. Successful. No error occured.
+ * - ERR_USE. The specified ipaddr and port are already bound to by
+ * another UDP PCB.
+ *
+ * @see udp_disconnect()
+ */
+err_t ICACHE_FLASH_ATTR
+udp_bind(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port)
+{
+ struct udp_pcb *ipcb;
+ u8_t rebind;
+
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_bind(ipaddr = "));
+ ip_addr_debug_print(UDP_DEBUG, ipaddr);
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, (", port = %"U16_F")\n", port));
+
+ rebind = 0;
+ /* Check for double bind and rebind of the same pcb */
+ for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) {
+ /* is this UDP PCB already on active list? */
+ if (pcb == ipcb) {
+ /* pcb may occur at most once in active list */
+ LWIP_ASSERT("rebind == 0", rebind == 0);
+ /* pcb already in list, just rebind */
+ rebind = 1;
+ }
+
+ /* By default, we don't allow to bind to a port that any other udp
+ PCB is alread bound to, unless *all* PCBs with that port have tha
+ REUSEADDR flag set. */
+#if SO_REUSE
+ else if (((pcb->so_options & SOF_REUSEADDR) == 0) &&
+ ((ipcb->so_options & SOF_REUSEADDR) == 0)) {
+#else /* SO_REUSE */
+ /* port matches that of PCB in list and REUSEADDR not set -> reject */
+ else {
+#endif /* SO_REUSE */
+ if ((ipcb->local_port == port) &&
+ /* IP address matches, or one is IP_ADDR_ANY? */
+ (ip_addr_isany(&(ipcb->local_ip)) ||
+ ip_addr_isany(ipaddr) ||
+ ip_addr_cmp(&(ipcb->local_ip), ipaddr))) {
+ /* other PCB already binds to this local IP and port */
+ LWIP_DEBUGF(UDP_DEBUG,
+ ("udp_bind: local port %"U16_F" already bound by another pcb\n", port));
+ return ERR_USE;
+ }
+ }
+ }
+
+ ip_addr_set(&pcb->local_ip, ipaddr);
+
+ /* no port specified? */
+ if (port == 0) {
+#ifndef UDP_LOCAL_PORT_RANGE_START
+#define UDP_LOCAL_PORT_RANGE_START 4096
+#define UDP_LOCAL_PORT_RANGE_END 0x7fff
+#endif
+ port = UDP_LOCAL_PORT_RANGE_START;
+ ipcb = udp_pcbs;
+ while ((ipcb != NULL) && (port != UDP_LOCAL_PORT_RANGE_END)) {
+ if (ipcb->local_port == port) {
+ /* port is already used by another udp_pcb */
+ port++;
+ /* restart scanning all udp pcbs */
+ ipcb = udp_pcbs;
+ } else {
+ /* go on with next udp pcb */
+ ipcb = ipcb->next;
+ }
+ }
+ if (ipcb != NULL) {
+ /* no more ports available in local range */
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_bind: out of free UDP ports\n"));
+ return ERR_USE;
+ }
+ }
+ pcb->local_port = port;
+ snmp_insert_udpidx_tree(pcb);
+ /* pcb not active yet? */
+ if (rebind == 0) {
+ /* place the PCB on the active list if not already there */
+ pcb->next = udp_pcbs;
+ udp_pcbs = pcb;
+ }
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("udp_bind: bound to %"U16_F".%"U16_F".%"U16_F".%"U16_F", port %"U16_F"\n",
+ ip4_addr1_16(&pcb->local_ip), ip4_addr2_16(&pcb->local_ip),
+ ip4_addr3_16(&pcb->local_ip), ip4_addr4_16(&pcb->local_ip),
+ pcb->local_port));
+ return ERR_OK;
+}
+/**
+ * Connect an UDP PCB.
+ *
+ * This will associate the UDP PCB with the remote address.
+ *
+ * @param pcb UDP PCB to be connected with remote address ipaddr and port.
+ * @param ipaddr remote IP address to connect with.
+ * @param port remote UDP port to connect with.
+ *
+ * @return lwIP error code
+ *
+ * ipaddr & port are expected to be in the same byte order as in the pcb.
+ *
+ * The udp pcb is bound to a random local port if not already bound.
+ *
+ * @see udp_disconnect()
+ */
+err_t ICACHE_FLASH_ATTR
+udp_connect(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port)
+{
+ struct udp_pcb *ipcb;
+
+ if (pcb->local_port == 0) {
+ err_t err = udp_bind(pcb, &pcb->local_ip, pcb->local_port);
+ if (err != ERR_OK) {
+ return err;
+ }
+ }
+
+ ip_addr_set(&pcb->remote_ip, ipaddr);
+ pcb->remote_port = port;
+ pcb->flags |= UDP_FLAGS_CONNECTED;
+/** TODO: this functionality belongs in upper layers */
+#ifdef LWIP_UDP_TODO
+ /* Nail down local IP for netconn_addr()/getsockname() */
+ if (ip_addr_isany(&pcb->local_ip) && !ip_addr_isany(&pcb->remote_ip)) {
+ struct netif *netif;
+
+ if ((netif = ip_route(&(pcb->remote_ip))) == NULL) {
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_connect: No route to 0x%lx\n", pcb->remote_ip.addr));
+ UDP_STATS_INC(udp.rterr);
+ return ERR_RTE;
+ }
+ /** TODO: this will bind the udp pcb locally, to the interface which
+ is used to route output packets to the remote address. However, we
+ might want to accept incoming packets on any interface! */
+ pcb->local_ip = netif->ip_addr;
+ } else if (ip_addr_isany(&pcb->remote_ip)) {
+ pcb->local_ip.addr = 0;
+ }
+#endif
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("udp_connect: connected to %"U16_F".%"U16_F".%"U16_F".%"U16_F",port %"U16_F"\n",
+ ip4_addr1_16(&pcb->local_ip), ip4_addr2_16(&pcb->local_ip),
+ ip4_addr3_16(&pcb->local_ip), ip4_addr4_16(&pcb->local_ip),
+ pcb->local_port));
+
+ /* Insert UDP PCB into the list of active UDP PCBs. */
+ for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) {
+ if (pcb == ipcb) {
+ /* already on the list, just return */
+ return ERR_OK;
+ }
+ }
+ /* PCB not yet on the list, add PCB now */
+ pcb->next = udp_pcbs;
+ udp_pcbs = pcb;
+ return ERR_OK;
+}
+
+/**
+ * Disconnect a UDP PCB
+ *
+ * @param pcb the udp pcb to disconnect.
+ */
+void ICACHE_FLASH_ATTR
+udp_disconnect(struct udp_pcb *pcb)
+{
+ /* reset remote address association */
+ ip_addr_set_any(&pcb->remote_ip);
+ pcb->remote_port = 0;
+ /* mark PCB as unconnected */
+ pcb->flags &= ~UDP_FLAGS_CONNECTED;
+}
+
+/**
+ * Set a receive callback for a UDP PCB
+ *
+ * This callback will be called when receiving a datagram for the pcb.
+ *
+ * @param pcb the pcb for wich to set the recv callback
+ * @param recv function pointer of the callback function
+ * @param recv_arg additional argument to pass to the callback function
+ */
+void ICACHE_FLASH_ATTR
+udp_recv(struct udp_pcb *pcb, udp_recv_fn recv, void *recv_arg)
+{
+ /* remember recv() callback and user data */
+ pcb->recv = recv;
+ pcb->recv_arg = recv_arg;
+}
+
+/**
+ * Remove an UDP PCB.
+ *
+ * @param pcb UDP PCB to be removed. The PCB is removed from the list of
+ * UDP PCB's and the data structure is freed from memory.
+ *
+ * @see udp_new()
+ */
+void ICACHE_FLASH_ATTR
+udp_remove(struct udp_pcb *pcb)
+{
+ struct udp_pcb *pcb2;
+
+ snmp_delete_udpidx_tree(pcb);
+ /* pcb to be removed is first in list? */
+ if (udp_pcbs == pcb) {
+ /* make list start at 2nd pcb */
+ udp_pcbs = udp_pcbs->next;
+ /* pcb not 1st in list */
+ } else {
+ for (pcb2 = udp_pcbs; pcb2 != NULL; pcb2 = pcb2->next) {
+ /* find pcb in udp_pcbs list */
+ if (pcb2->next != NULL && pcb2->next == pcb) {
+ /* remove pcb from list */
+ pcb2->next = pcb->next;
+ }
+ }
+ }
+ memp_free(MEMP_UDP_PCB, pcb);
+}
+
+/**
+ * Create a UDP PCB.
+ *
+ * @return The UDP PCB which was created. NULL if the PCB data structure
+ * could not be allocated.
+ *
+ * @see udp_remove()
+ */
+struct udp_pcb * ICACHE_FLASH_ATTR
+udp_new(void)
+{
+ struct udp_pcb *pcb;
+ pcb = (struct udp_pcb *)memp_malloc(MEMP_UDP_PCB);
+ /* could allocate UDP PCB? */
+ if (pcb != NULL) {
+ /* UDP Lite: by initializing to all zeroes, chksum_len is set to 0
+ * which means checksum is generated over the whole datagram per default
+ * (recommended as default by RFC 3828). */
+ /* initialize PCB to all zeroes */
+ memset(pcb, 0, sizeof(struct udp_pcb));
+ pcb->ttl = UDP_TTL;
+ }
+ return pcb;
+}
+
+#if UDP_DEBUG
+/**
+ * Print UDP header information for debug purposes.
+ *
+ * @param udphdr pointer to the udp header in memory.
+ */
+void ICACHE_FLASH_ATTR
+udp_debug_print(struct udp_hdr *udphdr)
+{
+ LWIP_DEBUGF(UDP_DEBUG, ("UDP header:\n"));
+ LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(UDP_DEBUG, ("| %5"U16_F" | %5"U16_F" | (src port, dest port)\n",
+ ntohs(udphdr->src), ntohs(udphdr->dest)));
+ LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(UDP_DEBUG, ("| %5"U16_F" | 0x%04"X16_F" | (len, chksum)\n",
+ ntohs(udphdr->len), ntohs(udphdr->chksum)));
+ LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n"));
+}
+#endif /* UDP_DEBUG */
+
+#endif /* LWIP_UDP */
diff --git a/app/lwip/netif/Makefile b/app/lwip/netif/Makefile
new file mode 100644
index 00000000..9e5e810a
--- /dev/null
+++ b/app/lwip/netif/Makefile
@@ -0,0 +1,46 @@
+
+#############################################################
+# Required variables for each makefile
+# Discard this section from all parent makefiles
+# Expected variables (with automatic defaults):
+# CSRCS (all "C" files in the dir)
+# SUBDIRS (all subdirs with a Makefile)
+# GEN_LIBS - list of libs to be generated ()
+# GEN_IMAGES - list of images to be generated ()
+# COMPONENTS_xxx - a list of libs/objs in the form
+# subdir/lib to be extracted and rolled up into
+# a generated lib/image xxx.a ()
+#
+ifndef PDIR
+
+GEN_LIBS = liblwipnetif.a
+
+endif
+
+
+#############################################################
+# Configuration i.e. compile options etc.
+# Target specific stuff (defines etc.) goes in here!
+# Generally values applying to a tree are captured in the
+# makefile at its root level - these are then overridden
+# for a subtree within the makefile rooted therein
+#
+#DEFINES +=
+
+#############################################################
+# Recursion Magic - Don't touch this!!
+#
+# Each subtree potentially has an include directory
+# corresponding to the common APIs applicable to modules
+# rooted at that subtree. Accordingly, the INCLUDE PATH
+# of a module can only contain the include directories up
+# its parent path, and not its siblings
+#
+# Required for each makefile to inherit from the parent
+#
+
+INCLUDES := $(INCLUDES) -I $(PDIR)include
+INCLUDES += -I ./
+PDIR := ../$(PDIR)
+sinclude $(PDIR)Makefile
+
diff --git a/app/lwip/netif/etharp.c b/app/lwip/netif/etharp.c
new file mode 100644
index 00000000..efc0979d
--- /dev/null
+++ b/app/lwip/netif/etharp.c
@@ -0,0 +1,1357 @@
+/**
+ * @file
+ * Address Resolution Protocol module for IP over Ethernet
+ *
+ * Functionally, ARP is divided into two parts. The first maps an IP address
+ * to a physical address when sending a packet, and the second part answers
+ * requests from other machines for our physical address.
+ *
+ * This implementation complies with RFC 826 (Ethernet ARP). It supports
+ * Gratuitious ARP from RFC3220 (IP Mobility Support for IPv4) section 4.6
+ * if an interface calls etharp_gratuitous(our_netif) upon address change.
+ */
+
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * Copyright (c) 2003-2004 Leon Woestenberg
+ * Copyright (c) 2003-2004 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_ARP || LWIP_ETHERNET
+
+#include "lwip/ip_addr.h"
+#include "lwip/def.h"
+#include "lwip/ip.h"
+#include "lwip/stats.h"
+#include "lwip/snmp.h"
+#include "lwip/dhcp.h"
+#include "lwip/autoip.h"
+#include "netif/etharp.h"
+
+#if PPPOE_SUPPORT
+#include "netif/ppp_oe.h"
+#endif /* PPPOE_SUPPORT */
+
+#include
+
+const struct eth_addr ethbroadcast = {{0xff,0xff,0xff,0xff,0xff,0xff}};
+const struct eth_addr ethzero = {{0,0,0,0,0,0}};
+
+#if LWIP_ARP /* don't build if not configured for use in lwipopts.h */
+
+/** the time an ARP entry stays valid after its last update,
+ * for ARP_TMR_INTERVAL = 5000, this is
+ * (240 * 5) seconds = 20 minutes.
+ */
+#define ARP_MAXAGE 240
+/** the time an ARP entry stays pending after first request,
+ * for ARP_TMR_INTERVAL = 5000, this is
+ * (2 * 5) seconds = 10 seconds.
+ *
+ * @internal Keep this number at least 2, otherwise it might
+ * run out instantly if the timeout occurs directly after a request.
+ */
+#define ARP_MAXPENDING 2
+
+#define HWTYPE_ETHERNET 1
+
+enum etharp_state {
+ ETHARP_STATE_EMPTY = 0,
+ ETHARP_STATE_PENDING,
+ ETHARP_STATE_STABLE
+};
+
+struct etharp_entry {
+#if ARP_QUEUEING
+ /** Pointer to queue of pending outgoing packets on this ARP entry. */
+ struct etharp_q_entry *q;
+#else /* ARP_QUEUEING */
+ /** Pointer to a single pending outgoing packet on this ARP entry. */
+ struct pbuf *q;
+#endif /* ARP_QUEUEING */
+ ip_addr_t ipaddr;
+ struct eth_addr ethaddr;
+#if LWIP_SNMP || LWIP_ARP
+ struct netif *netif;
+#endif /* LWIP_SNMP */
+ u8_t state;
+ u8_t ctime;
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+ u8_t static_entry;
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+};
+
+static struct etharp_entry arp_table[ARP_TABLE_SIZE];
+
+#if !LWIP_NETIF_HWADDRHINT
+static u8_t etharp_cached_entry;
+#endif /* !LWIP_NETIF_HWADDRHINT */
+
+/** Try hard to create a new entry - we want the IP address to appear in
+ the cache (even if this means removing an active entry or so). */
+#define ETHARP_FLAG_TRY_HARD 1
+#define ETHARP_FLAG_FIND_ONLY 2
+#define ETHARP_FLAG_STATIC_ENTRY 4
+
+#if LWIP_NETIF_HWADDRHINT
+#define ETHARP_SET_HINT(netif, hint) if (((netif) != NULL) && ((netif)->addr_hint != NULL)) \
+ *((netif)->addr_hint) = (hint);
+#else /* LWIP_NETIF_HWADDRHINT */
+#define ETHARP_SET_HINT(netif, hint) (etharp_cached_entry = (hint))
+#endif /* LWIP_NETIF_HWADDRHINT */
+
+static err_t update_arp_entry(struct netif *netif, ip_addr_t *ipaddr, struct eth_addr *ethaddr, u8_t flags);
+
+
+/* Some checks, instead of etharp_init(): */
+#if (LWIP_ARP && (ARP_TABLE_SIZE > 0x7f))
+ #error "ARP_TABLE_SIZE must fit in an s8_t, you have to reduce it in your lwipopts.h"
+#endif
+
+
+#if ARP_QUEUEING
+/**
+ * Free a complete queue of etharp entries
+ *
+ * @param q a qeueue of etharp_q_entry's to free
+ */
+static void
+free_etharp_q(struct etharp_q_entry *q)
+{
+ struct etharp_q_entry *r;
+ LWIP_ASSERT("q != NULL", q != NULL);
+ LWIP_ASSERT("q->p != NULL", q->p != NULL);
+ while (q) {
+ r = q;
+ q = q->next;
+ LWIP_ASSERT("r->p != NULL", (r->p != NULL));
+ pbuf_free(r->p);
+ memp_free(MEMP_ARP_QUEUE, r);
+ }
+}
+#else /* ARP_QUEUEING */
+
+/** Compatibility define: free the queued pbuf */
+#define free_etharp_q(q) pbuf_free(q)
+
+#endif /* ARP_QUEUEING */
+
+/** Clean up ARP table entries */
+static void ICACHE_FLASH_ATTR
+free_entry(int i)
+{
+ /* remove from SNMP ARP index tree */
+ snmp_delete_arpidx_tree(arp_table[i].netif, &arp_table[i].ipaddr);
+ /* and empty packet queue */
+ if (arp_table[i].q != NULL) {
+ /* remove all queued packets */
+ LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: freeing entry %"U16_F", packet queue %p.\n", (u16_t)i, (void *)(arp_table[i].q)));
+ free_etharp_q(arp_table[i].q);
+ arp_table[i].q = NULL;
+ }
+ /* recycle entry for re-use */
+ arp_table[i].state = ETHARP_STATE_EMPTY;
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+ arp_table[i].static_entry = 0;
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+#ifdef LWIP_DEBUG
+ /* for debugging, clean out the complete entry */
+ arp_table[i].ctime = 0;
+#if LWIP_SNMP
+ arp_table[i].netif = NULL;
+#endif /* LWIP_SNMP */
+ ip_addr_set_zero(&arp_table[i].ipaddr);
+ arp_table[i].ethaddr = ethzero;
+#endif /* LWIP_DEBUG */
+}
+
+/**
+ * Clears expired entries in the ARP table.
+ *
+ * This function should be called every ETHARP_TMR_INTERVAL milliseconds (5 seconds),
+ * in order to expire entries in the ARP table.
+ */
+void
+etharp_tmr(void)
+{
+ u8_t i;
+
+ LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer\n"));
+ /* remove expired entries from the ARP table */
+ for (i = 0; i < ARP_TABLE_SIZE; ++i) {
+ u8_t state = arp_table[i].state;
+ if (state != ETHARP_STATE_EMPTY
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+ && (arp_table[i].static_entry == 0)
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+ ) {
+ arp_table[i].ctime++;
+ if ((arp_table[i].ctime >= ARP_MAXAGE) ||
+ ((arp_table[i].state == ETHARP_STATE_PENDING) &&
+ (arp_table[i].ctime >= ARP_MAXPENDING))) {
+ /* pending or stable entry has become old! */
+ LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: expired %s entry %"U16_F".\n",
+ arp_table[i].state == ETHARP_STATE_STABLE ? "stable" : "pending", (u16_t)i));
+ /* clean up entries that have just been expired */
+ free_entry(i);
+ }
+#if ARP_QUEUEING
+ /* still pending entry? (not expired) */
+ if (arp_table[i].state == ETHARP_STATE_PENDING) {
+ /* resend an ARP query here? */
+ }
+#endif /* ARP_QUEUEING */
+ }
+ }
+}
+
+/**
+ * Search the ARP table for a matching or new entry.
+ *
+ * If an IP address is given, return a pending or stable ARP entry that matches
+ * the address. If no match is found, create a new entry with this address set,
+ * but in state ETHARP_EMPTY. The caller must check and possibly change the
+ * state of the returned entry.
+ *
+ * If ipaddr is NULL, return a initialized new entry in state ETHARP_EMPTY.
+ *
+ * In all cases, attempt to create new entries from an empty entry. If no
+ * empty entries are available and ETHARP_FLAG_TRY_HARD flag is set, recycle
+ * old entries. Heuristic choose the least important entry for recycling.
+ *
+ * @param ipaddr IP address to find in ARP cache, or to add if not found.
+ * @param flags @see definition of ETHARP_FLAG_*
+ * @param netif netif related to this address (used for NETIF_HWADDRHINT)
+ *
+ * @return The ARP entry index that matched or is created, ERR_MEM if no
+ * entry is found or could be recycled.
+ */
+static s8_t ICACHE_FLASH_ATTR
+find_entry(ip_addr_t *ipaddr, u8_t flags)
+{
+ s8_t old_pending = ARP_TABLE_SIZE, old_stable = ARP_TABLE_SIZE;
+ s8_t empty = ARP_TABLE_SIZE;
+ u8_t i = 0, age_pending = 0, age_stable = 0;
+ /* oldest entry with packets on queue */
+ s8_t old_queue = ARP_TABLE_SIZE;
+ /* its age */
+ u8_t age_queue = 0;
+
+ /**
+ * a) do a search through the cache, remember candidates
+ * b) select candidate entry
+ * c) create new entry
+ */
+
+ /* a) in a single search sweep, do all of this
+ * 1) remember the first empty entry (if any)
+ * 2) remember the oldest stable entry (if any)
+ * 3) remember the oldest pending entry without queued packets (if any)
+ * 4) remember the oldest pending entry with queued packets (if any)
+ * 5) search for a matching IP entry, either pending or stable
+ * until 5 matches, or all entries are searched for.
+ */
+
+ for (i = 0; i < ARP_TABLE_SIZE; ++i) {
+ u8_t state = arp_table[i].state;
+ /* no empty entry found yet and now we do find one? */
+ if ((empty == ARP_TABLE_SIZE) && (state == ETHARP_STATE_EMPTY)) {
+ LWIP_DEBUGF(ETHARP_DEBUG, ("find_entry: found empty entry %"U16_F"\n", (u16_t)i));
+ /* remember first empty entry */
+ empty = i;
+ } else if (state != ETHARP_STATE_EMPTY) {
+ LWIP_ASSERT("state == ETHARP_STATE_PENDING || state == ETHARP_STATE_STABLE",
+ state == ETHARP_STATE_PENDING || state == ETHARP_STATE_STABLE);
+ /* if given, does IP address match IP address in ARP entry? */
+ if (ipaddr && ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: found matching entry %"U16_F"\n", (u16_t)i));
+ /* found exact IP address match, simply bail out */
+ return i;
+ }
+ /* pending entry? */
+ if (state == ETHARP_STATE_PENDING) {
+ /* pending with queued packets? */
+ if (arp_table[i].q != NULL) {
+ if (arp_table[i].ctime >= age_queue) {
+ old_queue = i;
+ age_queue = arp_table[i].ctime;
+ }
+ } else
+ /* pending without queued packets? */
+ {
+ if (arp_table[i].ctime >= age_pending) {
+ old_pending = i;
+ age_pending = arp_table[i].ctime;
+ }
+ }
+ /* stable entry? */
+ } else if (state == ETHARP_STATE_STABLE) {
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+ /* don't record old_stable for static entries since they never expire */
+ if (arp_table[i].static_entry == 0)
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+ {
+ /* remember entry with oldest stable entry in oldest, its age in maxtime */
+ if (arp_table[i].ctime >= age_stable) {
+ old_stable = i;
+ age_stable = arp_table[i].ctime;
+ }
+ }
+ }
+ }
+ }
+ /* { we have no match } => try to create a new entry */
+
+ /* don't create new entry, only search? */
+ if (((flags & ETHARP_FLAG_FIND_ONLY) != 0) ||
+ /* or no empty entry found and not allowed to recycle? */
+ ((empty == ARP_TABLE_SIZE) && ((flags & ETHARP_FLAG_TRY_HARD) == 0))) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: no empty entry found and not allowed to recycle\n"));
+ return (s8_t)ERR_MEM;
+ }
+
+ /* b) choose the least destructive entry to recycle:
+ * 1) empty entry
+ * 2) oldest stable entry
+ * 3) oldest pending entry without queued packets
+ * 4) oldest pending entry with queued packets
+ *
+ * { ETHARP_FLAG_TRY_HARD is set at this point }
+ */
+
+ /* 1) empty entry available? */
+ if (empty < ARP_TABLE_SIZE) {
+ i = empty;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting empty entry %"U16_F"\n", (u16_t)i));
+ } else {
+ /* 2) found recyclable stable entry? */
+ if (old_stable < ARP_TABLE_SIZE) {
+ /* recycle oldest stable*/
+ i = old_stable;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting oldest stable entry %"U16_F"\n", (u16_t)i));
+ /* no queued packets should exist on stable entries */
+ LWIP_ASSERT("arp_table[i].q == NULL", arp_table[i].q == NULL);
+ /* 3) found recyclable pending entry without queued packets? */
+ } else if (old_pending < ARP_TABLE_SIZE) {
+ /* recycle oldest pending */
+ i = old_pending;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting oldest pending entry %"U16_F" (without queue)\n", (u16_t)i));
+ /* 4) found recyclable pending entry with queued packets? */
+ } else if (old_queue < ARP_TABLE_SIZE) {
+ /* recycle oldest pending (queued packets are free in free_entry) */
+ i = old_queue;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting oldest pending entry %"U16_F", freeing packet queue %p\n", (u16_t)i, (void *)(arp_table[i].q)));
+ /* no empty or recyclable entries found */
+ } else {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: no empty or recyclable entries found\n"));
+ return (s8_t)ERR_MEM;
+ }
+
+ /* { empty or recyclable entry found } */
+ LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE);
+ free_entry(i);
+ }
+
+ LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE);
+ LWIP_ASSERT("arp_table[i].state == ETHARP_STATE_EMPTY",
+ arp_table[i].state == ETHARP_STATE_EMPTY);
+
+ /* IP address given? */
+ if (ipaddr != NULL) {
+ /* set IP address */
+ ip_addr_copy(arp_table[i].ipaddr, *ipaddr);
+ }
+ arp_table[i].ctime = 0;
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+ arp_table[i].static_entry = 0;
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+ return (err_t)i;
+}
+
+/**
+ * Send an IP packet on the network using netif->linkoutput
+ * The ethernet header is filled in before sending.
+ *
+ * @params netif the lwIP network interface on which to send the packet
+ * @params p the packet to send, p->payload pointing to the (uninitialized) ethernet header
+ * @params src the source MAC address to be copied into the ethernet header
+ * @params dst the destination MAC address to be copied into the ethernet header
+ * @return ERR_OK if the packet was sent, any other err_t on failure
+ */
+static err_t ICACHE_FLASH_ATTR
+etharp_send_ip(struct netif *netif, struct pbuf *p, struct eth_addr *src, struct eth_addr *dst)
+{
+ struct eth_hdr *ethhdr = (struct eth_hdr *)p->payload;
+
+ LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!",
+ (netif->hwaddr_len == ETHARP_HWADDR_LEN));
+ ETHADDR32_COPY(ðhdr->dest, dst);
+ ETHADDR16_COPY(ðhdr->src, src);
+ ethhdr->type = PP_HTONS(ETHTYPE_IP);
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_send_ip: sending packet %p\n", (void *)p));
+ /* send the packet */
+ return netif->linkoutput(netif, p);
+}
+
+/**
+ * Update (or insert) a IP/MAC address pair in the ARP cache.
+ *
+ * If a pending entry is resolved, any queued packets will be sent
+ * at this point.
+ *
+ * @param netif netif related to this entry (used for NETIF_ADDRHINT)
+ * @param ipaddr IP address of the inserted ARP entry.
+ * @param ethaddr Ethernet address of the inserted ARP entry.
+ * @param flags @see definition of ETHARP_FLAG_*
+ *
+ * @return
+ * - ERR_OK Succesfully updated ARP cache.
+ * - ERR_MEM If we could not add a new ARP entry when ETHARP_FLAG_TRY_HARD was set.
+ * - ERR_ARG Non-unicast address given, those will not appear in ARP cache.
+ *
+ * @see pbuf_free()
+ */
+static err_t ICACHE_FLASH_ATTR
+update_arp_entry(struct netif *netif, ip_addr_t *ipaddr, struct eth_addr *ethaddr, u8_t flags)
+{
+ s8_t i;
+ LWIP_ASSERT("netif->hwaddr_len == ETHARP_HWADDR_LEN", netif->hwaddr_len == ETHARP_HWADDR_LEN);
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("update_arp_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n",
+ ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr),
+ ethaddr->addr[0], ethaddr->addr[1], ethaddr->addr[2],
+ ethaddr->addr[3], ethaddr->addr[4], ethaddr->addr[5]));
+ /* non-unicast address? */
+ if (ip_addr_isany(ipaddr) ||
+ ip_addr_isbroadcast(ipaddr, netif) ||
+ ip_addr_ismulticast(ipaddr)) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("update_arp_entry: will not add non-unicast IP address to ARP cache\n"));
+ return ERR_ARG;
+ }
+ /* find or create ARP entry */
+ i = find_entry(ipaddr, flags);
+ /* bail out if no entry could be found */
+ if (i < 0) {
+ return (err_t)i;
+ }
+
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+ if (flags & ETHARP_FLAG_STATIC_ENTRY) {
+ /* record static type */
+ arp_table[i].static_entry = 1;
+ }
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+
+ /* mark it stable */
+ arp_table[i].state = ETHARP_STATE_STABLE;
+
+#if LWIP_SNMP
+ /* record network interface */
+ arp_table[i].netif = netif;
+#endif /* LWIP_SNMP */
+ /* insert in SNMP ARP index tree */
+ snmp_insert_arpidx_tree(netif, &arp_table[i].ipaddr);
+
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("update_arp_entry: updating stable entry %"S16_F"\n", (s16_t)i));
+ /* update address */
+ ETHADDR32_COPY(&arp_table[i].ethaddr, ethaddr);
+ /* reset time stamp */
+ arp_table[i].ctime = 0;
+ /* this is where we will send out queued packets! */
+#if ARP_QUEUEING
+ while (arp_table[i].q != NULL) {
+ struct pbuf *p;
+ /* remember remainder of queue */
+ struct etharp_q_entry *q = arp_table[i].q;
+ /* pop first item off the queue */
+ arp_table[i].q = q->next;
+ /* get the packet pointer */
+ p = q->p;
+ /* now queue entry can be freed */
+ memp_free(MEMP_ARP_QUEUE, q);
+#else /* ARP_QUEUEING */
+ if (arp_table[i].q != NULL) {
+ struct pbuf *p = arp_table[i].q;
+ arp_table[i].q = NULL;
+#endif /* ARP_QUEUEING */
+ /* send the queued IP packet */
+ etharp_send_ip(netif, p, (struct eth_addr*)(netif->hwaddr), ethaddr);
+ /* free the queued IP packet */
+ pbuf_free(p);
+ }
+ return ERR_OK;
+}
+
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+/** Add a new static entry to the ARP table. If an entry exists for the
+ * specified IP address, this entry is overwritten.
+ * If packets are queued for the specified IP address, they are sent out.
+ *
+ * @param ipaddr IP address for the new static entry
+ * @param ethaddr ethernet address for the new static entry
+ * @return @see return values of etharp_add_static_entry
+ */
+err_t
+etharp_add_static_entry(ip_addr_t *ipaddr, struct eth_addr *ethaddr)
+{
+ struct netif *netif;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_add_static_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n",
+ ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr),
+ ethaddr->addr[0], ethaddr->addr[1], ethaddr->addr[2],
+ ethaddr->addr[3], ethaddr->addr[4], ethaddr->addr[5]));
+
+ netif = ip_route(ipaddr);
+ if (netif == NULL) {
+ return ERR_RTE;
+ }
+
+ return update_arp_entry(netif, ipaddr, ethaddr, ETHARP_FLAG_TRY_HARD | ETHARP_FLAG_STATIC_ENTRY);
+}
+
+/** Remove a static entry from the ARP table previously added with a call to
+ * etharp_add_static_entry.
+ *
+ * @param ipaddr IP address of the static entry to remove
+ * @return ERR_OK: entry removed
+ * ERR_MEM: entry wasn't found
+ * ERR_ARG: entry wasn't a static entry but a dynamic one
+ */
+err_t
+etharp_remove_static_entry(ip_addr_t *ipaddr)
+{
+ s8_t i;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_remove_static_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr)));
+
+ /* find or create ARP entry */
+ i = find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY);
+ /* bail out if no entry could be found */
+ if (i < 0) {
+ return (err_t)i;
+ }
+
+ if ((arp_table[i].state != ETHARP_STATE_STABLE) ||
+ (arp_table[i].static_entry == 0)) {
+ /* entry wasn't a static entry, cannot remove it */
+ return ERR_ARG;
+ }
+ /* entry found, free it */
+ free_entry(i);
+ return ERR_OK;
+}
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+
+/**
+ * Remove all ARP table entries of the specified netif.
+ *
+ * @param netif points to a network interface
+ */
+void ICACHE_FLASH_ATTR etharp_cleanup_netif(struct netif *netif)
+{
+ u8_t i;
+
+ for (i = 0; i < ARP_TABLE_SIZE; ++i) {
+ u8_t state = arp_table[i].state;
+ if ((state != ETHARP_STATE_EMPTY) && (arp_table[i].netif == netif)) {
+ free_entry(i);
+ }
+ }
+}
+
+/**
+ * Finds (stable) ethernet/IP address pair from ARP table
+ * using interface and IP address index.
+ * @note the addresses in the ARP table are in network order!
+ *
+ * @param netif points to interface index
+ * @param ipaddr points to the (network order) IP address index
+ * @param eth_ret points to return pointer
+ * @param ip_ret points to return pointer
+ * @return table index if found, -1 otherwise
+ */
+s8_t
+etharp_find_addr(struct netif *netif, ip_addr_t *ipaddr,
+ struct eth_addr **eth_ret, ip_addr_t **ip_ret)
+{
+ s8_t i;
+
+ LWIP_ASSERT("eth_ret != NULL && ip_ret != NULL",
+ eth_ret != NULL && ip_ret != NULL);
+
+ LWIP_UNUSED_ARG(netif);
+
+ i = find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY);
+ if((i >= 0) && arp_table[i].state == ETHARP_STATE_STABLE) {
+ *eth_ret = &arp_table[i].ethaddr;
+ *ip_ret = &arp_table[i].ipaddr;
+ return i;
+ }
+ return -1;
+}
+
+#if ETHARP_TRUST_IP_MAC
+/**
+ * Updates the ARP table using the given IP packet.
+ *
+ * Uses the incoming IP packet's source address to update the
+ * ARP cache for the local network. The function does not alter
+ * or free the packet. This function must be called before the
+ * packet p is passed to the IP layer.
+ *
+ * @param netif The lwIP network interface on which the IP packet pbuf arrived.
+ * @param p The IP packet that arrived on netif.
+ *
+ * @return NULL
+ *
+ * @see pbuf_free()
+ */
+static void ICACHE_FLASH_ATTR
+etharp_ip_input(struct netif *netif, struct pbuf *p)
+{
+ struct eth_hdr *ethhdr;
+ struct ip_hdr *iphdr;
+ ip_addr_t iphdr_src;
+ LWIP_ERROR("netif != NULL", (netif != NULL), return;);
+
+ /* Only insert an entry if the source IP address of the
+ incoming IP packet comes from a host on the local network. */
+ ethhdr = (struct eth_hdr *)p->payload;
+ iphdr = (struct ip_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR);
+#if ETHARP_SUPPORT_VLAN
+ if (ethhdr->type == ETHTYPE_VLAN) {
+ iphdr = (struct ip_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR);
+ }
+#endif /* ETHARP_SUPPORT_VLAN */
+
+ ip_addr_copy(iphdr_src, iphdr->src);
+
+ /* source is not on the local network? */
+ if (!ip_addr_netcmp(&iphdr_src, &(netif->ip_addr), &(netif->netmask))) {
+ /* do nothing */
+ return;
+ }
+
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_ip_input: updating ETHARP table.\n"));
+ /* update the source IP address in the cache, if present */
+ /* @todo We could use ETHARP_FLAG_TRY_HARD if we think we are going to talk
+ * back soon (for example, if the destination IP address is ours. */
+ update_arp_entry(netif, &iphdr_src, &(ethhdr->src), ETHARP_FLAG_FIND_ONLY);
+}
+#endif /* ETHARP_TRUST_IP_MAC */
+
+/**
+ * Responds to ARP requests to us. Upon ARP replies to us, add entry to cache
+ * send out queued IP packets. Updates cache with snooped address pairs.
+ *
+ * Should be called for incoming ARP packets. The pbuf in the argument
+ * is freed by this function.
+ *
+ * @param netif The lwIP network interface on which the ARP packet pbuf arrived.
+ * @param ethaddr Ethernet address of netif.
+ * @param p The ARP packet that arrived on netif. Is freed by this function.
+ *
+ * @return NULL
+ *
+ * @see pbuf_free()
+ */
+static void ICACHE_FLASH_ATTR
+etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p)
+{
+ struct etharp_hdr *hdr;
+ struct eth_hdr *ethhdr;
+ /* these are aligned properly, whereas the ARP header fields might not be */
+ ip_addr_t sipaddr, dipaddr;
+ u8_t for_us;
+#if LWIP_AUTOIP
+ const u8_t * ethdst_hwaddr;
+#endif /* LWIP_AUTOIP */
+#ifdef EBUF_LWIP
+ struct pbuf *q;
+#endif /* EBUF_LWIP */
+
+ LWIP_ERROR("netif != NULL", (netif != NULL), return;);
+
+ /* drop short ARP packets: we have to check for p->len instead of p->tot_len here
+ since a struct etharp_hdr is pointed to p->payload, so it musn't be chained! */
+ if (p->len < SIZEOF_ETHARP_PACKET) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+ ("etharp_arp_input: packet dropped, too short (%"S16_F"/%"S16_F")\n", p->tot_len,
+ (s16_t)SIZEOF_ETHARP_PACKET));
+ ETHARP_STATS_INC(etharp.lenerr);
+ ETHARP_STATS_INC(etharp.drop);
+ pbuf_free(p);
+ return;
+ }
+
+ ethhdr = (struct eth_hdr *)p->payload;
+ hdr = (struct etharp_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR);
+#if ETHARP_SUPPORT_VLAN
+ if (ethhdr->type == ETHTYPE_VLAN) {
+ hdr = (struct etharp_hdr *)(((u8_t*)ethhdr) + SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR);
+ }
+#endif /* ETHARP_SUPPORT_VLAN */
+
+ /* RFC 826 "Packet Reception": */
+ if ((hdr->hwtype != PP_HTONS(HWTYPE_ETHERNET)) ||
+ (hdr->hwlen != ETHARP_HWADDR_LEN) ||
+ (hdr->protolen != sizeof(ip_addr_t)) ||
+ (hdr->proto != PP_HTONS(ETHTYPE_IP)) ||
+ (ethhdr->type != PP_HTONS(ETHTYPE_ARP))) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+ ("etharp_arp_input: packet dropped, wrong hw type, hwlen, proto, protolen or ethernet type (%"U16_F"/%"U16_F"/%"U16_F"/%"U16_F"/%"U16_F")\n",
+ hdr->hwtype, hdr->hwlen, hdr->proto, hdr->protolen, ethhdr->type));
+ ETHARP_STATS_INC(etharp.proterr);
+ ETHARP_STATS_INC(etharp.drop);
+ pbuf_free(p);
+ return;
+ }
+ ETHARP_STATS_INC(etharp.recv);
+
+#if LWIP_AUTOIP
+ /* We have to check if a host already has configured our random
+ * created link local address and continously check if there is
+ * a host with this IP-address so we can detect collisions */
+ autoip_arp_reply(netif, hdr);
+#endif /* LWIP_AUTOIP */
+
+ /* Copy struct ip_addr2 to aligned ip_addr, to support compilers without
+ * structure packing (not using structure copy which breaks strict-aliasing rules). */
+ IPADDR2_COPY(&sipaddr, &hdr->sipaddr);
+ IPADDR2_COPY(&dipaddr, &hdr->dipaddr);
+
+ /* this interface is not configured? */
+ if (ip_addr_isany(&netif->ip_addr)) {
+ for_us = 0;
+ } else {
+ /* ARP packet directed to us? */
+ for_us = (u8_t)ip_addr_cmp(&dipaddr, &(netif->ip_addr));
+ }
+
+ /* ARP message directed to us?
+ -> add IP address in ARP cache; assume requester wants to talk to us,
+ can result in directly sending the queued packets for this host.
+ ARP message not directed to us?
+ -> update the source IP address in the cache, if present */
+ update_arp_entry(netif, &sipaddr, &(hdr->shwaddr),
+ for_us ? ETHARP_FLAG_TRY_HARD : ETHARP_FLAG_FIND_ONLY);
+
+ /* now act on the message itself */
+ switch (hdr->opcode) {
+ /* ARP request? */
+ case PP_HTONS(ARP_REQUEST):
+ /* ARP request. If it asked for our address, we send out a
+ * reply. In any case, we time-stamp any existing ARP entry,
+ * and possiby send out an IP packet that was queued on it. */
+
+ LWIP_DEBUGF (ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: incoming ARP request\n"));
+ /* ARP request for our address? */
+ if (for_us) {
+
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: replying to ARP request for our IP address\n"));
+ /* Re-use pbuf to send ARP reply.
+ Since we are re-using an existing pbuf, we can't call etharp_raw since
+ that would allocate a new pbuf. */
+ hdr->opcode = htons(ARP_REPLY);
+
+ IPADDR2_COPY(&hdr->dipaddr, &hdr->sipaddr);
+ IPADDR2_COPY(&hdr->sipaddr, &netif->ip_addr);
+
+ LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!",
+ (netif->hwaddr_len == ETHARP_HWADDR_LEN));
+#if LWIP_AUTOIP
+ /* If we are using Link-Local, all ARP packets that contain a Link-Local
+ * 'sender IP address' MUST be sent using link-layer broadcast instead of
+ * link-layer unicast. (See RFC3927 Section 2.5, last paragraph) */
+ ethdst_hwaddr = ip_addr_islinklocal(&netif->ip_addr) ? (u8_t*)(ethbroadcast.addr) : hdr->shwaddr.addr;
+#endif /* LWIP_AUTOIP */
+
+ ETHADDR16_COPY(&hdr->dhwaddr, &hdr->shwaddr);
+#if LWIP_AUTOIP
+ ETHADDR16_COPY(ðhdr->dest, ethdst_hwaddr);
+#else /* LWIP_AUTOIP */
+ ETHADDR16_COPY(ðhdr->dest, &hdr->shwaddr);
+#endif /* LWIP_AUTOIP */
+ ETHADDR16_COPY(&hdr->shwaddr, ethaddr);
+ ETHADDR16_COPY(ðhdr->src, ethaddr);
+
+ /* hwtype, hwaddr_len, proto, protolen and the type in the ethernet header
+ are already correct, we tested that before */
+#ifdef EBUF_LWIP
+ /*
+ * don't do flip-flop here... do a copy here.
+ * otherwise, we need to handle existing pbuf->eb in ieee80211_output.c
+ */
+
+ q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
+ if (q != NULL) {
+ pbuf_copy(q, p);
+ //pbuf_free(p);
+ } else {
+ LWIP_ASSERT("q != NULL", q != NULL);
+ }
+
+ netif->linkoutput(netif, q);
+ pbuf_free(q);
+#else
+
+ /* return ARP reply */
+ netif->linkoutput(netif, p);
+#endif /* ESF_LWIP */
+ /* we are not configured? */
+ } else if (ip_addr_isany(&netif->ip_addr)) {
+ /* { for_us == 0 and netif->ip_addr.addr == 0 } */
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: we are unconfigured, ARP request ignored.\n"));
+ /* request was not directed to us */
+ } else {
+ /* { for_us == 0 and netif->ip_addr.addr != 0 } */
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: ARP request was not for us.\n"));
+ }
+ break;
+ case PP_HTONS(ARP_REPLY):
+ /* ARP reply. We already updated the ARP cache earlier. */
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: incoming ARP reply\n"));
+#if (LWIP_DHCP && DHCP_DOES_ARP_CHECK)
+ /* DHCP wants to know about ARP replies from any host with an
+ * IP address also offered to us by the DHCP server. We do not
+ * want to take a duplicate IP address on a single network.
+ * @todo How should we handle redundant (fail-over) interfaces? */
+ dhcp_arp_reply(netif, &sipaddr);
+#endif /* (LWIP_DHCP && DHCP_DOES_ARP_CHECK) */
+ break;
+ default:
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: ARP unknown opcode type %"S16_F"\n", htons(hdr->opcode)));
+ ETHARP_STATS_INC(etharp.err);
+ break;
+ }
+ /* free ARP packet */
+ pbuf_free(p);
+}
+
+/**
+ * Resolve and fill-in Ethernet address header for outgoing IP packet.
+ *
+ * For IP multicast and broadcast, corresponding Ethernet addresses
+ * are selected and the packet is transmitted on the link.
+ *
+ * For unicast addresses, the packet is submitted to etharp_query(). In
+ * case the IP address is outside the local network, the IP address of
+ * the gateway is used.
+ *
+ * @param netif The lwIP network interface which the IP packet will be sent on.
+ * @param q The pbuf(s) containing the IP packet to be sent.
+ * @param ipaddr The IP address of the packet destination.
+ *
+ * @return
+ * - ERR_RTE No route to destination (no gateway to external networks),
+ * or the return type of either etharp_query() or etharp_send_ip().
+ */
+err_t
+etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr)
+{
+ struct eth_addr *dest, mcastaddr;
+
+ /* make room for Ethernet header - should not fail */
+ if (pbuf_header(q, sizeof(struct eth_hdr)) != 0) {
+ /* bail out */
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("etharp_output: could not allocate room for header.\n"));
+ LINK_STATS_INC(link.lenerr);
+ return ERR_BUF;
+ }
+
+ /* assume unresolved Ethernet address */
+ dest = NULL;
+ /* Determine on destination hardware address. Broadcasts and multicasts
+ * are special, other IP addresses are looked up in the ARP table. */
+
+ /* broadcast destination IP address? */
+ if (ip_addr_isbroadcast(ipaddr, netif)) {
+ /* broadcast on Ethernet also */
+ dest = (struct eth_addr *)ðbroadcast;
+ /* multicast destination IP address? */
+ } else if (ip_addr_ismulticast(ipaddr)) {
+ /* Hash IP multicast address to MAC address.*/
+ mcastaddr.addr[0] = 0x01;
+ mcastaddr.addr[1] = 0x00;
+ mcastaddr.addr[2] = 0x5e;
+ mcastaddr.addr[3] = ip4_addr2(ipaddr) & 0x7f;
+ mcastaddr.addr[4] = ip4_addr3(ipaddr);
+ mcastaddr.addr[5] = ip4_addr4(ipaddr);
+ /* destination Ethernet address is multicast */
+ dest = &mcastaddr;
+ /* unicast destination IP address? */
+ } else {
+ /* outside local network? */
+ if (!ip_addr_netcmp(ipaddr, &(netif->ip_addr), &(netif->netmask)) &&
+ !ip_addr_islinklocal(ipaddr)) {
+#if LWIP_AUTOIP
+ struct ip_hdr *iphdr = (struct ip_hdr*)((u8_t*)q->payload +
+ sizeof(struct eth_hdr));
+ /* According to RFC 3297, chapter 2.6.2 (Forwarding Rules), a packet with
+ a link-local source address must always be "directly to its destination
+ on the same physical link. The host MUST NOT send the packet to any
+ router for forwarding". */
+ if (!ip_addr_islinklocal(&iphdr->src))
+#endif /* LWIP_AUTOIP */
+ {
+ /* interface has default gateway? */
+ if (!ip_addr_isany(&netif->gw)) {
+ /* send to hardware address of default gateway IP address */
+ ipaddr = &(netif->gw);
+ /* no default gateway available */
+ } else {
+ /* no route to destination error (default gateway missing) */
+ return ERR_RTE;
+ }
+ }
+ }
+#if LWIP_NETIF_HWADDRHINT
+ if (netif->addr_hint != NULL) {
+ /* per-pcb cached entry was given */
+ u8_t etharp_cached_entry = *(netif->addr_hint);
+ if (etharp_cached_entry < ARP_TABLE_SIZE) {
+#endif /* LWIP_NETIF_HWADDRHINT */
+ if ((arp_table[etharp_cached_entry].state == ETHARP_STATE_STABLE) &&
+ (ip_addr_cmp(ipaddr, &arp_table[etharp_cached_entry].ipaddr))) {
+ /* the per-pcb-cached entry is stable and the right one! */
+ ETHARP_STATS_INC(etharp.cachehit);
+ return etharp_send_ip(netif, q, (struct eth_addr*)(netif->hwaddr),
+ &arp_table[etharp_cached_entry].ethaddr);
+ }
+#if LWIP_NETIF_HWADDRHINT
+ }
+ }
+#endif /* LWIP_NETIF_HWADDRHINT */
+ /* queue on destination Ethernet address belonging to ipaddr */
+ return etharp_query(netif, ipaddr, q);
+ }
+
+ /* continuation for multicast/broadcast destinations */
+ /* obtain source Ethernet address of the given interface */
+ /* send packet directly on the link */
+ return etharp_send_ip(netif, q, (struct eth_addr*)(netif->hwaddr), dest);
+}
+
+/**
+ * Send an ARP request for the given IP address and/or queue a packet.
+ *
+ * If the IP address was not yet in the cache, a pending ARP cache entry
+ * is added and an ARP request is sent for the given address. The packet
+ * is queued on this entry.
+ *
+ * If the IP address was already pending in the cache, a new ARP request
+ * is sent for the given address. The packet is queued on this entry.
+ *
+ * If the IP address was already stable in the cache, and a packet is
+ * given, it is directly sent and no ARP request is sent out.
+ *
+ * If the IP address was already stable in the cache, and no packet is
+ * given, an ARP request is sent out.
+ *
+ * @param netif The lwIP network interface on which ipaddr
+ * must be queried for.
+ * @param ipaddr The IP address to be resolved.
+ * @param q If non-NULL, a pbuf that must be delivered to the IP address.
+ * q is not freed by this function.
+ *
+ * @note q must only be ONE packet, not a packet queue!
+ *
+ * @return
+ * - ERR_BUF Could not make room for Ethernet header.
+ * - ERR_MEM Hardware address unknown, and no more ARP entries available
+ * to query for address or queue the packet.
+ * - ERR_MEM Could not queue packet due to memory shortage.
+ * - ERR_RTE No route to destination (no gateway to external networks).
+ * - ERR_ARG Non-unicast address given, those will not appear in ARP cache.
+ *
+ */
+err_t
+etharp_query(struct netif *netif, ip_addr_t *ipaddr, struct pbuf *q)
+{
+ struct eth_addr * srcaddr = (struct eth_addr *)netif->hwaddr;
+ err_t result = ERR_MEM;
+ s8_t i; /* ARP entry index */
+
+ /* non-unicast address? */
+ if (ip_addr_isbroadcast(ipaddr, netif) ||
+ ip_addr_ismulticast(ipaddr) ||
+ ip_addr_isany(ipaddr)) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: will not add non-unicast IP address to ARP cache\n"));
+ return ERR_ARG;
+ }
+
+ /* find entry in ARP cache, ask to create entry if queueing packet */
+ i = find_entry(ipaddr, ETHARP_FLAG_TRY_HARD);
+
+ /* could not find or create entry? */
+ if (i < 0) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not create ARP entry\n"));
+ if (q) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: packet dropped\n"));
+ ETHARP_STATS_INC(etharp.memerr);
+ }
+ return (err_t)i;
+ }
+
+ /* mark a fresh entry as pending (we just sent a request) */
+ if (arp_table[i].state == ETHARP_STATE_EMPTY) {
+ arp_table[i].state = ETHARP_STATE_PENDING;
+ }
+
+ /* { i is either a STABLE or (new or existing) PENDING entry } */
+ LWIP_ASSERT("arp_table[i].state == PENDING or STABLE",
+ ((arp_table[i].state == ETHARP_STATE_PENDING) ||
+ (arp_table[i].state == ETHARP_STATE_STABLE)));
+
+ /* do we have a pending entry? or an implicit query request? */
+ if ((arp_table[i].state == ETHARP_STATE_PENDING) || (q == NULL)) {
+ /* try to resolve it; send out ARP request */
+ result = etharp_request(netif, ipaddr);
+ if (result != ERR_OK) {
+ /* ARP request couldn't be sent */
+ /* We don't re-send arp request in etharp_tmr, but we still queue packets,
+ since this failure could be temporary, and the next packet calling
+ etharp_query again could lead to sending the queued packets. */
+ }
+ if (q == NULL) {
+ return result;
+ }
+ }
+
+ /* packet given? */
+ LWIP_ASSERT("q != NULL", q != NULL);
+ /* stable entry? */
+ if (arp_table[i].state == ETHARP_STATE_STABLE) {
+ /* we have a valid IP->Ethernet address mapping */
+ ETHARP_SET_HINT(netif, i);
+ /* send the packet */
+ result = etharp_send_ip(netif, q, srcaddr, &(arp_table[i].ethaddr));
+ /* pending entry? (either just created or already pending */
+ } else if (arp_table[i].state == ETHARP_STATE_PENDING) {
+ /* entry is still pending, queue the given packet 'q' */
+ struct pbuf *p;
+ int copy_needed = 0;
+ /* IF q includes a PBUF_REF, PBUF_POOL or PBUF_RAM, we have no choice but
+ * to copy the whole queue into a new PBUF_RAM (see bug #11400)
+ * PBUF_ROMs can be left as they are, since ROM must not get changed. */
+ p = q;
+ while (p) {
+ LWIP_ASSERT("no packet queues allowed!", (p->len != p->tot_len) || (p->next == 0));
+ if(p->type != PBUF_ROM) {
+ copy_needed = 1;
+ break;
+ }
+ p = p->next;
+ }
+ if(copy_needed) {
+ /* copy the whole packet into new pbufs */
+ p = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
+ if(p != NULL) {
+ if (pbuf_copy(p, q) != ERR_OK) {
+ pbuf_free(p);
+ p = NULL;
+ }
+ }
+ } else {
+ /* referencing the old pbuf is enough */
+ p = q;
+ pbuf_ref(p);
+ }
+ /* packet could be taken over? */
+ if (p != NULL) {
+ /* queue packet ... */
+#if ARP_QUEUEING
+ struct etharp_q_entry *new_entry;
+ /* allocate a new arp queue entry */
+ new_entry = (struct etharp_q_entry *)memp_malloc(MEMP_ARP_QUEUE);
+ if (new_entry != NULL) {
+ new_entry->next = 0;
+ new_entry->p = p;
+ if(arp_table[i].q != NULL) {
+ /* queue was already existent, append the new entry to the end */
+ struct etharp_q_entry *r;
+ r = arp_table[i].q;
+ while (r->next != NULL) {
+ r = r->next;
+ }
+ r->next = new_entry;
+ } else {
+ /* queue did not exist, first item in queue */
+ arp_table[i].q = new_entry;
+ }
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"S16_F"\n", (void *)q, (s16_t)i));
+ result = ERR_OK;
+ } else {
+ /* the pool MEMP_ARP_QUEUE is empty */
+ pbuf_free(p);
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q));
+ result = ERR_MEM;
+ }
+#else /* ARP_QUEUEING */
+ /* always queue one packet per ARP request only, freeing a previously queued packet */
+ if (arp_table[i].q != NULL) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: dropped previously queued packet %p for ARP entry %"S16_F"\n", (void *)q, (s16_t)i));
+ pbuf_free(arp_table[i].q);
+ }
+ arp_table[i].q = p;
+ result = ERR_OK;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"S16_F"\n", (void *)q, (s16_t)i));
+#endif /* ARP_QUEUEING */
+ } else {
+ ETHARP_STATS_INC(etharp.memerr);
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q));
+ result = ERR_MEM;
+ }
+ }
+ return result;
+}
+
+/**
+ * Send a raw ARP packet (opcode and all addresses can be modified)
+ *
+ * @param netif the lwip network interface on which to send the ARP packet
+ * @param ethsrc_addr the source MAC address for the ethernet header
+ * @param ethdst_addr the destination MAC address for the ethernet header
+ * @param hwsrc_addr the source MAC address for the ARP protocol header
+ * @param ipsrc_addr the source IP address for the ARP protocol header
+ * @param hwdst_addr the destination MAC address for the ARP protocol header
+ * @param ipdst_addr the destination IP address for the ARP protocol header
+ * @param opcode the type of the ARP packet
+ * @return ERR_OK if the ARP packet has been sent
+ * ERR_MEM if the ARP packet couldn't be allocated
+ * any other err_t on failure
+ */
+#if !LWIP_AUTOIP
+static
+#endif /* LWIP_AUTOIP */
+err_t ICACHE_FLASH_ATTR
+etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr,
+ const struct eth_addr *ethdst_addr,
+ const struct eth_addr *hwsrc_addr, const ip_addr_t *ipsrc_addr,
+ const struct eth_addr *hwdst_addr, const ip_addr_t *ipdst_addr,
+ const u16_t opcode)
+{
+ struct pbuf *p;
+ err_t result = ERR_OK;
+ struct eth_hdr *ethhdr;
+ struct etharp_hdr *hdr;
+#if LWIP_AUTOIP
+ const u8_t * ethdst_hwaddr;
+#endif /* LWIP_AUTOIP */
+
+ /* allocate a pbuf for the outgoing ARP request packet */
+ p = pbuf_alloc(PBUF_RAW, SIZEOF_ETHARP_PACKET, PBUF_RAM);
+ /* could allocate a pbuf for an ARP request? */
+ if (p == NULL) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("etharp_raw: could not allocate pbuf for ARP request.\n"));
+ ETHARP_STATS_INC(etharp.memerr);
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("check that first pbuf can hold struct etharp_hdr",
+ (p->len >= SIZEOF_ETHARP_PACKET));
+
+ ethhdr = (struct eth_hdr *)p->payload;
+ hdr = (struct etharp_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR);
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_raw: sending raw ARP packet.\n"));
+ hdr->opcode = htons(opcode);
+
+ LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!",
+ (netif->hwaddr_len == ETHARP_HWADDR_LEN));
+#if LWIP_AUTOIP
+ /* If we are using Link-Local, all ARP packets that contain a Link-Local
+ * 'sender IP address' MUST be sent using link-layer broadcast instead of
+ * link-layer unicast. (See RFC3927 Section 2.5, last paragraph) */
+ ethdst_hwaddr = ip_addr_islinklocal(ipsrc_addr) ? (u8_t*)(ethbroadcast.addr) : ethdst_addr->addr;
+#endif /* LWIP_AUTOIP */
+ /* Write the ARP MAC-Addresses */
+ ETHADDR16_COPY(&hdr->shwaddr, hwsrc_addr);
+ ETHADDR16_COPY(&hdr->dhwaddr, hwdst_addr);
+ /* Write the Ethernet MAC-Addresses */
+#if LWIP_AUTOIP
+ ETHADDR16_COPY(ðhdr->dest, ethdst_hwaddr);
+#else /* LWIP_AUTOIP */
+ ETHADDR16_COPY(ðhdr->dest, ethdst_addr);
+#endif /* LWIP_AUTOIP */
+ ETHADDR16_COPY(ðhdr->src, ethsrc_addr);
+ /* Copy struct ip_addr2 to aligned ip_addr, to support compilers without
+ * structure packing. */
+ IPADDR2_COPY(&hdr->sipaddr, ipsrc_addr);
+ IPADDR2_COPY(&hdr->dipaddr, ipdst_addr);
+
+ hdr->hwtype = PP_HTONS(HWTYPE_ETHERNET);
+ hdr->proto = PP_HTONS(ETHTYPE_IP);
+ /* set hwlen and protolen */
+ hdr->hwlen = ETHARP_HWADDR_LEN;
+ hdr->protolen = sizeof(ip_addr_t);
+
+ ethhdr->type = PP_HTONS(ETHTYPE_ARP);
+ /* send ARP query */
+ result = netif->linkoutput(netif, p);
+ ETHARP_STATS_INC(etharp.xmit);
+ /* free ARP query packet */
+ pbuf_free(p);
+ p = NULL;
+ /* could not allocate pbuf for ARP request */
+
+ return result;
+}
+
+/**
+ * Send an ARP request packet asking for ipaddr.
+ *
+ * @param netif the lwip network interface on which to send the request
+ * @param ipaddr the IP address for which to ask
+ * @return ERR_OK if the request has been sent
+ * ERR_MEM if the ARP packet couldn't be allocated
+ * any other err_t on failure
+ */
+err_t
+etharp_request(struct netif *netif, ip_addr_t *ipaddr)
+{
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_request: sending ARP request.\n"));
+ return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, ðbroadcast,
+ (struct eth_addr *)netif->hwaddr, &netif->ip_addr, ðzero,
+ ipaddr, ARP_REQUEST);
+}
+#endif /* LWIP_ARP */
+
+/**
+ * Process received ethernet frames. Using this function instead of directly
+ * calling ip_input and passing ARP frames through etharp in ethernetif_input,
+ * the ARP cache is protected from concurrent access.
+ *
+ * @param p the recevied packet, p->payload pointing to the ethernet header
+ * @param netif the network interface on which the packet was received
+ */
+err_t
+ethernet_input(struct pbuf *p, struct netif *netif)
+{
+ struct eth_hdr* ethhdr;
+ u16_t type;
+ s16_t ip_hdr_offset = SIZEOF_ETH_HDR;
+
+ if (p->len <= SIZEOF_ETH_HDR) {
+ /* a packet with only an ethernet header (or less) is not valid for us modify by ives at 2014.4.24*/
+ ETHARP_STATS_INC(etharp.proterr);
+ ETHARP_STATS_INC(etharp.drop);
+ goto free_and_return;
+ }
+
+ /* points to packet payload, which starts with an Ethernet header */
+ ethhdr = (struct eth_hdr *)p->payload;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE,
+ ("ethernet_input: dest:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", src:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", type:%"X16_F"\n",
+ (unsigned)ethhdr->dest.addr[0], (unsigned)ethhdr->dest.addr[1], (unsigned)ethhdr->dest.addr[2],
+ (unsigned)ethhdr->dest.addr[3], (unsigned)ethhdr->dest.addr[4], (unsigned)ethhdr->dest.addr[5],
+ (unsigned)ethhdr->src.addr[0], (unsigned)ethhdr->src.addr[1], (unsigned)ethhdr->src.addr[2],
+ (unsigned)ethhdr->src.addr[3], (unsigned)ethhdr->src.addr[4], (unsigned)ethhdr->src.addr[5],
+ (unsigned)htons(ethhdr->type)));
+
+ type = ethhdr->type;
+#if ETHARP_SUPPORT_VLAN
+ if (type == PP_HTONS(ETHTYPE_VLAN)) {
+ struct eth_vlan_hdr *vlan = (struct eth_vlan_hdr*)(((char*)ethhdr) + SIZEOF_ETH_HDR);
+ if (p->len <= SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR) {
+ /* a packet with only an ethernet/vlan header (or less) is not valid for us modify by ives at 2014.4.24*/
+ ETHARP_STATS_INC(etharp.proterr);
+ ETHARP_STATS_INC(etharp.drop);
+ goto free_and_return;
+ }
+#ifdef ETHARP_VLAN_CHECK /* if not, allow all VLANs */
+ if (VLAN_ID(vlan) != ETHARP_VLAN_CHECK) {
+ /* silently ignore this packet: not for our VLAN */
+ pbuf_free(p);
+ return ERR_OK;
+ }
+#endif /* ETHARP_VLAN_CHECK */
+ type = vlan->tpid;
+ ip_hdr_offset = SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR;
+ }
+#endif /* ETHARP_SUPPORT_VLAN */
+
+#if LWIP_ARP_FILTER_NETIF
+ netif = LWIP_ARP_FILTER_NETIF_FN(p, netif, htons(type));
+#endif /* LWIP_ARP_FILTER_NETIF*/
+
+ switch (type) {
+#if LWIP_ARP
+ /* IP packet? */
+ case PP_HTONS(ETHTYPE_IP):
+ if (!(netif->flags & NETIF_FLAG_ETHARP)) {
+ goto free_and_return;
+ }
+#if ETHARP_TRUST_IP_MAC
+ /* update ARP table */
+ etharp_ip_input(netif, p);
+#endif /* ETHARP_TRUST_IP_MAC */
+ /* skip Ethernet header */
+ if(pbuf_header(p, -ip_hdr_offset)) {
+ LWIP_ASSERT("Can't move over header in packet", 0);
+ goto free_and_return;
+ } else {
+ /* pass to IP layer */
+ ip_input(p, netif);
+ }
+ break;
+
+ case PP_HTONS(ETHTYPE_ARP):
+ if (!(netif->flags & NETIF_FLAG_ETHARP)) {
+ goto free_and_return;
+ }
+ /* pass p to ARP module */
+ etharp_arp_input(netif, (struct eth_addr*)(netif->hwaddr), p);
+ break;
+#endif /* LWIP_ARP */
+#if PPPOE_SUPPORT
+ case PP_HTONS(ETHTYPE_PPPOEDISC): /* PPP Over Ethernet Discovery Stage */
+ pppoe_disc_input(netif, p);
+ break;
+
+ case PP_HTONS(ETHTYPE_PPPOE): /* PPP Over Ethernet Session Stage */
+ pppoe_data_input(netif, p);
+ break;
+#endif /* PPPOE_SUPPORT */
+
+ default:
+ ETHARP_STATS_INC(etharp.proterr);
+ ETHARP_STATS_INC(etharp.drop);
+ goto free_and_return;
+ }
+
+ /* This means the pbuf is freed or consumed,
+ so the caller doesn't have to free it again */
+ return ERR_OK;
+
+free_and_return:
+ pbuf_free(p);
+ return ERR_OK;
+}
+#endif /* LWIP_ARP || LWIP_ETHERNET */
diff --git a/app/modules/Makefile b/app/modules/Makefile
new file mode 100644
index 00000000..1d9f19d9
--- /dev/null
+++ b/app/modules/Makefile
@@ -0,0 +1,49 @@
+
+#############################################################
+# Required variables for each makefile
+# Discard this section from all parent makefiles
+# Expected variables (with automatic defaults):
+# CSRCS (all "C" files in the dir)
+# SUBDIRS (all subdirs with a Makefile)
+# GEN_LIBS - list of libs to be generated ()
+# GEN_IMAGES - list of images to be generated ()
+# COMPONENTS_xxx - a list of libs/objs in the form
+# subdir/lib to be extracted and rolled up into
+# a generated lib/image xxx.a ()
+#
+ifndef PDIR
+GEN_LIBS = libmodules.a
+endif
+
+#############################################################
+# Configuration i.e. compile options etc.
+# Target specific stuff (defines etc.) goes in here!
+# Generally values applying to a tree are captured in the
+# makefile at its root level - these are then overridden
+# for a subtree within the makefile rooted therein
+#
+#DEFINES +=
+
+#############################################################
+# Recursion Magic - Don't touch this!!
+#
+# Each subtree potentially has an include directory
+# corresponding to the common APIs applicable to modules
+# rooted at that subtree. Accordingly, the INCLUDE PATH
+# of a module can only contain the include directories up
+# its parent path, and not its siblings
+#
+# Required for each makefile to inherit from the parent
+#
+
+INCLUDES := $(INCLUDES) -I $(PDIR)include
+INCLUDES += -I ./
+INCLUDES += -I ../libc
+INCLUDES += -I ../lua
+INCLUDES += -I ../platform
+INCLUDES += -I ../wofs
+INCLUDES += -I ../spiffs
+INCLUDES += -I ../smart
+PDIR := ../$(PDIR)
+sinclude $(PDIR)Makefile
+
diff --git a/app/modules/adc.c b/app/modules/adc.c
new file mode 100644
index 00000000..338a2507
--- /dev/null
+++ b/app/modules/adc.c
@@ -0,0 +1,44 @@
+// Module for interfacing with adc
+
+//#include "lua.h"
+#include "lualib.h"
+#include "lauxlib.h"
+#include "platform.h"
+#include "auxmods.h"
+#include "lrotable.h"
+
+#include "c_types.h"
+
+// Lua: read(id) , return system adc
+static int ICACHE_FLASH_ATTR adc_sample( lua_State* L )
+{
+ unsigned id = luaL_checkinteger( L, 1 );
+ MOD_CHECK_ID( adc, id );
+ unsigned val = 0xFFFF & system_adc_read();
+ lua_pushinteger( L, val );
+ return 1;
+}
+
+// Module function map
+#define MIN_OPT_LEVEL 2
+#include "lrodefs.h"
+const LUA_REG_TYPE adc_map[] =
+{
+ { LSTRKEY( "read" ), LFUNCVAL( adc_sample ) },
+#if LUA_OPTIMIZE_MEMORY > 0
+
+#endif
+ { LNILKEY, LNILVAL }
+};
+
+LUALIB_API int ICACHE_FLASH_ATTR luaopen_adc( lua_State *L )
+{
+#if LUA_OPTIMIZE_MEMORY > 0
+ return 0;
+#else // #if LUA_OPTIMIZE_MEMORY > 0
+ luaL_register( L, AUXLIB_ADC, adc_map );
+ // Add constants
+
+ return 1;
+#endif // #if LUA_OPTIMIZE_MEMORY > 0
+}
diff --git a/app/modules/auxmods.h b/app/modules/auxmods.h
new file mode 100644
index 00000000..d8166643
--- /dev/null
+++ b/app/modules/auxmods.h
@@ -0,0 +1,93 @@
+// Auxiliary Lua modules. All of them are declared here, then each platform
+// decides what module(s) to register in the src/platform/xxxxx/platform_conf.h file
+// FIXME: no longer platform_conf.h - either CPU header file, or board file
+
+#ifndef __AUXMODS_H__
+#define __AUXMODS_H__
+
+#include "lua.h"
+
+#define AUXLIB_GPIO "gpio"
+LUALIB_API int ( luaopen_gpio )( lua_State *L );
+
+#define AUXLIB_SPI "spi"
+LUALIB_API int ( luaopen_spi )( lua_State *L );
+
+#define AUXLIB_CAN "can"
+LUALIB_API int ( luaopen_can )( lua_State *L );
+
+#define AUXLIB_TMR "tmr"
+LUALIB_API int ( luaopen_tmr )( lua_State *L );
+
+#define AUXLIB_PD "pd"
+LUALIB_API int ( luaopen_pd )( lua_State *L );
+
+#define AUXLIB_UART "uart"
+LUALIB_API int ( luaopen_uart )( lua_State *L );
+
+#define AUXLIB_TERM "term"
+LUALIB_API int ( luaopen_term )( lua_State *L );
+
+#define AUXLIB_PWM "pwm"
+LUALIB_API int ( luaopen_pwm )( lua_State *L );
+
+#define AUXLIB_PACK "pack"
+LUALIB_API int ( luaopen_pack )( lua_State *L );
+
+#define AUXLIB_BIT "bit"
+LUALIB_API int ( luaopen_bit )( lua_State *L );
+
+#define AUXLIB_NET "net"
+LUALIB_API int ( luaopen_net )( lua_State *L );
+
+#define AUXLIB_CPU "cpu"
+LUALIB_API int ( luaopen_cpu )( lua_State* L );
+
+#define AUXLIB_ADC "adc"
+LUALIB_API int ( luaopen_adc )( lua_State *L );
+
+#define AUXLIB_RPC "rpc"
+LUALIB_API int ( luaopen_rpc )( lua_State *L );
+
+#define AUXLIB_BITARRAY "bitarray"
+LUALIB_API int ( luaopen_bitarray )( lua_State *L );
+
+#define AUXLIB_ELUA "elua"
+LUALIB_API int ( luaopen_elua )( lua_State *L );
+
+#define AUXLIB_I2C "i2c"
+LUALIB_API int ( luaopen_i2c )( lua_State *L );
+
+#define AUXLIB_WIFI "wifi"
+LUALIB_API int ( luaopen_wifi )( lua_State *L );
+
+#define AUXLIB_NODE "node"
+LUALIB_API int ( luaopen_node )( lua_State *L );
+
+#define AUXLIB_FILE "file"
+LUALIB_API int ( luaopen_file )( lua_State *L );
+
+#define AUXLIB_OW "ow"
+LUALIB_API int ( luaopen_ow )( lua_State *L );
+
+// Helper macros
+#define MOD_CHECK_ID( mod, id )\
+ if( !platform_ ## mod ## _exists( id ) )\
+ return luaL_error( L, #mod" %d does not exist", ( unsigned )id )
+
+#define MOD_CHECK_TIMER( id )\
+ if( id == PLATFORM_TIMER_SYS_ID && !platform_timer_sys_available() )\
+ return luaL_error( L, "the system timer is not available on this platform" );\
+ if( !platform_timer_exists( id ) )\
+ return luaL_error( L, "timer %d does not exist", ( unsigned )id )\
+
+#define MOD_CHECK_RES_ID( mod, id, resmod, resid )\
+ if( !platform_ ## mod ## _check_ ## resmod ## _id( id, resid ) )\
+ return luaL_error( L, #resmod" %d not valid with " #mod " %d", ( unsigned )resid, ( unsigned )id )
+
+#define MOD_REG_NUMBER( L, name, val )\
+ lua_pushnumber( L, val );\
+ lua_setfield( L, -2, name )
+
+#endif
+
diff --git a/app/modules/bit.c b/app/modules/bit.c
new file mode 100644
index 00000000..8579cc26
--- /dev/null
+++ b/app/modules/bit.c
@@ -0,0 +1,145 @@
+/* Bitwise operations library */
+/* (c) Reuben Thomas 2000-2008 */
+/* See README for license */
+
+// Modified by BogdanM for eLua
+
+
+#include "c_limits.h"
+
+//#include "lua.h"
+#include "lauxlib.h"
+#include "auxmods.h"
+// #include "type.h"
+#include "lrotable.h"
+
+/* FIXME: Assume size_t is an unsigned lua_Integer */
+typedef size_t lua_UInteger;
+#define LUA_UINTEGER_MAX SIZE_MAX
+
+/* Define TOBIT to get a bit value */
+#define TOBIT(L, n) \
+ (luaL_checkinteger((L), (n)))
+
+/* Operations
+
+ The macros MONADIC and VARIADIC only deal with bitwise operations.
+
+ LOGICAL_SHIFT truncates its left-hand operand before shifting so
+ that any extra bits at the most-significant end are not shifted
+ into the result.
+
+ ARITHMETIC_SHIFT does not truncate its left-hand operand, so that
+ the sign bits are not removed and right shift work properly.
+ */
+
+#define MONADIC(name, op) \
+ static int ICACHE_FLASH_ATTR bit_ ## name(lua_State *L) { \
+ lua_pushinteger(L, op TOBIT(L, 1)); \
+ return 1; \
+ }
+
+#define VARIADIC(name, op) \
+ static int ICACHE_FLASH_ATTR bit_ ## name(lua_State *L) { \
+ int n = lua_gettop(L), i; \
+ lua_Integer w = TOBIT(L, 1); \
+ for (i = 2; i <= n; i++) \
+ w op TOBIT(L, i); \
+ lua_pushinteger(L, w); \
+ return 1; \
+ }
+
+#define LOGICAL_SHIFT(name, op) \
+ static int ICACHE_FLASH_ATTR bit_ ## name(lua_State *L) { \
+ lua_pushinteger(L, (lua_UInteger)TOBIT(L, 1) op \
+ (unsigned)luaL_checknumber(L, 2)); \
+ return 1; \
+ }
+
+#define ARITHMETIC_SHIFT(name, op) \
+ static int ICACHE_FLASH_ATTR bit_ ## name(lua_State *L) { \
+ lua_pushinteger(L, (lua_Integer)TOBIT(L, 1) op \
+ (unsigned)luaL_checknumber(L, 2)); \
+ return 1; \
+ }
+
+MONADIC(bnot, ~)
+VARIADIC(band, &=)
+VARIADIC(bor, |=)
+VARIADIC(bxor, ^=)
+ARITHMETIC_SHIFT(lshift, <<)
+LOGICAL_SHIFT(rshift, >>)
+ARITHMETIC_SHIFT(arshift, >>)
+
+// Lua: res = bit( position )
+static int ICACHE_FLASH_ATTR bit_bit( lua_State* L )
+{
+ lua_pushinteger( L, ( lua_Integer )( 1 << luaL_checkinteger( L, 1 ) ) );
+ return 1;
+}
+
+// Lua: res = isset( value, position )
+static int ICACHE_FLASH_ATTR bit_isset( lua_State* L )
+{
+ lua_UInteger val = ( lua_UInteger )luaL_checkinteger( L, 1 );
+ unsigned pos = ( unsigned )luaL_checkinteger( L, 2 );
+
+ lua_pushboolean( L, val & ( 1 << pos ) ? 1 : 0 );
+ return 1;
+}
+
+// Lua: res = isclear( value, position )
+static int ICACHE_FLASH_ATTR bit_isclear( lua_State* L )
+{
+ lua_UInteger val = ( lua_UInteger )luaL_checkinteger( L, 1 );
+ unsigned pos = ( unsigned )luaL_checkinteger( L, 2 );
+
+ lua_pushboolean( L, val & ( 1 << pos ) ? 0 : 1 );
+ return 1;
+}
+
+// Lua: res = set( value, pos1, pos2, ... )
+static int ICACHE_FLASH_ATTR bit_set( lua_State* L )
+{
+ lua_UInteger val = ( lua_UInteger )luaL_checkinteger( L, 1 );
+ unsigned total = lua_gettop( L ), i;
+
+ for( i = 2; i <= total; i ++ )
+ val |= 1 << ( unsigned )luaL_checkinteger( L, i );
+ lua_pushinteger( L, ( lua_Integer )val );
+ return 1;
+}
+
+// Lua: res = clear( value, pos1, pos2, ... )
+static int ICACHE_FLASH_ATTR bit_clear( lua_State* L )
+{
+ lua_UInteger val = ( lua_UInteger )luaL_checkinteger( L, 1 );
+ unsigned total = lua_gettop( L ), i;
+
+ for( i = 2; i <= total; i ++ )
+ val &= ~( 1 << ( unsigned )luaL_checkinteger( L, i ) );
+ lua_pushinteger( L, ( lua_Integer )val );
+ return 1;
+}
+
+#define MIN_OPT_LEVEL 2
+#include "lrodefs.h"
+const LUA_REG_TYPE bit_map[] = {
+ { LSTRKEY( "bnot" ), LFUNCVAL( bit_bnot ) },
+ { LSTRKEY( "band" ), LFUNCVAL( bit_band ) },
+ { LSTRKEY( "bor" ), LFUNCVAL( bit_bor ) },
+ { LSTRKEY( "bxor" ), LFUNCVAL( bit_bxor ) },
+ { LSTRKEY( "lshift" ), LFUNCVAL( bit_lshift ) },
+ { LSTRKEY( "rshift" ), LFUNCVAL( bit_rshift ) },
+ { LSTRKEY( "arshift" ), LFUNCVAL( bit_arshift ) },
+ { LSTRKEY( "bit" ), LFUNCVAL( bit_bit ) },
+ { LSTRKEY( "set" ), LFUNCVAL( bit_set ) },
+ { LSTRKEY( "clear" ), LFUNCVAL( bit_clear ) },
+ { LSTRKEY( "isset" ), LFUNCVAL( bit_isset ) },
+ { LSTRKEY( "isclear" ), LFUNCVAL( bit_isclear ) },
+ { LNILKEY, LNILVAL}
+};
+
+LUALIB_API int ICACHE_FLASH_ATTR luaopen_bit (lua_State *L) {
+ LREGISTER( L, "bit", bit_map );
+}
diff --git a/app/modules/file.c b/app/modules/file.c
new file mode 100644
index 00000000..9edc6fd5
--- /dev/null
+++ b/app/modules/file.c
@@ -0,0 +1,262 @@
+// Module for interfacing with file system
+
+#include "lua.h"
+#include "lualib.h"
+#include "lauxlib.h"
+#include "platform.h"
+#include "auxmods.h"
+#include "lrotable.h"
+
+#include "c_types.h"
+#include "flash_fs.h"
+#include "c_string.h"
+
+static int file_fd = FS_OPEN_OK - 1;
+
+// Lua: open(filename, mode)
+static int ICACHE_FLASH_ATTR file_open( lua_State* L )
+{
+ size_t len;
+ if((FS_OPEN_OK - 1)!=file_fd){
+ fs_close(file_fd);
+ file_fd = FS_OPEN_OK - 1;
+ }
+
+ const char *fname = luaL_checklstring( L, 1, &len );
+ if( len > FS_NAME_MAX_LENGTH )
+ return luaL_error(L, "filename too long");
+ const char *mode = luaL_optstring(L, 2, "r");
+
+ file_fd = fs_open(fname, fs_mode2flag(mode));
+
+ if(file_fd < FS_OPEN_OK){
+ file_fd = FS_OPEN_OK - 1;
+ lua_pushnil(L);
+ } else {
+ lua_pushboolean(L, 1);
+ }
+ return 1;
+}
+
+// Lua: close()
+static int ICACHE_FLASH_ATTR file_close( lua_State* L )
+{
+ if((FS_OPEN_OK - 1)!=file_fd){
+ fs_close(file_fd);
+ file_fd = FS_OPEN_OK - 1;
+ }
+ return 0;
+}
+
+#if defined(BUILD_WOFS)
+// Lua: list()
+static int ICACHE_FLASH_ATTR file_list( lua_State* L )
+{
+ uint32_t start = 0;
+ size_t act_len = 0;
+ char fsname[ FS_NAME_MAX_LENGTH + 1 ];
+ lua_newtable( L );
+ while( FS_FILE_OK == wofs_next(&start, fsname, FS_NAME_MAX_LENGTH, &act_len) ){
+ lua_pushinteger(L, act_len);
+ lua_setfield( L, -2, fsname );
+ }
+ return 1;
+}
+
+// Lua: format()
+static int ICACHE_FLASH_ATTR file_format( lua_State* L )
+{
+ size_t len;
+ file_close(L);
+ if( !fs_format() )
+ {
+ NODE_ERR( "\ni*** ERROR ***: unable to format. FS might be compromised.\n" );
+ NODE_ERR( "It is advised to re-flash the nodeMcu image.\n" );
+ }
+ else{
+ NODE_ERR( "format done.\n" );
+ }
+ return 0;
+}
+
+#elif defined(BUILD_SPIFFS)
+
+extern spiffs fs;
+
+// Lua: list()
+static int ICACHE_FLASH_ATTR file_list( lua_State* L )
+{
+ spiffs_DIR d;
+ struct spiffs_dirent e;
+ struct spiffs_dirent *pe = &e;
+
+ lua_newtable( L );
+ SPIFFS_opendir(&fs, "/", &d);
+ while ((pe = SPIFFS_readdir(&d, pe))) {
+ // NODE_ERR(" %s size:%i\n", pe->name, pe->size);
+ lua_pushinteger(L, pe->size);
+ lua_setfield( L, -2, pe->name );
+ }
+ SPIFFS_closedir(&d);
+ return 1;
+}
+
+static int ICACHE_FLASH_ATTR file_seek (lua_State *L)
+{
+ static const int mode[] = {FS_SEEK_SET, FS_SEEK_CUR, FS_SEEK_END};
+ static const char *const modenames[] = {"set", "cur", "end", NULL};
+ if((FS_OPEN_OK - 1)==file_fd)
+ return luaL_error(L, "open a file first");
+ int op = luaL_checkoption(L, 1, "cur", modenames);
+ long offset = luaL_optlong(L, 2, 0);
+ op = fs_seek(file_fd, offset, mode[op]);
+ if (op)
+ lua_pushboolean(L, 1); /* error */
+ else
+ lua_pushinteger(L, fs_tell(file_fd));
+ return 1;
+}
+
+// Lua: remove(filename)
+static int ICACHE_FLASH_ATTR file_remove( lua_State* L )
+{
+ size_t len;
+ const char *fname = luaL_checklstring( L, 1, &len );
+ if( len > FS_NAME_MAX_LENGTH )
+ return luaL_error(L, "filename too long");
+ file_close(L);
+ SPIFFS_remove(&fs, fname);
+ return 0;
+}
+
+// Lua: flush()
+static int ICACHE_FLASH_ATTR file_flush( lua_State* L )
+{
+ if((FS_OPEN_OK - 1)==file_fd)
+ return luaL_error(L, "open a file first");
+ if(fs_flush(file_fd) == 0)
+ lua_pushboolean(L, 1);
+ else
+ lua_pushnil(L);
+ return 1;
+}
+#if 0
+// Lua: check()
+static int ICACHE_FLASH_ATTR file_check( lua_State* L )
+{
+ file_close(L);
+ lua_pushinteger(L, fs_check());
+ return 1;
+}
+#endif
+
+#endif
+
+// Lua: readline()
+static int ICACHE_FLASH_ATTR file_readline( lua_State* L )
+{
+ luaL_Buffer b;
+ if((FS_OPEN_OK - 1)==file_fd)
+ return luaL_error(L, "open a file first");
+
+ luaL_buffinit(L, &b);
+ char *p = luaL_prepbuffer(&b);
+ signed char c = EOF;
+ int i = 0;
+
+ do{
+ c = (signed char)fs_getc(file_fd);
+ if(c==EOF){
+ break;
+ }
+ p[i++] = c;
+ }while((c!=EOF) && (c!='\n') && (i0 && p[i-1] == '\n')
+ i--; /* do not include `eol' */
+#endif
+
+ if(i==0){
+ luaL_pushresult(&b); /* close buffer */
+ return (lua_objlen(L, -1) > 0); /* check whether read something */
+ }
+
+ luaL_addsize(&b, i);
+ luaL_pushresult(&b); /* close buffer */
+ return 1; /* read at least an `eol' */
+}
+
+// Lua: write("string")
+static int ICACHE_FLASH_ATTR file_write( lua_State* L )
+{
+ if((FS_OPEN_OK - 1)==file_fd)
+ return luaL_error(L, "open a file first");
+ size_t l, rl;
+ const char *s = luaL_checklstring(L, 1, &l);
+ rl = fs_write(file_fd, s, l);
+ if(rl==l)
+ lua_pushboolean(L, 1);
+ else
+ lua_pushnil(L);
+ return 1;
+}
+
+// Lua: writeline("string")
+static int ICACHE_FLASH_ATTR file_writeline( lua_State* L )
+{
+ if((FS_OPEN_OK - 1)==file_fd)
+ return luaL_error(L, "open a file first");
+ size_t l, rl;
+ const char *s = luaL_checklstring(L, 1, &l);
+ rl = fs_write(file_fd, s, l);
+ if(rl==l){
+ rl = fs_write(file_fd, "\n", 1);
+ if(rl==1)
+ lua_pushboolean(L, 1);
+ else
+ lua_pushnil(L);
+ }
+ else{
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+// Module function map
+#define MIN_OPT_LEVEL 2
+#include "lrodefs.h"
+const LUA_REG_TYPE file_map[] =
+{
+ { LSTRKEY( "list" ), LFUNCVAL( file_list ) },
+ { LSTRKEY( "open" ), LFUNCVAL( file_open ) },
+ { LSTRKEY( "close" ), LFUNCVAL( file_close ) },
+ { LSTRKEY( "write" ), LFUNCVAL( file_write ) },
+ { LSTRKEY( "writeline" ), LFUNCVAL( file_writeline ) },
+ { LSTRKEY( "readline" ), LFUNCVAL( file_readline ) },
+#if defined(BUILD_WOFS)
+ { LSTRKEY( "format" ), LFUNCVAL( file_format ) },
+#elif defined(BUILD_SPIFFS)
+ { LSTRKEY( "remove" ), LFUNCVAL( file_remove ) },
+ { LSTRKEY( "seek" ), LFUNCVAL( file_seek ) },
+ { LSTRKEY( "flush" ), LFUNCVAL( file_flush ) },
+ // { LSTRKEY( "check" ), LFUNCVAL( file_check ) },
+#endif
+
+#if LUA_OPTIMIZE_MEMORY > 0
+
+#endif
+ { LNILKEY, LNILVAL }
+};
+
+LUALIB_API int ICACHE_FLASH_ATTR luaopen_file( lua_State *L )
+{
+#if LUA_OPTIMIZE_MEMORY > 0
+ return 0;
+#else // #if LUA_OPTIMIZE_MEMORY > 0
+ luaL_register( L, AUXLIB_NODE, file_map );
+ // Add constants
+
+ return 1;
+#endif // #if LUA_OPTIMIZE_MEMORY > 0
+}
diff --git a/app/modules/gpio.c b/app/modules/gpio.c
new file mode 100644
index 00000000..e3fa319c
--- /dev/null
+++ b/app/modules/gpio.c
@@ -0,0 +1,200 @@
+// Module for interfacing with GPIO
+
+//#include "lua.h"
+#include "lualib.h"
+#include "lauxlib.h"
+#include "platform.h"
+#include "auxmods.h"
+#include "lrotable.h"
+
+#include "c_types.h"
+#include "c_string.h"
+
+#define PULLUP PLATFORM_GPIO_PULLUP
+#define FLOAT PLATFORM_GPIO_FLOAT
+#define OUTPUT PLATFORM_GPIO_OUTPUT
+#define INPUT PLATFORM_GPIO_INPUT
+#define INTERRUPT PLATFORM_GPIO_INT
+#define HIGH PLATFORM_GPIO_HIGH
+#define LOW PLATFORM_GPIO_LOW
+
+
+#ifdef GPIO_INTERRUPT_ENABLE
+static int gpio_cb_ref[GPIO_PIN_NUM];
+static lua_State* gL = NULL;
+
+void ICACHE_FLASH_ATTR lua_gpio_unref(unsigned pin){
+ if(gpio_cb_ref[pin] != LUA_NOREF){
+ if(gL!=NULL)
+ luaL_unref(gL, LUA_REGISTRYINDEX, gpio_cb_ref[pin]);
+ }
+ gpio_cb_ref[pin] = LUA_NOREF;
+}
+
+void ICACHE_FLASH_ATTR gpio_intr_callback( unsigned pin, unsigned level )
+{
+ NODE_DBG("pin:%d, level:%d \n", pin, level);
+ if(gpio_cb_ref[pin] == LUA_NOREF)
+ return;
+ if(!gL)
+ return;
+ lua_rawgeti(gL, LUA_REGISTRYINDEX, gpio_cb_ref[pin]);
+ lua_pushinteger(gL, level);
+ lua_call(gL, 1, 0);
+}
+
+// Lua: trig( pin, type, function )
+static int ICACHE_FLASH_ATTR lgpio_trig( lua_State* L )
+{
+ unsigned type;
+ unsigned pin;
+ size_t sl;
+
+ pin = luaL_checkinteger( L, 1 );
+ MOD_CHECK_ID( gpio, pin );
+ if(pin==0)
+ return luaL_error( L, "no interrupt for D0" );
+
+ const char *str = luaL_checklstring( L, 2, &sl );
+ if (str == NULL)
+ return luaL_error( L, "wrong arg type" );
+
+ if(sl == 2 && c_strcmp(str, "up") == 0){
+ type = GPIO_PIN_INTR_POSEDGE;
+ }else if(sl == 4 && c_strcmp(str, "down") == 0){
+ type = GPIO_PIN_INTR_NEGEDGE;
+ }else if(sl == 4 && c_strcmp(str, "both") == 0){
+ type = GPIO_PIN_INTR_ANYEGDE;
+ }else if(sl == 3 && c_strcmp(str, "low") == 0){
+ type = GPIO_PIN_INTR_LOLEVEL;
+ }else if(sl == 4 && c_strcmp(str, "high") == 0){
+ type = GPIO_PIN_INTR_HILEVEL;
+ }else{
+ type = GPIO_PIN_INTR_DISABLE;
+ }
+
+ // luaL_checkanyfunction(L, 3);
+ if (lua_type(L, 3) == LUA_TFUNCTION || lua_type(L, 3) == LUA_TLIGHTFUNCTION){
+ lua_pushvalue(L, 3); // copy argument (func) to the top of stack
+ if(gpio_cb_ref[pin] != LUA_NOREF)
+ luaL_unref(L, LUA_REGISTRYINDEX, gpio_cb_ref[pin]);
+ gpio_cb_ref[pin] = luaL_ref(L, LUA_REGISTRYINDEX);
+ }
+
+ platform_gpio_intr_init(pin, type);
+ return 0;
+}
+#endif
+
+// Lua: mode( pin, mode, pullup )
+static int ICACHE_FLASH_ATTR lgpio_mode( lua_State* L )
+{
+ unsigned mode, pullup = FLOAT;
+ unsigned pin;
+
+ pin = luaL_checkinteger( L, 1 );
+ MOD_CHECK_ID( gpio, pin );
+ mode = luaL_checkinteger( L, 2 );
+ if ( mode!=OUTPUT && mode!=INPUT && mode!=INTERRUPT)
+ return luaL_error( L, "wrong arg type" );
+ if(pin==0 && mode==INTERRUPT)
+ return luaL_error( L, "no interrupt for D0" );
+ if(lua_isnumber(L, 3))
+ pullup = lua_tointeger( L, 3 );
+ if(pullup!=FLOAT)
+ pullup = PULLUP;
+#ifdef GPIO_INTERRUPT_ENABLE
+ gL = L; // save to local gL, for callback function
+ if (mode!=INTERRUPT){ // disable interrupt
+ if(gpio_cb_ref[pin] != LUA_NOREF){
+ luaL_unref(L, LUA_REGISTRYINDEX, gpio_cb_ref[pin]);
+ }
+ gpio_cb_ref[pin] = LUA_NOREF;
+ }
+#endif
+ int r = platform_gpio_mode( pin, mode, pullup );
+ if( r<0 )
+ return luaL_error( L, "wrong pin num." );
+ return 0;
+}
+
+// Lua: read( pin )
+static int ICACHE_FLASH_ATTR lgpio_read( lua_State* L )
+{
+ unsigned pin;
+
+ pin = luaL_checkinteger( L, 1 );
+ MOD_CHECK_ID( gpio, pin );
+
+ unsigned level = platform_gpio_read( pin );
+ lua_pushinteger( L, level );
+ return 1;
+}
+
+// Lua: write( pin, level )
+static int ICACHE_FLASH_ATTR lgpio_write( lua_State* L )
+{
+ unsigned level;
+ unsigned pin;
+
+ pin = luaL_checkinteger( L, 1 );
+ MOD_CHECK_ID( gpio, pin );
+ level = luaL_checkinteger( L, 2 );
+ if ( level!=HIGH && level!=LOW )
+ return luaL_error( L, "wrong arg type" );
+ platform_gpio_write(pin, level);
+ return 0;
+}
+
+// Module function map
+#define MIN_OPT_LEVEL 2
+#include "lrodefs.h"
+const LUA_REG_TYPE gpio_map[] =
+{
+ { LSTRKEY( "mode" ), LFUNCVAL( lgpio_mode ) },
+ { LSTRKEY( "read" ), LFUNCVAL( lgpio_read ) },
+ { LSTRKEY( "write" ), LFUNCVAL( lgpio_write ) },
+#ifdef GPIO_INTERRUPT_ENABLE
+ { LSTRKEY( "trig" ), LFUNCVAL( lgpio_trig ) },
+#endif
+#if LUA_OPTIMIZE_MEMORY > 0
+#ifdef GPIO_INTERRUPT_ENABLE
+ { LSTRKEY( "INT" ), LNUMVAL( INTERRUPT ) },
+#endif
+ { LSTRKEY( "OUTPUT" ), LNUMVAL( OUTPUT ) },
+ { LSTRKEY( "INPUT" ), LNUMVAL( INPUT ) },
+ { LSTRKEY( "HIGH" ), LNUMVAL( HIGH ) },
+ { LSTRKEY( "LOW" ), LNUMVAL( LOW ) },
+ { LSTRKEY( "FLOAT" ), LNUMVAL( FLOAT ) },
+ { LSTRKEY( "PULLUP" ), LNUMVAL( PULLUP ) },
+#endif
+ { LNILKEY, LNILVAL }
+};
+
+LUALIB_API int ICACHE_FLASH_ATTR luaopen_gpio( lua_State *L )
+{
+#ifdef GPIO_INTERRUPT_ENABLE
+ int i;
+ for(i=0;i 0
+ return 0;
+#else // #if LUA_OPTIMIZE_MEMORY > 0
+ luaL_register( L, AUXLIB_GPIO, gpio_map );
+ // Add constants
+#ifdef GPIO_INTERRUPT_ENABLE
+ MOD_REG_NUMBER( L, "INT", INTERRUPT );
+#endif
+ MOD_REG_NUMBER( L, "OUTPUT", OUTPUT );
+ MOD_REG_NUMBER( L, "INPUT", INPUT );
+ MOD_REG_NUMBER( L, "HIGH", HIGH );
+ MOD_REG_NUMBER( L, "LOW", LOW );
+ MOD_REG_NUMBER( L, "FLOAT", FLOAT );
+ MOD_REG_NUMBER( L, "PULLUP", PULLUP );
+ return 1;
+#endif // #if LUA_OPTIMIZE_MEMORY > 0
+}
diff --git a/app/modules/i2c.c b/app/modules/i2c.c
new file mode 100644
index 00000000..ae8f1fbc
--- /dev/null
+++ b/app/modules/i2c.c
@@ -0,0 +1,181 @@
+// Module for interfacing with the I2C interface
+
+//#include "lua.h"
+#include "lualib.h"
+#include "lauxlib.h"
+#include "platform.h"
+#include "auxmods.h"
+#include "lrotable.h"
+
+// Lua: speed = i2c.setup( id, sda, scl, speed )
+static int ICACHE_FLASH_ATTR i2c_setup( lua_State *L )
+{
+ unsigned id = luaL_checkinteger( L, 1 );
+ unsigned sda = luaL_checkinteger( L, 2 );
+ unsigned scl = luaL_checkinteger( L, 3 );
+
+ MOD_CHECK_ID( i2c, id );
+ MOD_CHECK_ID( gpio, sda );
+ MOD_CHECK_ID( gpio, scl );
+
+ if(scl==0 || sda==0)
+ return luaL_error( L, "no i2c for D0" );
+
+ s32 speed = ( s32 )luaL_checkinteger( L, 4 );
+ if (speed <= 0)
+ return luaL_error( L, "wrong arg range" );
+ lua_pushinteger( L, platform_i2c_setup( id, sda, scl, (u32)speed ) );
+ return 1;
+}
+
+// Lua: i2c.start( id )
+static int ICACHE_FLASH_ATTR i2c_start( lua_State *L )
+{
+ unsigned id = luaL_checkinteger( L, 1 );
+
+ MOD_CHECK_ID( i2c, id );
+ platform_i2c_send_start( id );
+ return 0;
+}
+
+// Lua: i2c.stop( id )
+static int ICACHE_FLASH_ATTR i2c_stop( lua_State *L )
+{
+ unsigned id = luaL_checkinteger( L, 1 );
+
+ MOD_CHECK_ID( i2c, id );
+ platform_i2c_send_stop( id );
+ return 0;
+}
+
+// Lua: status = i2c.address( id, address, direction )
+static int ICACHE_FLASH_ATTR i2c_address( lua_State *L )
+{
+ unsigned id = luaL_checkinteger( L, 1 );
+ int address = luaL_checkinteger( L, 2 );
+ int direction = luaL_checkinteger( L, 3 );
+
+ MOD_CHECK_ID( i2c, id );
+ if ( address < 0 || address > 127 )
+ return luaL_error( L, "wrong arg range" );
+ lua_pushboolean( L, platform_i2c_send_address( id, (u16)address, direction ) );
+ return 1;
+}
+
+// Lua: wrote = i2c.write( id, data1, [data2], ..., [datan] )
+// data can be either a string, a table or an 8-bit number
+static int ICACHE_FLASH_ATTR i2c_write( lua_State *L )
+{
+ unsigned id = luaL_checkinteger( L, 1 );
+ const char *pdata;
+ size_t datalen, i;
+ int numdata;
+ u32 wrote = 0;
+ unsigned argn;
+
+ MOD_CHECK_ID( i2c, id );
+ if( lua_gettop( L ) < 2 )
+ return luaL_error( L, "wrong arg type" );
+ for( argn = 2; argn <= lua_gettop( L ); argn ++ )
+ {
+ // lua_isnumber() would silently convert a string of digits to an integer
+ // whereas here strings are handled separately.
+ if( lua_type( L, argn ) == LUA_TNUMBER )
+ {
+ numdata = ( int )luaL_checkinteger( L, argn );
+ if( numdata < 0 || numdata > 255 )
+ return luaL_error( L, "wrong arg range" );
+ if( platform_i2c_send_byte( id, numdata ) != 1 )
+ break;
+ wrote ++;
+ }
+ else if( lua_istable( L, argn ) )
+ {
+ datalen = lua_objlen( L, argn );
+ for( i = 0; i < datalen; i ++ )
+ {
+ lua_rawgeti( L, argn, i + 1 );
+ numdata = ( int )luaL_checkinteger( L, -1 );
+ lua_pop( L, 1 );
+ if( numdata < 0 || numdata > 255 )
+ return luaL_error( L, "wrong arg range" );
+ if( platform_i2c_send_byte( id, numdata ) == 0 )
+ break;
+ }
+ wrote += i;
+ if( i < datalen )
+ break;
+ }
+ else
+ {
+ pdata = luaL_checklstring( L, argn, &datalen );
+ for( i = 0; i < datalen; i ++ )
+ if( platform_i2c_send_byte( id, pdata[ i ] ) == 0 )
+ break;
+ wrote += i;
+ if( i < datalen )
+ break;
+ }
+ }
+ lua_pushinteger( L, wrote );
+ return 1;
+}
+
+// Lua: read = i2c.read( id, size )
+static int ICACHE_FLASH_ATTR i2c_read( lua_State *L )
+{
+ unsigned id = luaL_checkinteger( L, 1 );
+ u32 size = ( u32 )luaL_checkinteger( L, 2 ), i;
+ luaL_Buffer b;
+ int data;
+
+ MOD_CHECK_ID( i2c, id );
+ if( size == 0 )
+ return 0;
+ luaL_buffinit( L, &b );
+ for( i = 0; i < size; i ++ )
+ if( ( data = platform_i2c_recv_byte( id, i < size - 1 ) ) == -1 )
+ break;
+ else
+ luaL_addchar( &b, ( char )data );
+ luaL_pushresult( &b );
+ return 1;
+}
+
+// Module function map
+#define MIN_OPT_LEVEL 2
+#include "lrodefs.h"
+const LUA_REG_TYPE i2c_map[] =
+{
+ { LSTRKEY( "setup" ), LFUNCVAL( i2c_setup ) },
+ { LSTRKEY( "start" ), LFUNCVAL( i2c_start ) },
+ { LSTRKEY( "stop" ), LFUNCVAL( i2c_stop ) },
+ { LSTRKEY( "address" ), LFUNCVAL( i2c_address ) },
+ { LSTRKEY( "write" ), LFUNCVAL( i2c_write ) },
+ { LSTRKEY( "read" ), LFUNCVAL( i2c_read ) },
+#if LUA_OPTIMIZE_MEMORY > 0
+ // { LSTRKEY( "FAST" ), LNUMVAL( PLATFORM_I2C_SPEED_FAST ) },
+ { LSTRKEY( "SLOW" ), LNUMVAL( PLATFORM_I2C_SPEED_SLOW ) },
+ { LSTRKEY( "TRANSMITTER" ), LNUMVAL( PLATFORM_I2C_DIRECTION_TRANSMITTER ) },
+ { LSTRKEY( "RECEIVER" ), LNUMVAL( PLATFORM_I2C_DIRECTION_RECEIVER ) },
+#endif
+ { LNILKEY, LNILVAL }
+};
+
+LUALIB_API int ICACHE_FLASH_ATTR luaopen_i2c( lua_State *L )
+{
+#if LUA_OPTIMIZE_MEMORY > 0
+ return 0;
+#else // #if LUA_OPTIMIZE_MEMORY > 0
+ luaL_register( L, AUXLIB_I2C, i2c_map );
+
+ // Add the stop bits and parity constants (for i2c.setup)
+ // MOD_REG_NUMBER( L, "FAST", PLATFORM_I2C_SPEED_FAST );
+ MOD_REG_NUMBER( L, "SLOW", PLATFORM_I2C_SPEED_SLOW );
+ MOD_REG_NUMBER( L, "TRANSMITTER", PLATFORM_I2C_DIRECTION_TRANSMITTER );
+ MOD_REG_NUMBER( L, "RECEIVER", PLATFORM_I2C_DIRECTION_RECEIVER );
+
+ return 1;
+#endif // #if LUA_OPTIMIZE_MEMORY > 0
+}
+
diff --git a/app/modules/modules.h b/app/modules/modules.h
new file mode 100644
index 00000000..e58ca748
--- /dev/null
+++ b/app/modules/modules.h
@@ -0,0 +1,119 @@
+/**
+ * External modules library
+ */
+
+#ifndef __MODULES_H__
+#define __MODULES_H__
+
+#if defined(LUA_USE_MODULES_GPIO)
+#define MODULES_GPIO "gpio"
+#define ROM_MODULES_GPIO \
+ _ROM(MODULES_GPIO, luaopen_gpio, gpio_map)
+#else
+#define ROM_MODULES_GPIO
+#endif
+
+#if defined(LUA_USE_MODULES_PWM)
+#define MODULES_PWM "pwm"
+#define ROM_MODULES_PWM \
+ _ROM(MODULES_PWM, luaopen_pwm, pwm_map)
+#else
+#define ROM_MODULES_PWM
+#endif
+
+#if defined(LUA_USE_MODULES_WIFI)
+#define MODULES_WIFI "wifi"
+#define ROM_MODULES_WIFI \
+ _ROM(MODULES_WIFI, luaopen_wifi, wifi_map)
+#else
+#define ROM_MODULES_WIFI
+#endif
+
+#if defined(LUA_USE_MODULES_NET)
+#define MODULES_NET "net"
+#define ROM_MODULES_NET \
+ _ROM(MODULES_NET, luaopen_net, net_map)
+#else
+#define ROM_MODULES_NET
+#endif
+
+#if defined(LUA_USE_MODULES_I2C)
+#define MODULES_I2C "i2c"
+#define ROM_MODULES_I2C \
+ _ROM(MODULES_I2C, luaopen_i2c, i2c_map)
+#else
+#define ROM_MODULES_I2C
+#endif
+
+#if defined(LUA_USE_MODULES_TMR)
+#define MODULES_TMR "tmr"
+#define ROM_MODULES_TMR \
+ _ROM(MODULES_TMR, luaopen_tmr, tmr_map)
+#else
+#define ROM_MODULES_TMR
+#endif
+
+#if defined(LUA_USE_MODULES_NODE)
+#define MODULES_NODE "node"
+#define ROM_MODULES_NODE \
+ _ROM(MODULES_NODE, luaopen_node, node_map)
+#else
+#define ROM_MODULES_NODE
+#endif
+
+#if defined(LUA_USE_MODULES_FILE)
+#define MODULES_FILE "file"
+#define ROM_MODULES_FILE \
+ _ROM(MODULES_FILE, luaopen_file, file_map)
+#else
+#define ROM_MODULES_FILE
+#endif
+
+#if defined(LUA_USE_MODULES_ADC)
+#define MODULES_ADC "adc"
+#define ROM_MODULES_ADC \
+ _ROM(MODULES_ADC, luaopen_adc, adc_map)
+#else
+#define ROM_MODULES_ADC
+#endif
+
+#if defined(LUA_USE_MODULES_UART)
+#define MODULES_UART "uart"
+#define ROM_MODULES_UART \
+ _ROM(MODULES_UART, luaopen_uart, uart_map)
+#else
+#define ROM_MODULES_UART
+#endif
+
+#if defined(LUA_USE_MODULES_OW)
+#define MODULES_OW "ow"
+#define ROM_MODULES_OW \
+ _ROM(MODULES_OW, luaopen_ow, ow_map)
+#else
+#define ROM_MODULES_OW
+#endif
+
+#if defined(LUA_USE_MODULES_BIT)
+#define MODULES_BIT "bit"
+#define ROM_MODULES_BIT \
+ _ROM(MODULES_BIT, luaopen_bit, bit_map)
+#else
+#define ROM_MODULES_BIT
+#endif
+
+#define LUA_MODULES_ROM \
+ ROM_MODULES_GPIO \
+ ROM_MODULES_PWM \
+ ROM_MODULES_WIFI \
+ ROM_MODULES_I2C \
+ ROM_MODULES_TMR \
+ ROM_MODULES_NODE \
+ ROM_MODULES_FILE \
+ ROM_MODULES_NET \
+ ROM_MODULES_ADC \
+ ROM_MODULES_UART \
+ ROM_MODULES_OW \
+ ROM_MODULES_BIT
+
+#endif
+
diff --git a/app/modules/net.c b/app/modules/net.c
new file mode 100644
index 00000000..125b2f62
--- /dev/null
+++ b/app/modules/net.c
@@ -0,0 +1,1359 @@
+// Module for network
+
+//#include "lua.h"
+#include "lualib.h"
+#include "lauxlib.h"
+#include "platform.h"
+#include "auxmods.h"
+#include "lrotable.h"
+
+#include "c_string.h"
+#include "c_stdlib.h"
+
+#include "c_types.h"
+#include "mem.h"
+#include "espconn.h"
+
+#ifdef CLIENT_SSL_ENABLE
+unsigned char *default_certificate;
+unsigned int default_certificate_len = 0;
+unsigned char *default_private_key;
+unsigned int default_private_key_len = 0;
+#endif
+
+#define TCP ESPCONN_TCP
+#define UDP ESPCONN_UDP
+
+static ip_addr_t host_ip; // for dns
+
+#if 0
+static int expose_array(lua_State* L, char *array, unsigned short len);
+#endif
+
+#define MAX_SOCKET 5
+static int socket_num = 0;
+static int socket[MAX_SOCKET];
+static lua_State *gL = NULL;
+static int tcpserver_cb_connect_ref = LUA_NOREF; // for tcp server connected callback
+static uint16_t tcp_server_timeover = 30;
+
+static struct espconn *pTcpServer = NULL;
+static struct espconn *pUdpServer = NULL;
+
+typedef struct lnet_userdata
+{
+ struct espconn *pesp_conn;
+ int self_ref;
+ int cb_connect_ref;
+ int cb_reconnect_ref;
+ int cb_disconnect_ref;
+ int cb_receive_ref;
+ int cb_send_ref;
+ int cb_dns_found_ref;
+#ifdef CLIENT_SSL_ENABLE
+ uint8_t secure;
+#endif
+}lnet_userdata;
+
+static void ICACHE_FLASH_ATTR
+net_server_disconnected(void *arg) // for tcp server only
+{
+ NODE_DBG("net_server_disconnected is called.\n");
+ struct espconn *pesp_conn = arg;
+ if(pesp_conn == NULL)
+ return;
+ lnet_userdata *nud = (lnet_userdata *)pesp_conn->reverse;
+ if(nud == NULL)
+ return;
+ if(gL == NULL)
+ return;
+#if 0
+ char temp[20] = {0};
+ c_sprintf(temp, IPSTR, IP2STR( &(pesp_conn->proto.tcp->remote_ip) ) );
+ NODE_DBG("remote ");
+ NODE_DBG(temp);
+ NODE_DBG(":");
+ NODE_DBG("%d",pesp_conn->proto.tcp->remote_port);
+ NODE_DBG(" disconnected.\n");
+#endif
+ if(nud->cb_disconnect_ref != LUA_NOREF && nud->self_ref != LUA_NOREF)
+ {
+ lua_rawgeti(gL, LUA_REGISTRYINDEX, nud->cb_disconnect_ref);
+ lua_rawgeti(gL, LUA_REGISTRYINDEX, nud->self_ref); // pass the userdata(client) to callback func in lua
+ lua_call(gL, 1, 0);
+ }
+ int i;
+ lua_gc(gL, LUA_GCSTOP, 0);
+ for(i=0;iself_ref) ){
+ // found the saved client
+ nud->pesp_conn->reverse = NULL;
+ nud->pesp_conn = NULL; // the espconn is made by low level sdk, do not need to free, delete() will not free it.
+ nud->self_ref = LUA_NOREF; // unref this, and the net.socket userdata will delete it self
+ luaL_unref(gL, LUA_REGISTRYINDEX, socket[i]);
+ socket[i] = LUA_NOREF;
+ socket_num--;
+ break;
+ }
+ }
+ lua_gc(gL, LUA_GCRESTART, 0);
+}
+
+static void ICACHE_FLASH_ATTR
+net_socket_disconnected(void *arg) // tcp only
+{
+ NODE_DBG("net_socket_disconnected is called.\n");
+ struct espconn *pesp_conn = arg;
+ if(pesp_conn == NULL)
+ return;
+ lnet_userdata *nud = (lnet_userdata *)pesp_conn->reverse;
+ if(nud == NULL)
+ return;
+ if(nud->cb_disconnect_ref != LUA_NOREF && nud->self_ref != LUA_NOREF)
+ {
+ lua_rawgeti(gL, LUA_REGISTRYINDEX, nud->cb_disconnect_ref);
+ lua_rawgeti(gL, LUA_REGISTRYINDEX, nud->self_ref); // pass the userdata(client) to callback func in lua
+ lua_call(gL, 1, 0);
+ }
+
+ if(pesp_conn->proto.tcp)
+ c_free(pesp_conn->proto.tcp);
+ pesp_conn->proto.tcp = NULL;
+ if(nud->pesp_conn)
+ c_free(nud->pesp_conn);
+ nud->pesp_conn = NULL; // espconn is already disconnected
+ 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);
+}
+
+static void ICACHE_FLASH_ATTR
+net_server_reconnected(void *arg, sint8_t err)
+{
+ NODE_DBG("net_server_reconnected is called.\n");
+ net_server_disconnected(arg);
+}
+
+static void ICACHE_FLASH_ATTR
+net_socket_reconnected(void *arg, sint8_t err)
+{
+ NODE_DBG("net_socket_reconnected is called.\n");
+ net_socket_disconnected(arg);
+}
+
+static void ICACHE_FLASH_ATTR
+net_socket_received(void *arg, char *pdata, unsigned short len)
+{
+ NODE_DBG("net_socket_received is called.\n");
+ struct espconn *pesp_conn = arg;
+ if(pesp_conn == NULL)
+ return;
+ lnet_userdata *nud = (lnet_userdata *)pesp_conn->reverse;
+ if(nud == NULL)
+ return;
+ if(nud->cb_receive_ref == LUA_NOREF)
+ return;
+ if(nud->self_ref == LUA_NOREF)
+ return;
+ lua_rawgeti(gL, LUA_REGISTRYINDEX, nud->cb_receive_ref);
+ lua_rawgeti(gL, LUA_REGISTRYINDEX, nud->self_ref); // pass the userdata(server) to callback func in lua
+ // expose_array(gL, pdata, len);
+ // *(pdata+len) = 0;
+ // NODE_DBG(pdata);
+ // NODE_DBG("\n");
+ lua_pushlstring(gL, pdata, len);
+ // lua_pushinteger(gL, len);
+ lua_call(gL, 2, 0);
+}
+
+static void ICACHE_FLASH_ATTR
+net_socket_sent(void *arg)
+{
+ // NODE_DBG("net_socket_sent is called.\n");
+ struct espconn *pesp_conn = arg;
+ if(pesp_conn == NULL)
+ return;
+ lnet_userdata *nud = (lnet_userdata *)pesp_conn->reverse;
+ if(nud == NULL)
+ return;
+ if(nud->cb_send_ref == LUA_NOREF)
+ return;
+ if(nud->self_ref == LUA_NOREF)
+ return;
+ lua_rawgeti(gL, LUA_REGISTRYINDEX, nud->cb_send_ref);
+ lua_rawgeti(gL, LUA_REGISTRYINDEX, nud->self_ref); // pass the userdata(server) to callback func in lua
+ lua_call(gL, 1, 0);
+}
+
+static void ICACHE_FLASH_ATTR
+net_dns_found(const char *name, ip_addr_t *ipaddr, void *arg)
+{
+ NODE_DBG("net_dns_found is called.\n");
+ struct espconn *pesp_conn = arg;
+ if(pesp_conn == NULL){
+ NODE_DBG("pesp_conn null.\n");
+ return;
+ }
+ lnet_userdata *nud = (lnet_userdata *)pesp_conn->reverse;
+ if(nud == NULL){
+ NODE_DBG("nud null.\n");
+ return;
+ }
+ if(nud->cb_dns_found_ref == LUA_NOREF){
+ NODE_DBG("cb_dns_found_ref null.\n");
+ return;
+ }
+
+ if(ipaddr == NULL)
+ {
+ NODE_ERR( "DNS Fail!\n" );
+ return;
+ }
+
+ // ipaddr->addr is a uint32_t ip
+ char ip_str[20];
+ c_memset(ip_str, 0, sizeof(ip_str));
+ if(host_ip.addr == 0 && ipaddr->addr != 0)
+ {
+ 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);
+
+ 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);
+ 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);
+ }
+}
+
+static void ICACHE_FLASH_ATTR
+net_server_connected(void *arg) // for tcp only
+{
+ NODE_DBG("net_server_connected is called.\n");
+ struct espconn *pesp_conn = arg;
+ int i = 0;
+ lnet_userdata *skt = NULL;
+ if(pesp_conn == NULL)
+ return;
+
+#if 0
+ char temp[20] = {0};
+ c_sprintf(temp, IPSTR, IP2STR( &(pesp_conn->proto.tcp->remote_ip) ) );
+ NODE_DBG("remote ");
+ NODE_DBG(temp);
+ NODE_DBG(":");
+ NODE_DBG("%d",pesp_conn->proto.tcp->remote_port);
+ NODE_DBG(" connected.\n");
+#endif
+
+ for(i=0;i=MAX_SOCKET) // can't create more socket
+ {
+ NODE_ERR("MAX_SOCKET\n");
+ pesp_conn->reverse = NULL; // not accept this conn
+ if(pesp_conn->proto.tcp->remote_port || pesp_conn->proto.tcp->local_port)
+ espconn_disconnect(pesp_conn);
+ return;
+ }
+
+ if(tcpserver_cb_connect_ref == LUA_NOREF)
+ return;
+ if(!gL)
+ return;
+
+ lua_rawgeti(gL, LUA_REGISTRYINDEX, tcpserver_cb_connect_ref); // get function
+ // create a new client object
+ skt = (lnet_userdata *)lua_newuserdata(gL, sizeof(lnet_userdata));
+
+ if(!skt){
+ NODE_ERR("can't newudata\n");
+ lua_pop(gL, 1);
+ return;
+ }
+ // set its metatable
+ luaL_getmetatable(gL, "net.socket");
+ lua_setmetatable(gL, -2);
+ // pre-initialize it, in case of errors
+ skt->self_ref = LUA_NOREF;
+ lua_pushvalue(gL, -1); // copy the top of stack
+ skt->self_ref = luaL_ref(gL, LUA_REGISTRYINDEX); // ref to it self, for module api to find the userdata
+ socket[i] = skt->self_ref; // save to socket array
+ socket_num++;
+ skt->cb_connect_ref = LUA_NOREF; // this socket already connected
+ skt->cb_reconnect_ref = LUA_NOREF;
+ skt->cb_disconnect_ref = LUA_NOREF;
+
+ skt->cb_receive_ref = LUA_NOREF;
+ skt->cb_send_ref = LUA_NOREF;
+ skt->cb_dns_found_ref = LUA_NOREF;
+
+#ifdef CLIENT_SSL_ENABLE
+ skt->secure = 0; // as a server SSL is not supported.
+#endif
+
+ skt->pesp_conn = pesp_conn; // point to the espconn made by low level sdk
+ pesp_conn->reverse = skt; // let espcon carray the info of this userdata(net.socket)
+
+ espconn_regist_recvcb(pesp_conn, net_socket_received);
+ espconn_regist_sentcb(pesp_conn, net_socket_sent);
+ espconn_regist_disconcb(pesp_conn, net_server_disconnected);
+ espconn_regist_reconcb(pesp_conn, net_server_reconnected);
+
+ // now socket[i] has the client ref, and stack top has the userdata
+ lua_call(gL, 1, 0); // function(conn)
+}
+
+static void ICACHE_FLASH_ATTR
+net_socket_connected(void *arg)
+{
+ NODE_DBG("net_socket_connected is called.\n");
+ struct espconn *pesp_conn = arg;
+ if(pesp_conn == NULL)
+ return;
+ lnet_userdata *nud = (lnet_userdata *)pesp_conn->reverse;
+ if(nud == NULL)
+ return;
+ // can receive and send data, even if there is no connected callback in lua.
+ espconn_regist_recvcb(pesp_conn, net_socket_received);
+ espconn_regist_sentcb(pesp_conn, net_socket_sent);
+ espconn_regist_disconcb(pesp_conn, net_socket_disconnected);
+
+ if(nud->cb_connect_ref == LUA_NOREF)
+ return;
+ if(nud->self_ref == LUA_NOREF)
+ return;
+ lua_rawgeti(gL, LUA_REGISTRYINDEX, nud->cb_connect_ref);
+ lua_rawgeti(gL, LUA_REGISTRYINDEX, nud->self_ref); // pass the userdata(client) to callback func in lua
+ lua_call(gL, 1, 0);
+}
+
+// Lua: s = net.create(type, secure/timeout, function(conn))
+static int ICACHE_FLASH_ATTR
+net_create( lua_State* L, const char* mt )
+{
+ NODE_DBG("net_create is called.\n");
+ struct espconn *pesp_conn = NULL;
+ lnet_userdata *nud, *temp = NULL;
+ unsigned type;
+#ifdef CLIENT_SSL_ENABLE
+ unsigned secure = 0;
+#endif
+ uint8_t stack = 1;
+ bool isserver = false;
+
+ if (mt!=NULL && c_strcmp(mt, "net.server")==0)
+ isserver = true;
+ else if (mt!=NULL && c_strcmp(mt, "net.socket")==0)
+ isserver = false;
+ else
+ {
+ NODE_DBG("wrong metatable for net_create.\n");
+ return 0;
+ }
+
+ type = luaL_checkinteger( L, stack );
+ if ( type != ESPCONN_TCP && type != ESPCONN_UDP )
+ return luaL_error( L, "wrong arg type" );
+ stack++;
+#ifdef CLIENT_SSL_ENABLE
+ if(!isserver){
+ if ( lua_isnumber(L, stack) )
+ {
+ secure = lua_tointeger(L, stack);
+ stack++;
+ if ( secure != 0 && secure != 1 ){
+ return luaL_error( L, "wrong arg type" );
+ }
+ } else {
+ secure = 0; // default to 0
+ }
+ }
+#endif
+
+ if(isserver && type == ESPCONN_TCP){
+ if ( lua_isnumber(L, stack) )
+ {
+ unsigned to = lua_tointeger(L, stack);
+ stack++;
+ if ( to < 1 || to > 28800 ){
+ return luaL_error( L, "wrong arg type" );
+ }
+ tcp_server_timeover = (uint16_t)to;
+ } else {
+ tcp_server_timeover = 30; // default to 30
+ }
+ }
+
+ // create a object
+ nud = (lnet_userdata *)lua_newuserdata(L, sizeof(lnet_userdata));
+ // pre-initialize it, in case of errors
+ nud->self_ref = LUA_NOREF;
+ nud->cb_connect_ref = LUA_NOREF;
+ nud->cb_reconnect_ref = LUA_NOREF;
+ nud->cb_disconnect_ref = LUA_NOREF;
+ nud->cb_receive_ref = LUA_NOREF;
+ nud->cb_send_ref = LUA_NOREF;
+ nud->cb_dns_found_ref = LUA_NOREF;
+ nud->pesp_conn = NULL;
+#ifdef CLIENT_SSL_ENABLE
+ nud->secure = secure;
+#endif
+
+ // set its metatable
+ luaL_getmetatable(L, mt);
+ lua_setmetatable(L, -2);
+
+ // create the espconn struct
+ if(isserver && type==ESPCONN_TCP && pTcpServer){
+ if(tcpserver_cb_connect_ref != LUA_NOREF){ // self_ref should be unref in close()
+ lua_pop(L,1);
+ return luaL_error(L, "only one tcp server allowed");
+ }
+ pesp_conn = nud->pesp_conn = pTcpServer;
+ } else if(isserver && type==ESPCONN_UDP && pUdpServer){
+ temp = (lnet_userdata *)pUdpServer->reverse;
+ if(temp && temp->self_ref != LUA_NOREF){
+ lua_pop(L,1);
+ return luaL_error(L, "only one udp server allowed");
+ }
+ pesp_conn = nud->pesp_conn = pUdpServer;
+ } else {
+ pesp_conn = nud->pesp_conn = (struct espconn *)c_zalloc(sizeof(struct espconn));
+ if(!pesp_conn)
+ return luaL_error(L, "not enough memory");
+
+ pesp_conn->proto.tcp = NULL;
+ pesp_conn->proto.udp = NULL;
+ pesp_conn->reverse = NULL;
+ if( type==ESPCONN_TCP )
+ {
+ pesp_conn->proto.tcp = (esp_tcp *)c_zalloc(sizeof(esp_tcp));
+ if(!pesp_conn->proto.tcp){
+ c_free(pesp_conn);
+ pesp_conn = nud->pesp_conn = NULL;
+ return luaL_error(L, "not enough memory");
+ }
+ NODE_DBG("TCP server/socket is set.\n");
+ }
+ else if( type==ESPCONN_UDP )
+ {
+ pesp_conn->proto.udp = (esp_udp *)c_zalloc(sizeof(esp_udp));
+ if(!pesp_conn->proto.udp){
+ c_free(pesp_conn);
+ pesp_conn = nud->pesp_conn = NULL;
+ return luaL_error(L, "not enough memory");
+ }
+ NODE_DBG("UDP server/socket is set.\n");
+ }
+ }
+ pesp_conn->type = type;
+ pesp_conn->state = ESPCONN_NONE;
+ // reverse is for the callback function
+ pesp_conn->reverse = nud;
+
+ if(isserver && type==ESPCONN_TCP && pTcpServer==NULL){
+ pTcpServer = pesp_conn;
+ } else if(isserver && type==ESPCONN_UDP && pUdpServer==NULL){
+ pUdpServer = pesp_conn;
+ }
+
+ gL = L; // global L for net module.
+
+ // if call back function is specified, call it with para userdata
+ // luaL_checkanyfunction(L, 2);
+ if (lua_type(L, stack) == LUA_TFUNCTION || lua_type(L, stack) == LUA_TLIGHTFUNCTION){
+ lua_pushvalue(L, stack); // copy argument (func) to the top of stack
+ lua_pushvalue(L, -2); // copy the self_ref(userdata) to the top
+ lua_call(L, 1, 0);
+ }
+
+ return 1;
+}
+
+// static int net_close( lua_State* L, const char* mt );
+// Lua: net.delete( socket/server )
+// call close() first
+// server: disconnect server, unref everything
+// socket: unref everything
+static int ICACHE_FLASH_ATTR
+net_delete( lua_State* L, const char* mt )
+{
+ NODE_DBG("net_delete is called.\n");
+ bool isserver = false;
+ if (mt!=NULL && c_strcmp(mt, "net.server")==0)
+ isserver = true;
+ else if (mt!=NULL && c_strcmp(mt, "net.socket")==0)
+ isserver = false;
+ else
+ {
+ NODE_DBG("wrong metatable for net_delete.\n");
+ return 0;
+ }
+
+ // net_close( L, mt ); // close it first
+
+ lnet_userdata *nud = (lnet_userdata *)luaL_checkudata(L, 1, mt);
+ luaL_argcheck(L, nud, 1, "Server/Socket expected");
+ if(nud==NULL){
+ NODE_DBG("userdata is nil.\n");
+ return 0;
+ }
+ if(nud->pesp_conn){ // for client connected to tcp server, this should set NULL in disconnect cb
+ nud->pesp_conn->reverse = NULL;
+ if(!isserver) // socket is freed here
+ {
+ if(nud->pesp_conn->type == ESPCONN_UDP){
+ if(nud->pesp_conn->proto.udp)
+ c_free(nud->pesp_conn->proto.udp);
+ nud->pesp_conn->proto.udp = NULL;
+ } else if (nud->pesp_conn->type == ESPCONN_TCP) {
+ if(nud->pesp_conn->proto.tcp)
+ c_free(nud->pesp_conn->proto.tcp);
+ nud->pesp_conn->proto.tcp = NULL;
+ }
+ c_free(nud->pesp_conn);
+ }
+ nud->pesp_conn = NULL; // for socket, it will free this when disconnected
+ }
+
+ // free (unref) callback ref
+ if(LUA_NOREF!=nud->cb_connect_ref){
+ luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_connect_ref);
+ nud->cb_connect_ref = LUA_NOREF;
+ }
+ if(LUA_NOREF!=nud->cb_reconnect_ref){
+ luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_reconnect_ref);
+ nud->cb_reconnect_ref = LUA_NOREF;
+ }
+ if(LUA_NOREF!=nud->cb_disconnect_ref){
+ luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_disconnect_ref);
+ nud->cb_disconnect_ref = LUA_NOREF;
+ }
+ if(LUA_NOREF!=nud->cb_receive_ref){
+ luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_receive_ref);
+ nud->cb_receive_ref = LUA_NOREF;
+ }
+ if(LUA_NOREF!=nud->cb_send_ref){
+ luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_send_ref);
+ nud->cb_send_ref = LUA_NOREF;
+ }
+ if(LUA_NOREF!=nud->cb_dns_found_ref){
+ luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_dns_found_ref);
+ nud->cb_dns_found_ref = LUA_NOREF;
+ }
+ lua_gc(gL, LUA_GCSTOP, 0);
+ if(LUA_NOREF!=nud->self_ref){
+ luaL_unref(L, LUA_REGISTRYINDEX, nud->self_ref);
+ nud->self_ref = LUA_NOREF;
+ }
+ lua_gc(gL, LUA_GCRESTART, 0);
+ return 0;
+}
+
+static void ICACHE_FLASH_ATTR socket_connect(struct espconn *pesp_conn)
+{
+ if(pesp_conn == NULL)
+ return;
+ lnet_userdata *nud = (lnet_userdata *)pesp_conn->reverse;
+ if(nud == NULL)
+ return;
+
+ if( pesp_conn->type == ESPCONN_TCP )
+ {
+#ifdef CLIENT_SSL_ENABLE
+ if(nud->secure){
+ espconn_secure_connect(pesp_conn);
+ }
+ else
+#endif
+ {
+ espconn_connect(pesp_conn);
+ }
+ }
+ else if (pesp_conn->type == ESPCONN_UDP)
+ {
+ espconn_create(pesp_conn);
+ }
+ NODE_DBG("socket_connect is called.\n");
+}
+
+static void socket_dns_found(const char *name, ip_addr_t *ipaddr, void *arg);
+static dns_reconn_count = 0;
+static void ICACHE_FLASH_ATTR
+socket_dns_found(const char *name, ip_addr_t *ipaddr, void *arg)
+{
+ NODE_DBG("socket_dns_found is called.\n");
+ struct espconn *pesp_conn = arg;
+ if(pesp_conn == NULL){
+ NODE_DBG("pesp_conn null.\n");
+ return;
+ }
+
+ if(ipaddr == NULL)
+ {
+ dns_reconn_count++;
+ if( dns_reconn_count >= 5 ){
+ NODE_ERR( "DNS Fail!\n" );
+ return;
+ }
+ NODE_ERR( "DNS retry %d!\n", dns_reconn_count );
+ host_ip.addr = 0;
+ espconn_gethostbyname(pesp_conn, name, &host_ip, socket_dns_found);
+ return;
+ }
+
+ // ipaddr->addr is a uint32_t ip
+ if(ipaddr->addr != 0)
+ {
+ dns_reconn_count = 0;
+ if( pesp_conn->type == ESPCONN_TCP )
+ {
+ c_memcpy(pesp_conn->proto.tcp->remote_ip, &(ipaddr->addr), 4);
+ NODE_DBG("TCP ip is set: ");
+ NODE_DBG(IPSTR, IP2STR(&(ipaddr->addr)));
+ NODE_DBG("\n");
+ }
+ else if (pesp_conn->type == ESPCONN_UDP)
+ {
+ c_memcpy(pesp_conn->proto.udp->remote_ip, &(ipaddr->addr), 4);
+ NODE_DBG("UDP ip is set: ");
+ NODE_DBG(IPSTR, IP2STR(&(ipaddr->addr)));
+ NODE_DBG("\n");
+ }
+ socket_connect(pesp_conn);
+ }
+}
+
+// Lua: server:listen( port, ip, function(con) )
+// Lua: socket:connect( port, ip, function(con) )
+static int ICACHE_FLASH_ATTR
+net_start( lua_State* L, const char* mt )
+{
+ NODE_DBG("net_start is called.\n");
+ struct espconn *pesp_conn = NULL;
+ lnet_userdata *nud;
+ unsigned port;
+ size_t il;
+ bool isserver = false;
+ ip_addr_t ipaddr;
+ const char *domain;
+ uint8_t stack = 1;
+
+ if (mt!=NULL && c_strcmp(mt, "net.server")==0)
+ isserver = true;
+ else if (mt!=NULL && c_strcmp(mt, "net.socket")==0)
+ isserver = false;
+ else
+ {
+ NODE_DBG("wrong metatable for net_start.\n");
+ return 0;
+ }
+ nud = (lnet_userdata *)luaL_checkudata(L, stack, mt);
+ luaL_argcheck(L, nud, stack, "Server/Socket expected");
+ stack++;
+
+ if(nud==NULL){
+ NODE_DBG("userdata is nil.\n");
+ return 0;
+ }
+
+ pesp_conn = nud->pesp_conn;
+ port = luaL_checkinteger( L, stack );
+ stack++;
+ if( pesp_conn->type == ESPCONN_TCP )
+ {
+ if(isserver)
+ pesp_conn->proto.tcp->local_port = port;
+ else
+ pesp_conn->proto.tcp->remote_port = port;
+ NODE_DBG("TCP port is set: %d.\n", port);
+ }
+ else if (pesp_conn->type == ESPCONN_UDP)
+ {
+ if(isserver)
+ pesp_conn->proto.udp->local_port = port;
+ else
+ pesp_conn->proto.udp->remote_port = port;
+ NODE_DBG("UDP port is set: %d.\n", port);
+ }
+
+ if( lua_isstring(L,stack) ) // deal with the domain string
+ {
+ domain = luaL_checklstring( L, stack, &il );
+ stack++;
+ if (domain == NULL)
+ {
+ if(isserver)
+ domain = "0.0.0.0";
+ else
+ domain = "127.0.0.1";
+ }
+ ipaddr.addr = ipaddr_addr(domain);
+ if( pesp_conn->type == ESPCONN_TCP )
+ {
+ if(isserver)
+ c_memcpy(pesp_conn->proto.tcp->local_ip, &ipaddr.addr, 4);
+ else
+ c_memcpy(pesp_conn->proto.tcp->remote_ip, &ipaddr.addr, 4);
+ NODE_DBG("TCP ip is set: ");
+ NODE_DBG(IPSTR, IP2STR(&ipaddr.addr));
+ NODE_DBG("\n");
+ }
+ else if (pesp_conn->type == ESPCONN_UDP)
+ {
+ if(isserver)
+ c_memcpy(pesp_conn->proto.udp->local_ip, &ipaddr.addr, 4);
+ else
+ c_memcpy(pesp_conn->proto.udp->remote_ip, &ipaddr.addr, 4);
+ NODE_DBG("UDP ip is set: ");
+ NODE_DBG(IPSTR, IP2STR(&ipaddr.addr));
+ NODE_DBG("\n");
+ }
+ }
+
+ // call back function when a connection is obtained, tcp only
+ if ( pesp_conn->type == ESPCONN_TCP ) {
+ if (lua_type(L, stack) == LUA_TFUNCTION || lua_type(L, stack) == LUA_TLIGHTFUNCTION){
+ lua_pushvalue(L, stack); // copy argument (func) to the top of stack
+ if(isserver) // for tcp server connected callback
+ {
+ if(tcpserver_cb_connect_ref != LUA_NOREF)
+ luaL_unref(L, LUA_REGISTRYINDEX, tcpserver_cb_connect_ref);
+ tcpserver_cb_connect_ref = luaL_ref(L, LUA_REGISTRYINDEX);
+ }
+ else
+ {
+ if(nud->cb_connect_ref != LUA_NOREF)
+ luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_connect_ref);
+ nud->cb_connect_ref = luaL_ref(L, LUA_REGISTRYINDEX);
+ }
+ }
+ }
+
+ if(!isserver || pesp_conn->type == ESPCONN_UDP){ // self_ref is only needed by socket userdata, or udp server
+ lua_pushvalue(L, 1); // copy to the top of stack
+ if(nud->self_ref != LUA_NOREF)
+ luaL_unref(L, LUA_REGISTRYINDEX, nud->self_ref);
+ nud->self_ref = luaL_ref(L, LUA_REGISTRYINDEX);
+ }
+
+ if( pesp_conn->type == ESPCONN_TCP )
+ {
+ if(isserver){ // no secure server support for now
+ espconn_regist_connectcb(pesp_conn, net_server_connected);
+ // tcp server, SSL is not supported
+#ifdef CLIENT_SSL_ENABLE
+ // if(nud->secure)
+ // espconn_secure_accept(pesp_conn);
+ // else
+#endif
+ espconn_accept(pesp_conn); // if it's a server, no need to dns.
+ espconn_regist_time(pesp_conn, tcp_server_timeover, 0);
+ }
+ else{
+ espconn_regist_connectcb(pesp_conn, net_socket_connected);
+ espconn_regist_reconcb(pesp_conn, net_socket_reconnected);
+#ifdef CLIENT_SSL_ENABLE
+ if(nud->secure){
+ if(pesp_conn->proto.tcp->remote_port || pesp_conn->proto.tcp->local_port)
+ espconn_secure_disconnect(pesp_conn);
+ // espconn_secure_connect(pesp_conn);
+ }
+ else
+#endif
+ {
+ if(pesp_conn->proto.tcp->remote_port || pesp_conn->proto.tcp->local_port)
+ espconn_disconnect(pesp_conn);
+ // espconn_connect(pesp_conn);
+ }
+ }
+ }
+ else if (pesp_conn->type == ESPCONN_UDP)
+ {
+ espconn_regist_recvcb(pesp_conn, net_socket_received);
+ espconn_regist_sentcb(pesp_conn, net_socket_sent);
+ if(pesp_conn->proto.tcp->remote_port || pesp_conn->proto.tcp->local_port)
+ espconn_delete(pesp_conn);
+ if(isserver)
+ espconn_create(pesp_conn); // if it's a server, no need to dns.
+ }
+
+ if(!isserver){
+ if((ipaddr.addr == IPADDR_NONE) && (c_memcmp(domain,"255.255.255.255",16) != 0))
+ {
+ host_ip.addr = 0;
+ dns_reconn_count = 0;
+ if(ESPCONN_OK == espconn_gethostbyname(pesp_conn, domain, &host_ip, socket_dns_found)){
+ socket_dns_found(domain, &host_ip, pesp_conn); // ip is returned in host_ip.
+ }
+ }
+ else
+ {
+ socket_connect(pesp_conn);
+ }
+ }
+ return 0;
+}
+
+// Lua: server/socket:close()
+// server disconnect everything, unref everything
+// client disconnect and unref itself
+static int ICACHE_FLASH_ATTR
+net_close( lua_State* L, const char* mt )
+{
+ NODE_DBG("net_close is called.\n");
+ bool isserver = false;
+ int i = 0;
+ lnet_userdata *nud = NULL, *skt = NULL;
+
+ nud = (lnet_userdata *)luaL_checkudata(L, 1, mt);
+ luaL_argcheck(L, nud, 1, "Server/Socket expected");
+ if(nud == NULL)
+ return 0;
+
+ if(nud->pesp_conn == NULL)
+ return 0;
+
+ if (mt!=NULL && c_strcmp(mt, "net.server")==0)
+ isserver = true;
+ else if (mt!=NULL && c_strcmp(mt, "net.socket")==0)
+ isserver = false;
+ else
+ {
+ NODE_DBG("wrong metatable for net_close.\n");
+ return 0;
+ }
+
+ if(isserver && nud->pesp_conn->type == ESPCONN_TCP && tcpserver_cb_connect_ref != LUA_NOREF){
+ luaL_unref(L, LUA_REGISTRYINDEX, tcpserver_cb_connect_ref);
+ tcpserver_cb_connect_ref = LUA_NOREF;
+ }
+
+ int n = lua_gettop(L);
+ skt = nud;
+
+ do{
+ if(isserver && skt == NULL){
+ if(socket[i] != LUA_NOREF){ // there is client socket exists
+ lua_rawgeti(L, LUA_REGISTRYINDEX, socket[i]); // get the referenced user_data to stack top
+#if 0
+ socket[i] = LUA_NOREF;
+ socket_num--;
+#endif // do this in net_server_disconnected
+ i++;
+ if(lua_isuserdata(L,-1)){
+ skt = lua_touserdata(L,-1);
+ } else {
+ lua_pop(L, 1);
+ continue;
+ }
+ }else{
+ // skip LUA_NOREF
+ i++;
+ continue;
+ }
+ }
+
+ if(skt==NULL){
+ NODE_DBG("userdata is nil.\n");
+ continue;
+ }
+
+ if(skt->pesp_conn) // disconnect the connection
+ {
+ if(skt->pesp_conn->type == ESPCONN_TCP)
+ {
+ #ifdef CLIENT_SSL_ENABLE
+ if(skt->secure){
+ if(skt->pesp_conn->proto.tcp->remote_port || skt->pesp_conn->proto.tcp->local_port)
+ espconn_secure_disconnect(skt->pesp_conn);
+ }
+ else
+ #endif
+ {
+ if(skt->pesp_conn->proto.tcp->remote_port || skt->pesp_conn->proto.tcp->local_port)
+ espconn_disconnect(skt->pesp_conn);
+ }
+ }else if(skt->pesp_conn->type == ESPCONN_UDP)
+ {
+ if(skt->pesp_conn->proto.tcp->remote_port || skt->pesp_conn->proto.tcp->local_port)
+ espconn_delete(skt->pesp_conn);
+
+ // a udp server/socket unref it self here. not in disconnect.
+ if(LUA_NOREF!=skt->self_ref){ // for a udp self_ref is NOREF
+ luaL_unref(L, LUA_REGISTRYINDEX, skt->self_ref);
+ skt->self_ref = LUA_NOREF; // for a socket, now only var in lua is ref to the userdata
+ }
+ }
+ }
+#if 0
+ // unref the self_ref
+ if(LUA_NOREF!=skt->self_ref){ // for a server self_ref is NOREF
+ luaL_unref(L, LUA_REGISTRYINDEX, skt->self_ref);
+ skt->self_ref = LUA_NOREF; // for a socket, now only var in lua is ref to the userdata
+ }
+#endif
+ lua_settop(L, n); // reset the stack top
+ skt = NULL;
+ } while( isserver && iself_ref){ // for a server self_ref is NOREF
+ luaL_unref(L, LUA_REGISTRYINDEX, nud->self_ref);
+ nud->self_ref = LUA_NOREF; // now only var in lua is ref to the userdata
+ }
+#endif
+
+ return 0;
+}
+
+// Lua: socket/udpserver:on( "method", function(s) )
+static int ICACHE_FLASH_ATTR
+net_on( lua_State* L, const char* mt )
+{
+ NODE_DBG("net_on is called.\n");
+ bool isserver = false;
+ lnet_userdata *nud;
+ size_t sl;
+
+ nud = (lnet_userdata *)luaL_checkudata(L, 1, mt);
+ luaL_argcheck(L, nud, 1, "Server/Socket expected");
+ if(nud==NULL){
+ NODE_DBG("userdata is nil.\n");
+ return 0;
+ }
+
+ if (mt!=NULL && c_strcmp(mt, "net.server")==0)
+ isserver = true;
+ else if (mt!=NULL && c_strcmp(mt, "net.socket")==0)
+ isserver = false;
+ else
+ {
+ NODE_DBG("wrong metatable for net_on.\n");
+ return 0;
+ }
+
+ const char *method = luaL_checklstring( L, 2, &sl );
+ if (method == NULL)
+ return luaL_error( L, "wrong arg type" );
+
+ luaL_checkanyfunction(L, 3);
+ lua_pushvalue(L, 3); // copy argument (func) to the top of stack
+
+ if(!isserver && nud->pesp_conn->type == ESPCONN_TCP && sl == 10 && c_strcmp(method, "connection") == 0){
+ if(nud->cb_connect_ref != LUA_NOREF)
+ luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_connect_ref);
+ nud->cb_connect_ref = luaL_ref(L, LUA_REGISTRYINDEX);
+ }else if(!isserver && nud->pesp_conn->type == ESPCONN_TCP && sl == 12 && c_strcmp(method, "reconnection") == 0){
+ if(nud->cb_reconnect_ref != LUA_NOREF)
+ luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_reconnect_ref);
+ nud->cb_reconnect_ref = luaL_ref(L, LUA_REGISTRYINDEX);
+ }else if(!isserver && nud->pesp_conn->type == ESPCONN_TCP && sl == 13 && c_strcmp(method, "disconnection") == 0){
+ if(nud->cb_disconnect_ref != LUA_NOREF)
+ luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_disconnect_ref);
+ nud->cb_disconnect_ref = luaL_ref(L, LUA_REGISTRYINDEX);
+ }else if((!isserver || nud->pesp_conn->type == ESPCONN_UDP) && sl == 7 && c_strcmp(method, "receive") == 0){
+ if(nud->cb_receive_ref != LUA_NOREF)
+ luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_receive_ref);
+ nud->cb_receive_ref = luaL_ref(L, LUA_REGISTRYINDEX);
+ }else if((!isserver || nud->pesp_conn->type == ESPCONN_UDP) && sl == 4 && c_strcmp(method, "sent") == 0){
+ if(nud->cb_send_ref != LUA_NOREF)
+ luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_send_ref);
+ nud->cb_send_ref = luaL_ref(L, LUA_REGISTRYINDEX);
+ }else if(!isserver && nud->pesp_conn->type == ESPCONN_TCP && sl == 3 && c_strcmp(method, "dns") == 0){
+ if(nud->cb_dns_found_ref != LUA_NOREF)
+ luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_dns_found_ref);
+ nud->cb_dns_found_ref = luaL_ref(L, LUA_REGISTRYINDEX);
+ }else{
+ lua_pop(L, 1);
+ return luaL_error( L, "method not supported" );
+ }
+
+ return 0;
+}
+
+// Lua: server/socket:send( string, function(sent) )
+static int ICACHE_FLASH_ATTR
+net_send( lua_State* L, const char* mt )
+{
+ // NODE_DBG("net_send is called.\n");
+ bool isserver = false;
+ struct espconn *pesp_conn = NULL;
+ lnet_userdata *nud;
+ size_t l;
+
+ nud = (lnet_userdata *)luaL_checkudata(L, 1, mt);
+ luaL_argcheck(L, nud, 1, "Server/Socket expected");
+ if(nud==NULL){
+ NODE_DBG("userdata is nil.\n");
+ return 0;
+ }
+
+ if(nud->pesp_conn == NULL){
+ NODE_DBG("nud->pesp_conn is NULL.\n");
+ return 0;
+ }
+ pesp_conn = nud->pesp_conn;
+
+ if (mt!=NULL && c_strcmp(mt, "net.server")==0)
+ isserver = true;
+ else if (mt!=NULL && c_strcmp(mt, "net.socket")==0)
+ isserver = false;
+ else
+ {
+ NODE_DBG("wrong metatable for net_send.\n");
+ return 0;
+ }
+
+ if(isserver && nud->pesp_conn->type == ESPCONN_TCP){
+ return luaL_error( L, "tcp server send not supported" );
+ }
+
+#if 0
+ char temp[20] = {0};
+ c_sprintf(temp, IPSTR, IP2STR( &(pesp_conn->proto.tcp->remote_ip) ) );
+ NODE_DBG("remote ");
+ NODE_DBG(temp);
+ NODE_DBG(":");
+ NODE_DBG("%d",pesp_conn->proto.tcp->remote_port);
+ NODE_DBG(" sending data.\n");
+#endif
+
+ const char *payload = luaL_checklstring( L, 2, &l );
+ if (l>1460 || payload == NULL)
+ return luaL_error( L, "need <1460 payload" );
+
+ if (lua_type(L, 3) == LUA_TFUNCTION || lua_type(L, 3) == LUA_TLIGHTFUNCTION){
+ lua_pushvalue(L, 3); // copy argument (func) to the top of stack
+ if(nud->cb_send_ref != LUA_NOREF)
+ luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_send_ref);
+ nud->cb_send_ref = luaL_ref(L, LUA_REGISTRYINDEX);
+ }
+#ifdef CLIENT_SSL_ENABLE
+ if(nud->secure)
+ espconn_secure_sent(pesp_conn, (unsigned char *)payload, l);
+ else
+#endif
+ espconn_sent(pesp_conn, (unsigned char *)payload, l);
+
+ return 0;
+}
+
+// Lua: socket:dns( string, function(socket, ip) )
+static int ICACHE_FLASH_ATTR
+net_dns( lua_State* L, const char* mt )
+{
+ NODE_DBG("net_dns is called.\n");
+ bool isserver = false;
+ struct espconn *pesp_conn = NULL;
+ lnet_userdata *nud;
+ size_t l;
+
+ nud = (lnet_userdata *)luaL_checkudata(L, 1, mt);
+ luaL_argcheck(L, nud, 1, "Server/Socket expected");
+ if(nud==NULL){
+ NODE_DBG("userdata is nil.\n");
+ return 0;
+ }
+
+ if (mt!=NULL && c_strcmp(mt, "net.server")==0)
+ isserver = true;
+ else if (mt!=NULL && c_strcmp(mt, "net.socket")==0)
+ isserver = false;
+ else
+ {
+ NODE_DBG("wrong metatable for net_send.\n");
+ return 0;
+ }
+
+ pesp_conn = nud->pesp_conn;
+
+ if(!isserver || pesp_conn->type == ESPCONN_UDP){ // self_ref is only needed by socket userdata, or udp server
+ lua_pushvalue(L, 1); // copy to the top of stack
+ if(nud->self_ref != LUA_NOREF)
+ luaL_unref(L, LUA_REGISTRYINDEX, nud->self_ref);
+ nud->self_ref = luaL_ref(L, LUA_REGISTRYINDEX);
+ }
+
+ const char *domain = luaL_checklstring( L, 2, &l );
+ if (l>128 || domain == NULL)
+ return luaL_error( L, "need <128 domain" );
+
+ if (lua_type(L, 3) == LUA_TFUNCTION || lua_type(L, 3) == LUA_TLIGHTFUNCTION){
+ lua_pushvalue(L, 3); // copy argument (func) to the top of stack
+ if(nud->cb_dns_found_ref != LUA_NOREF)
+ luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_dns_found_ref);
+ nud->cb_dns_found_ref = luaL_ref(L, LUA_REGISTRYINDEX);
+ }
+
+ host_ip.addr = 0;
+ espconn_gethostbyname(pesp_conn, domain, &host_ip, net_dns_found);
+
+ return 0;
+}
+
+
+// Lua: s = net.createServer(type, function(server))
+static int ICACHE_FLASH_ATTR net_createServer( lua_State* L )
+{
+ const char *mt = "net.server";
+ return net_create(L, mt);
+}
+
+// Lua: server:delete()
+static int ICACHE_FLASH_ATTR
+net_server_delete( lua_State* L )
+{
+ const char *mt = "net.server";
+ return net_delete(L, mt);
+}
+
+// Lua: server:listen( port, ip )
+static int ICACHE_FLASH_ATTR
+net_server_listen( lua_State* L )
+{
+ const char *mt = "net.server";
+ return net_start(L, mt);
+}
+
+// Lua: server:close()
+static int ICACHE_FLASH_ATTR
+net_server_close( lua_State* L )
+{
+ const char *mt = "net.server";
+ return net_close(L, mt);
+}
+
+// Lua: udpserver:on( "method", function(udpserver) )
+static int ICACHE_FLASH_ATTR
+net_udpserver_on( lua_State* L )
+{
+ const char *mt = "net.server";
+ return net_on(L, mt);
+}
+
+// Lua: udpserver:send(string, function() )
+static int ICACHE_FLASH_ATTR
+net_udpserver_send( lua_State* L )
+{
+ const char *mt = "net.server";
+ return net_send(L, mt);;
+}
+
+// Lua: s = net.createConnection(type, function(conn))
+static int ICACHE_FLASH_ATTR
+net_createConnection( lua_State* L )
+{
+ const char *mt = "net.socket";
+ return net_create(L, mt);
+}
+
+// Lua: socket:delete()
+static int ICACHE_FLASH_ATTR
+net_socket_delete( lua_State* L )
+{
+ const char *mt = "net.socket";
+ return net_delete(L, mt);
+}
+
+// Lua: socket:connect( port, ip )
+static int ICACHE_FLASH_ATTR
+net_socket_connect( lua_State* L )
+{
+ const char *mt = "net.socket";
+ return net_start(L, mt);
+}
+
+// Lua: socket:close()
+static int ICACHE_FLASH_ATTR
+net_socket_close( lua_State* L )
+{
+ const char *mt = "net.socket";
+ return net_close(L, mt);
+}
+
+// Lua: socket:on( "method", function(socket) )
+static int ICACHE_FLASH_ATTR
+net_socket_on( lua_State* L )
+{
+ const char *mt = "net.socket";
+ return net_on(L, mt);
+}
+
+// Lua: socket:send( string, function() )
+static int ICACHE_FLASH_ATTR
+net_socket_send( lua_State* L )
+{
+ const char *mt = "net.socket";
+ return net_send(L, mt);
+}
+
+// Lua: socket:dns( string, function(ip) )
+static int ICACHE_FLASH_ATTR
+net_socket_dns( lua_State* L )
+{
+ const char *mt = "net.socket";
+ return net_dns(L, mt);
+}
+
+#if 0
+static int ICACHE_FLASH_ATTR
+net_array_index( lua_State* L )
+{
+ char** parray = luaL_checkudata(L, 1, "net.array");
+ int index = luaL_checkint(L, 2);
+ lua_pushnumber(L, (*parray)[index-1]);
+ return 1;
+}
+
+static int ICACHE_FLASH_ATTR
+net_array_newindex( lua_State* L )
+{
+ char** parray = luaL_checkudata(L, 1, "net.array");
+ int index = luaL_checkint(L, 2);
+ int value = luaL_checkint(L, 3);
+ (*parray)[index-1] = value;
+ return 0;
+}
+
+// expose an array to lua, by storing it in a userdata with the array metatable
+static int ICACHE_FLASH_ATTR
+expose_array(lua_State* L, char *array, unsigned short len) {
+ char** parray = lua_newuserdata(L, len);
+ *parray = array;
+ luaL_getmetatable(L, "net.array");
+ lua_setmetatable(L, -2);
+ return 1;
+}
+#endif
+
+// Module function map
+#define MIN_OPT_LEVEL 2
+#include "lrodefs.h"
+static const LUA_REG_TYPE net_server_map[] =
+{
+ { LSTRKEY( "listen" ), LFUNCVAL ( net_server_listen ) },
+ { LSTRKEY( "close" ), LFUNCVAL ( net_server_close ) },
+ { LSTRKEY( "on" ), LFUNCVAL ( net_udpserver_on ) },
+ { LSTRKEY( "send" ), LFUNCVAL ( net_udpserver_send ) },
+ // { LSTRKEY( "delete" ), LFUNCVAL ( net_server_delete ) },
+ { LSTRKEY( "__gc" ), LFUNCVAL ( net_server_delete ) },
+#if LUA_OPTIMIZE_MEMORY > 0
+ { LSTRKEY( "__index" ), LROVAL ( net_server_map ) },
+#endif
+ { LNILKEY, LNILVAL }
+};
+
+static const LUA_REG_TYPE net_socket_map[] =
+{
+ { LSTRKEY( "connect" ), LFUNCVAL( net_socket_connect ) },
+ { LSTRKEY( "close" ), LFUNCVAL ( net_socket_close ) },
+ { LSTRKEY( "on" ), LFUNCVAL ( net_socket_on ) },
+ { LSTRKEY( "send" ), LFUNCVAL ( net_socket_send ) },
+ { LSTRKEY( "dns" ), LFUNCVAL ( net_socket_dns ) },
+ // { LSTRKEY( "delete" ), LFUNCVAL ( net_socket_delete ) },
+ { LSTRKEY( "__gc" ), LFUNCVAL ( net_socket_delete ) },
+#if LUA_OPTIMIZE_MEMORY > 0
+ { LSTRKEY( "__index" ), LROVAL ( net_socket_map ) },
+#endif
+ { LNILKEY, LNILVAL }
+};
+#if 0
+static const LUA_REG_TYPE net_array_map[] =
+{
+ { LSTRKEY( "__index" ), LFUNCVAL( net_array_index ) },
+ { LSTRKEY( "__newindex" ), LFUNCVAL( net_array_newindex ) },
+ { LNILKEY, LNILVAL }
+};
+#endif
+const LUA_REG_TYPE net_map[] =
+{
+ { LSTRKEY( "createServer" ), LFUNCVAL ( net_createServer ) },
+ { LSTRKEY( "createConnection" ), LFUNCVAL ( net_createConnection ) },
+#if LUA_OPTIMIZE_MEMORY > 0
+ { LSTRKEY( "TCP" ), LNUMVAL( TCP ) },
+ { LSTRKEY( "UDP" ), LNUMVAL( UDP ) },
+
+ { LSTRKEY( "__metatable" ), LROVAL( net_map ) },
+#endif
+ { LNILKEY, LNILVAL }
+};
+
+LUALIB_API int ICACHE_FLASH_ATTR luaopen_net( lua_State *L )
+{
+ int i;
+ for(i=0;i 0
+ luaL_rometatable(L, "net.server", (void *)net_server_map); // create metatable for net.server
+ luaL_rometatable(L, "net.socket", (void *)net_socket_map); // create metatable for net.socket
+ #if 0
+ luaL_rometatable(L, "net.array", (void *)net_array_map); // create metatable for net.array
+ #endif
+ return 0;
+#else // #if LUA_OPTIMIZE_MEMORY > 0
+ int n;
+ luaL_register( L, AUXLIB_NET, net_map );
+
+ // Set it as its own metatable
+ lua_pushvalue( L, -1 );
+ lua_setmetatable( L, -2 );
+
+ // Module constants
+ MOD_REG_NUMBER( L, "TCP", TCP );
+ MOD_REG_NUMBER( L, "UDP", UDP );
+
+ n = lua_gettop(L);
+
+ // create metatable
+ luaL_newmetatable(L, "net.server");
+ // metatable.__index = metatable
+ lua_pushliteral(L, "__index");
+ lua_pushvalue(L,-2);
+ lua_rawset(L,-3);
+ // Setup the methods inside metatable
+ luaL_register( L, NULL, net_server_map );
+
+ lua_settop(L, n);
+ // create metatable
+ luaL_newmetatable(L, "net.socket");
+ // metatable.__index = metatable
+ lua_pushliteral(L, "__index");
+ lua_pushvalue(L,-2);
+ lua_rawset(L,-3);
+ // Setup the methods inside metatable
+ luaL_register( L, NULL, net_socket_map );
+#if 0
+ lua_settop(L, n);
+ // create metatable
+ luaL_newmetatable(L, "net.array");
+ // Setup the methods inside metatable
+ luaL_register( L, NULL, net_array_map );
+#endif
+ return 1;
+#endif // #if LUA_OPTIMIZE_MEMORY > 0
+}
diff --git a/app/modules/node.c b/app/modules/node.c
new file mode 100644
index 00000000..94b2f7a1
--- /dev/null
+++ b/app/modules/node.c
@@ -0,0 +1,262 @@
+// Module for interfacing with system
+
+//#include "lua.h"
+#include "lualib.h"
+#include "lauxlib.h"
+#include "platform.h"
+#include "auxmods.h"
+#include "lrotable.h"
+
+#include "c_types.h"
+#include "romfs.h"
+#include "c_string.h"
+#include "driver/uart.h"
+
+// Lua: restart()
+static int ICACHE_FLASH_ATTR node_restart( lua_State* L )
+{
+ system_restart();
+ return 0;
+}
+
+// Lua: dsleep( us )
+static int ICACHE_FLASH_ATTR node_deepsleep( lua_State* L )
+{
+ s32 us;
+ us = luaL_checkinteger( L, 1 );
+ if ( us <= 0 )
+ return luaL_error( L, "wrong arg range" );
+ system_deep_sleep( us );
+ return 0;
+}
+
+// Lua: chipid()
+static int ICACHE_FLASH_ATTR node_chipid( lua_State* L )
+{
+ uint32_t id = system_get_chip_id();
+ lua_pushinteger(L, id);
+ return 1;
+}
+
+// Lua: heap()
+static int ICACHE_FLASH_ATTR node_heap( lua_State* L )
+{
+ uint32_t sz = system_get_free_heap_size();
+ lua_pushinteger(L, sz);
+ return 1;
+}
+
+extern int led_high_count; // this is defined in lua.c
+extern int led_low_count;
+// Lua: led(low, high)
+static int ICACHE_FLASH_ATTR node_led( lua_State* L )
+{
+ int low, high;
+ if ( lua_isnumber(L, 1) )
+ {
+ low = lua_tointeger(L, 1);
+ if ( low < 0 ){
+ return luaL_error( L, "wrong arg type" );
+ }
+ } else {
+ low = LED_LOW_COUNT_DEFAULT; // default to LED_LOW_COUNT_DEFAULT
+ }
+ if ( lua_isnumber(L, 2) )
+ {
+ high = lua_tointeger(L, 2);
+ if ( high < 0 ){
+ return luaL_error( L, "wrong arg type" );
+ }
+ } else {
+ high = LED_HIGH_COUNT_DEFAULT; // default to LED_HIGH_COUNT_DEFAULT
+ }
+ led_high_count = (uint32_t)high / READLINE_INTERVAL;
+ led_low_count = (uint32_t)low / READLINE_INTERVAL;
+ return 0;
+}
+
+static int long_key_ref = LUA_NOREF;
+static int short_key_ref = LUA_NOREF;
+static lua_State *gL = NULL;
+
+void ICACHE_FLASH_ATTR default_long_press(void *arg){
+ if(led_high_count == 12 && led_low_count == 12){
+ led_low_count = led_high_count = 6;
+ } else {
+ led_low_count = led_high_count = 12;
+ }
+ // led_high_count = 1000 / READLINE_INTERVAL;
+ // led_low_count = 1000 / READLINE_INTERVAL;
+ // NODE_DBG("default_long_press is called. hc: %d, lc: %d\n", led_high_count, led_low_count);
+}
+
+void ICACHE_FLASH_ATTR default_short_press(void *arg){
+ system_restart();
+}
+
+void ICACHE_FLASH_ATTR key_long_press(void *arg){
+ NODE_DBG("key_long_press is called.\n");
+ if(long_key_ref == LUA_NOREF){
+ default_long_press(arg);
+ return;
+ }
+ if(!gL)
+ return;
+ lua_rawgeti(gL, LUA_REGISTRYINDEX, long_key_ref);
+ lua_call(gL, 0, 0);
+}
+
+void ICACHE_FLASH_ATTR key_short_press(void *arg){
+ NODE_DBG("key_short_press is called.\n");
+ if(short_key_ref == LUA_NOREF){
+ default_short_press(arg);
+ return;
+ }
+ if(!gL)
+ return;
+ lua_rawgeti(gL, LUA_REGISTRYINDEX, short_key_ref);
+ lua_call(gL, 0, 0);
+}
+
+// Lua: key(type, function)
+static int ICACHE_FLASH_ATTR node_key( lua_State* L )
+{
+ int *ref = NULL;
+ size_t sl;
+
+ const char *str = luaL_checklstring( L, 1, &sl );
+ if (str == NULL)
+ return luaL_error( L, "wrong arg type" );
+
+ if(sl == 5 && c_strcmp(str, "short") == 0){
+ ref = &short_key_ref;
+ }else if(sl == 4 && c_strcmp(str, "long") == 0){
+ ref = &long_key_ref;
+ }else{
+ ref = &short_key_ref;
+ }
+ gL = L;
+ // luaL_checkanyfunction(L, 2);
+ if (lua_type(L, 2) == LUA_TFUNCTION || lua_type(L, 2) == LUA_TLIGHTFUNCTION){
+ lua_pushvalue(L, 2); // copy argument (func) to the top of stack
+ if(*ref != LUA_NOREF)
+ luaL_unref(L, LUA_REGISTRYINDEX, *ref);
+ *ref = luaL_ref(L, LUA_REGISTRYINDEX);
+ } else { // unref the key press function
+ if(*ref != LUA_NOREF)
+ luaL_unref(L, LUA_REGISTRYINDEX, *ref);
+ *ref = LUA_NOREF;
+ }
+
+ return 0;
+}
+
+extern lua_Load gLoad;
+extern os_timer_t lua_timer;
+extern void dojob(lua_Load *load);
+// Lua: input("string")
+static int ICACHE_FLASH_ATTR node_input( lua_State* L )
+{
+ size_t l=0;
+ const char *s = luaL_checklstring(L, 1, &l);
+ if (s != NULL && l > 0 && l < LUA_MAXINPUT - 1)
+ {
+ lua_Load *load = &gLoad;
+ if(load->line_position == 0){
+ c_memcpy(load->line, s, l);
+ load->line[l+1] = '\0';
+ load->line_position = c_strlen(load->line)+1;
+ load->done = 1;
+ NODE_DBG("Get command:\n");
+ NODE_DBG(load->line); // buggy here
+ NODE_DBG("\nResult(if any):\n");
+ os_timer_disarm(&lua_timer);
+ os_timer_setfn(&lua_timer, (os_timer_func_t *)dojob, load);
+ os_timer_arm(&lua_timer, READLINE_INTERVAL, 0); // no repeat
+ }
+ }
+ return 0;
+}
+
+static int output_redir_ref = LUA_NOREF;
+static int serial_debug = 1;
+void ICACHE_FLASH_ATTR output_redirect(const char *str){
+ // if(c_strlen(str)>=TX_BUFF_SIZE){
+ // NODE_ERR("output too long.\n");
+ // return;
+ // }
+
+ if(output_redir_ref == LUA_NOREF || !gL){
+ uart0_sendStr(str);
+ return;
+ }
+
+ if(serial_debug!=0){
+ uart0_sendStr(str);
+ }
+
+ lua_rawgeti(gL, LUA_REGISTRYINDEX, output_redir_ref);
+ lua_pushstring(gL, str);
+ lua_call(gL, 1, 0); // this call back function should never user output.
+}
+
+// Lua: output(function(c), debug)
+static int ICACHE_FLASH_ATTR node_output( lua_State* L )
+{
+ gL = L;
+ // luaL_checkanyfunction(L, 1);
+ if (lua_type(L, 1) == LUA_TFUNCTION || lua_type(L, 1) == LUA_TLIGHTFUNCTION){
+ lua_pushvalue(L, 1); // copy argument (func) to the top of stack
+ if(output_redir_ref != LUA_NOREF)
+ luaL_unref(L, LUA_REGISTRYINDEX, output_redir_ref);
+ output_redir_ref = luaL_ref(L, LUA_REGISTRYINDEX);
+ } else { // unref the key press function
+ if(output_redir_ref != LUA_NOREF)
+ luaL_unref(L, LUA_REGISTRYINDEX, output_redir_ref);
+ output_redir_ref = LUA_NOREF;
+ serial_debug = 1;
+ return 0;
+ }
+
+ if ( lua_isnumber(L, 2) )
+ {
+ serial_debug = lua_tointeger(L, 2);
+ if(serial_debug!=0)
+ serial_debug = 1;
+ } else {
+ serial_debug = 1; // default to 1
+ }
+
+ return 0;
+}
+
+// Module function map
+#define MIN_OPT_LEVEL 2
+#include "lrodefs.h"
+const LUA_REG_TYPE node_map[] =
+{
+ { LSTRKEY( "restart" ), LFUNCVAL( node_restart ) },
+ { LSTRKEY( "dsleep" ), LFUNCVAL( node_deepsleep ) },
+ { LSTRKEY( "chipid" ), LFUNCVAL( node_chipid ) },
+ { LSTRKEY( "heap" ), LFUNCVAL( node_heap ) },
+ { LSTRKEY( "key" ), LFUNCVAL( node_key ) },
+ { LSTRKEY( "led" ), LFUNCVAL( node_led ) },
+ { LSTRKEY( "input" ), LFUNCVAL( node_input ) },
+ { LSTRKEY( "output" ), LFUNCVAL( node_output ) },
+#if LUA_OPTIMIZE_MEMORY > 0
+
+#endif
+ { LNILKEY, LNILVAL }
+};
+
+LUALIB_API int ICACHE_FLASH_ATTR luaopen_node( lua_State *L )
+{
+#if LUA_OPTIMIZE_MEMORY > 0
+ return 0;
+#else // #if LUA_OPTIMIZE_MEMORY > 0
+ luaL_register( L, AUXLIB_NODE, node_map );
+ // Add constants
+
+ return 1;
+#endif // #if LUA_OPTIMIZE_MEMORY > 0
+}
diff --git a/app/modules/ow.c b/app/modules/ow.c
new file mode 100644
index 00000000..6a399752
--- /dev/null
+++ b/app/modules/ow.c
@@ -0,0 +1,327 @@
+// Module for interfacing with the OneWire interface
+
+//#include "lua.h"
+#include "lualib.h"
+#include "lauxlib.h"
+#include "auxmods.h"
+#include "lrotable.h"
+#include "driver/onewire.h"
+
+// Lua: ow.setup( id )
+static int ICACHE_FLASH_ATTR ow_setup( lua_State *L )
+{
+ unsigned id = luaL_checkinteger( L, 1 );
+
+ if(id==0)
+ return luaL_error( L, "no 1-wire for D0" );
+
+ MOD_CHECK_ID( ow, id );
+
+ onewire_init( id );
+ return 0;
+}
+
+// Lua: r = ow.reset( id )
+static int ICACHE_FLASH_ATTR ow_reset( lua_State *L )
+{
+ unsigned id = luaL_checkinteger( L, 1 );
+ MOD_CHECK_ID( ow, id );
+ lua_pushinteger( L, onewire_reset(id) );
+ return 1;
+}
+
+// Lua: ow.skip( id )
+static int ICACHE_FLASH_ATTR ow_skip( lua_State *L )
+{
+ unsigned id = luaL_checkinteger( L, 1 );
+ MOD_CHECK_ID( ow, id );
+ onewire_skip(id);
+ return 0;
+}
+
+// Lua: ow.select( id, buf[8])
+static int ICACHE_FLASH_ATTR ow_select( lua_State *L )
+{
+ uint8_t rom[8];
+ size_t datalen;
+ int numdata, i;
+ unsigned id = luaL_checkinteger( L, 1 );
+ const char *pdata;
+ MOD_CHECK_ID( ow, id );
+
+ if( lua_istable( L, 2 ) )
+ {
+ datalen = lua_objlen( L, 2 );
+ if (datalen!=8)
+ return luaL_error( L, "wrong arg range" );
+ for( i = 0; i < datalen; i ++ )
+ {
+ lua_rawgeti( L, 2, i + 1 );
+ numdata = ( int )luaL_checkinteger( L, -1 );
+ lua_pop( L, 1 );
+ if( numdata > 255 )
+ return luaL_error( L, "wrong arg range" );
+ rom[i] = (uint8_t)numdata;
+ }
+ }
+ else
+ {
+ pdata = luaL_checklstring( L, 2, &datalen );
+ if (datalen!=8)
+ return luaL_error( L, "wrong arg range" );
+ for( i = 0; i < datalen; i ++ ){
+ rom[i] = pdata[i];
+ }
+ }
+
+ onewire_select(id, rom);
+ return 0;
+}
+
+// Lua: ow.write( id, v, power)
+static int ICACHE_FLASH_ATTR ow_write( lua_State *L )
+{
+ int power = 0;
+ unsigned id = luaL_checkinteger( L, 1 );
+ MOD_CHECK_ID( ow, id );
+
+ int v = (int)luaL_checkinteger( L, 2 );
+ if( v > 255 )
+ return luaL_error( L, "wrong arg range" );
+ if(lua_isnumber(L, 3))
+ power = lua_tointeger(L, 3);
+ if(power!=0)
+ power = 1;
+
+ onewire_write((uint8_t)id, (uint8_t)v, (uint8_t)power);
+
+ return 0;
+}
+
+// Lua: ow.write_bytes( id, buf, power)
+static int ICACHE_FLASH_ATTR ow_write_bytes( lua_State *L )
+{
+ int power = 0;
+ size_t datalen;
+ unsigned id = luaL_checkinteger( L, 1 );
+ MOD_CHECK_ID( ow, id );
+
+ const uint8_t *pdata = luaL_checklstring( L, 2, &datalen );
+
+ if(lua_isnumber(L, 3))
+ power = lua_tointeger(L, 3);
+ if(power!=0)
+ power = 1;
+
+ onewire_write_bytes((uint8_t)id, pdata, (uint16_t)datalen, (uint8_t)power);
+
+ return 0;
+}
+
+// Lua: r = ow.read( id )
+static int ICACHE_FLASH_ATTR ow_read( lua_State *L )
+{
+ unsigned id = luaL_checkinteger( L, 1 );
+ MOD_CHECK_ID( ow, id );
+ lua_pushinteger( L, onewire_read(id) );
+ return 1;
+}
+
+// Lua: r = ow.read_bytes( id, size )
+static int ICACHE_FLASH_ATTR ow_read_bytes( lua_State *L )
+{
+ unsigned id = luaL_checkinteger( L, 1 );
+ MOD_CHECK_ID( ow, id );
+ u32 size = ( u32 )luaL_checkinteger( L, 2 );
+ if( size == 0 )
+ return 0;
+
+ luaL_Buffer b;
+ luaL_buffinit( L, &b );
+ char *p = luaL_prepbuffer(&b);
+
+ onewire_read_bytes(id, (uint8_t *)p, size);
+
+ luaL_addsize(&b, size);
+ luaL_pushresult( &b );
+ return 1;
+}
+
+// Lua: ow.depower( id )
+static int ICACHE_FLASH_ATTR ow_depower( lua_State *L )
+{
+ unsigned id = luaL_checkinteger( L, 1 );
+ MOD_CHECK_ID( ow, id );
+ onewire_depower(id);
+ return 0;
+}
+
+#if ONEWIRE_SEARCH
+// Clear the search state so that if will start from the beginning again.
+// Lua: ow.reset_search( id )
+static int ICACHE_FLASH_ATTR ow_reset_search( lua_State *L )
+{
+ unsigned id = luaL_checkinteger( L, 1 );
+ MOD_CHECK_ID( ow, id );
+ onewire_reset_search(id);
+ return 0;
+}
+
+
+// Setup the search to find the device type 'family_code' on the next call
+// to search(*newAddr) if it is present.
+// Lua: ow.target_search( id, family_code)
+static int ICACHE_FLASH_ATTR ow_target_search( lua_State *L )
+{
+ unsigned id = luaL_checkinteger( L, 1 );
+ MOD_CHECK_ID( ow, id );
+
+ int code = (int)luaL_checkinteger( L, 2 );
+ if( code > 255 )
+ return luaL_error( L, "wrong arg range" );
+
+ onewire_target_search((uint8_t)id, (uint8_t)code);
+
+ return 0;
+}
+
+// Look for the next device. Returns 1 if a new address has been
+// returned. A zero might mean that the bus is shorted, there are
+// no devices, or you have already retrieved all of them. It
+// might be a good idea to check the CRC to make sure you didn't
+// get garbage. The order is deterministic. You will always get
+// the same devices in the same order.
+
+// Lua: r = ow.search( id )
+static int ICACHE_FLASH_ATTR ow_search( lua_State *L )
+{
+ unsigned id = luaL_checkinteger( L, 1 );
+ MOD_CHECK_ID( ow, id );
+
+ luaL_Buffer b;
+ luaL_buffinit( L, &b );
+ char *p = luaL_prepbuffer(&b);
+
+ if(onewire_search(id, (uint8_t *)p)){
+ luaL_addsize(&b, 8);
+ luaL_pushresult( &b );
+ } else {
+ luaL_pushresult(&b); /* close buffer */
+ lua_pop(L,1);
+ lua_pushnil(L);
+ }
+ return 1;
+}
+#endif
+
+#if ONEWIRE_CRC
+// uint8_t onewire_crc8(const uint8_t *addr, uint8_t len);
+// Lua: r = ow.crc8( buf )
+static int ICACHE_FLASH_ATTR ow_crc8( lua_State *L )
+{
+ size_t datalen;
+ const uint8_t *pdata = luaL_checklstring( L, 1, &datalen );
+ if(datalen > 255)
+ return luaL_error( L, "wrong arg range" );
+ lua_pushinteger( L, onewire_crc8(pdata, (uint8_t)datalen) );
+ return 1;
+}
+
+#if ONEWIRE_CRC16
+// bool onewire_check_crc16(const uint8_t* input, uint16_t len, const uint8_t* inverted_crc, uint16_t crc);
+// Lua: b = ow.check_crc16( buf, inverted_crc0, inverted_crc1, crc )
+static int ICACHE_FLASH_ATTR ow_check_crc16( lua_State *L )
+{
+ size_t datalen;
+ uint8_t inverted_crc[2];
+ const uint8_t *pdata = luaL_checklstring( L, 1, &datalen );
+ if(datalen > 65535)
+ return luaL_error( L, "wrong arg range" );
+
+ int crc = 0;
+ crc = luaL_checkinteger( L, 2 );
+ if(datalen > 255)
+ return luaL_error( L, "wrong arg range" );
+ inverted_crc[0] = (uint8_t)crc;
+
+ crc = luaL_checkinteger( L, 3 );
+ if(datalen > 255)
+ return luaL_error( L, "wrong arg range" );
+ inverted_crc[1] = (uint8_t)crc;
+
+ crc = 0;
+ if(lua_isnumber(L, 4))
+ crc = lua_tointeger(L, 4);
+ if(crc > 65535)
+ return luaL_error( L, "wrong arg range" );
+
+ lua_pushboolean( L, onewire_check_crc16(pdata, (uint16_t)datalen, inverted_crc, (uint16_t)crc) );
+
+ return 1;
+}
+
+// uint16_t onewire_crc16(const uint8_t* input, uint16_t len, uint16_t crc);
+// Lua: r = ow.crc16( buf, crc )
+static int ICACHE_FLASH_ATTR ow_crc16( lua_State *L )
+{
+ size_t datalen;
+ const uint8_t *pdata = luaL_checklstring( L, 1, &datalen );
+ if(datalen > 65535)
+ return luaL_error( L, "wrong arg range" );
+ int crc = 0;
+ if(lua_isnumber(L, 2))
+ crc = lua_tointeger(L, 2);
+ if(crc > 65535)
+ return luaL_error( L, "wrong arg range" );
+
+ lua_pushinteger( L, onewire_crc16(pdata, (uint16_t)datalen, (uint16_t)crc) );
+
+ return 1;
+}
+#endif
+#endif
+
+// Module function map
+#define MIN_OPT_LEVEL 2
+#include "lrodefs.h"
+const LUA_REG_TYPE ow_map[] =
+{
+ { LSTRKEY( "setup" ), LFUNCVAL( ow_setup ) },
+ { LSTRKEY( "reset" ), LFUNCVAL( ow_reset ) },
+ { LSTRKEY( "skip" ), LFUNCVAL( ow_skip ) },
+ { LSTRKEY( "select" ), LFUNCVAL( ow_select ) },
+ { LSTRKEY( "write" ), LFUNCVAL( ow_write ) },
+ { LSTRKEY( "write_bytes" ), LFUNCVAL( ow_write_bytes ) },
+ { LSTRKEY( "read" ), LFUNCVAL( ow_read ) },
+ { LSTRKEY( "read_bytes" ), LFUNCVAL( ow_read_bytes ) },
+ { LSTRKEY( "depower" ), LFUNCVAL( ow_depower ) },
+#if ONEWIRE_SEARCH
+ { LSTRKEY( "reset_search" ), LFUNCVAL( ow_reset_search ) },
+ { LSTRKEY( "target_search" ), LFUNCVAL( ow_target_search ) },
+ { LSTRKEY( "search" ), LFUNCVAL( ow_search ) },
+#endif
+#if ONEWIRE_CRC
+ { LSTRKEY( "crc8" ), LFUNCVAL( ow_crc8 ) },
+#if ONEWIRE_CRC16
+ { LSTRKEY( "check_crc16" ), LFUNCVAL( ow_check_crc16 ) },
+ { LSTRKEY( "crc16" ), LFUNCVAL( ow_crc16 ) },
+#endif
+#endif
+#if LUA_OPTIMIZE_MEMORY > 0
+
+#endif
+ { LNILKEY, LNILVAL }
+};
+
+LUALIB_API int ICACHE_FLASH_ATTR luaopen_ow( lua_State *L )
+{
+#if LUA_OPTIMIZE_MEMORY > 0
+ return 0;
+#else // #if LUA_OPTIMIZE_MEMORY > 0
+ luaL_register( L, AUXLIB_OW, ow_map );
+
+ // Add the constants
+
+ return 1;
+#endif // #if LUA_OPTIMIZE_MEMORY > 0
+}
diff --git a/app/modules/pwm.c b/app/modules/pwm.c
new file mode 100644
index 00000000..ead3f064
--- /dev/null
+++ b/app/modules/pwm.c
@@ -0,0 +1,154 @@
+// Module for interfacing with PWM
+
+//#include "lua.h"
+#include "lualib.h"
+#include "lauxlib.h"
+#include "platform.h"
+#include "auxmods.h"
+#include "lrotable.h"
+
+#include "c_types.h"
+
+// Lua: realfrequency = setup( id, frequency, duty )
+static int ICACHE_FLASH_ATTR lpwm_setup( lua_State* L )
+{
+ s32 freq; // signed, to error check for negative values
+ unsigned duty;
+ unsigned id;
+
+ id = luaL_checkinteger( L, 1 );
+ if(id==0)
+ return luaL_error( L, "no pwm for D0" );
+ MOD_CHECK_ID( pwm, id );
+ freq = luaL_checkinteger( L, 2 );
+ if ( freq <= 0 )
+ return luaL_error( L, "wrong arg range" );
+ duty = luaL_checkinteger( L, 3 );
+ if ( duty > NORMAL_PWM_DEPTH )
+ // Negative values will turn out > 100, so will also fail.
+ return luaL_error( L, "wrong arg range" );
+ freq = platform_pwm_setup( id, (u32)freq, duty );
+ if(freq==0)
+ return luaL_error( L, "too many pwms." );
+ lua_pushinteger( L, freq );
+ return 1;
+}
+
+// Lua: close( id )
+static int ICACHE_FLASH_ATTR lpwm_close( lua_State* L )
+{
+ unsigned id;
+
+ id = luaL_checkinteger( L, 1 );
+ MOD_CHECK_ID( pwm, id );
+ platform_pwm_close( id );
+ return 0;
+}
+
+// Lua: start( id )
+static int ICACHE_FLASH_ATTR lpwm_start( lua_State* L )
+{
+ unsigned id;
+ id = luaL_checkinteger( L, 1 );
+ MOD_CHECK_ID( pwm, id );
+ platform_pwm_start( id );
+ return 0;
+}
+
+// Lua: stop( id )
+static int ICACHE_FLASH_ATTR lpwm_stop( lua_State* L )
+{
+ unsigned id;
+
+ id = luaL_checkinteger( L, 1 );
+ MOD_CHECK_ID( pwm, id );
+ platform_pwm_stop( id );
+ return 0;
+}
+
+// Lua: realclock = setclock( id, clock )
+static int ICACHE_FLASH_ATTR lpwm_setclock( lua_State* L )
+{
+ unsigned id;
+ s32 clk; // signed to error-check for negative values
+
+ id = luaL_checkinteger( L, 1 );
+ MOD_CHECK_ID( pwm, id );
+ clk = luaL_checkinteger( L, 2 );
+ if ( clk <= 0 )
+ return luaL_error( L, "wrong arg range" );
+ clk = platform_pwm_set_clock( id, (u32)clk );
+ lua_pushinteger( L, clk );
+ return 1;
+}
+
+// Lua: clock = getclock( id )
+static int ICACHE_FLASH_ATTR lpwm_getclock( lua_State* L )
+{
+ unsigned id;
+ u32 clk;
+
+ id = luaL_checkinteger( L, 1 );
+ MOD_CHECK_ID( pwm, id );
+ clk = platform_pwm_get_clock( id );
+ lua_pushinteger( L, clk );
+ return 1;
+}
+
+// Lua: realduty = setduty( id, duty )
+static int ICACHE_FLASH_ATTR lpwm_setduty( lua_State* L )
+{
+ unsigned id;
+ s32 duty; // signed to error-check for negative values
+
+ id = luaL_checkinteger( L, 1 );
+ MOD_CHECK_ID( pwm, id );
+ duty = luaL_checkinteger( L, 2 );
+ if ( duty > NORMAL_PWM_DEPTH )
+ return luaL_error( L, "wrong arg range" );
+ duty = platform_pwm_set_duty( id, (u32)duty );
+ lua_pushinteger( L, duty );
+ return 1;
+}
+
+// Lua: duty = getduty( id )
+static int ICACHE_FLASH_ATTR lpwm_getduty( lua_State* L )
+{
+ unsigned id;
+ u32 duty;
+
+ id = luaL_checkinteger( L, 1 );
+ MOD_CHECK_ID( pwm, id );
+ duty = platform_pwm_get_duty( id );
+ lua_pushinteger( L, duty );
+ return 1;
+}
+
+// Module function map
+#define MIN_OPT_LEVEL 2
+#include "lrodefs.h"
+const LUA_REG_TYPE pwm_map[] =
+{
+ { LSTRKEY( "setup" ), LFUNCVAL( lpwm_setup ) },
+ { LSTRKEY( "close" ), LFUNCVAL( lpwm_close ) },
+ { LSTRKEY( "start" ), LFUNCVAL( lpwm_start ) },
+ { LSTRKEY( "stop" ), LFUNCVAL( lpwm_stop ) },
+ { LSTRKEY( "setclock" ), LFUNCVAL( lpwm_setclock ) },
+ { LSTRKEY( "getclock" ), LFUNCVAL( lpwm_getclock ) },
+ { LSTRKEY( "setduty" ), LFUNCVAL( lpwm_setduty ) },
+ { LSTRKEY( "getduty" ), LFUNCVAL( lpwm_getduty ) },
+#if LUA_OPTIMIZE_MEMORY > 0
+
+#endif
+ { LNILKEY, LNILVAL }
+};
+
+LUALIB_API int ICACHE_FLASH_ATTR luaopen_pwm( lua_State *L )
+{
+#if LUA_OPTIMIZE_MEMORY > 0
+ return 0;
+#else // #if LUA_OPTIMIZE_MEMORY > 0
+ luaL_register( L, AUXLIB_PWM, pwm_map );
+ return 1;
+#endif // #if LUA_OPTIMIZE_MEMORY > 0
+}
diff --git a/app/modules/tmr.c b/app/modules/tmr.c
new file mode 100644
index 00000000..9c0e2d47
--- /dev/null
+++ b/app/modules/tmr.c
@@ -0,0 +1,189 @@
+// Module for interfacing with timer
+
+//#include "lua.h"
+#include "lualib.h"
+#include "lauxlib.h"
+#include "platform.h"
+#include "auxmods.h"
+#include "lrotable.h"
+
+#include "c_types.h"
+
+static os_timer_t alarm_timer[NUM_TMR];
+static int alarm_timer_cb_ref[NUM_TMR] = {LUA_NOREF,LUA_NOREF,LUA_NOREF,LUA_NOREF,LUA_NOREF,LUA_NOREF,LUA_NOREF};
+
+void ICACHE_FLASH_ATTR alarm_timer_common(lua_State* L, unsigned id){
+ if(alarm_timer_cb_ref[id] == LUA_NOREF)
+ return;
+ lua_rawgeti(L, LUA_REGISTRYINDEX, alarm_timer_cb_ref[id]);
+ lua_call(L, 0, 0);
+}
+
+void ICACHE_FLASH_ATTR
+alarm_timer_cb0(void *arg){
+ if( !arg )
+ return;
+ alarm_timer_common((lua_State*)arg, 0);
+}
+
+void ICACHE_FLASH_ATTR
+alarm_timer_cb1(void *arg){
+ if( !arg )
+ return;
+ alarm_timer_common((lua_State*)arg, 1);
+}
+
+void ICACHE_FLASH_ATTR
+alarm_timer_cb2(void *arg){
+ if( !arg )
+ return;
+ alarm_timer_common((lua_State*)arg, 2);
+}
+
+void ICACHE_FLASH_ATTR
+alarm_timer_cb3(void *arg){
+ if( !arg )
+ return;
+ alarm_timer_common((lua_State*)arg, 3);
+}
+
+void ICACHE_FLASH_ATTR
+alarm_timer_cb4(void *arg){
+ if( !arg )
+ return;
+ alarm_timer_common((lua_State*)arg, 4);
+}
+
+void ICACHE_FLASH_ATTR
+alarm_timer_cb5(void *arg){
+ if( !arg )
+ return;
+ alarm_timer_common((lua_State*)arg, 5);
+}
+
+void ICACHE_FLASH_ATTR
+alarm_timer_cb6(void *arg){
+ if( !arg )
+ return;
+ alarm_timer_common((lua_State*)arg, 6);
+}
+
+typedef void (*alarm_timer_callback)(void *arg);
+static alarm_timer_callback alarm_timer_cb[NUM_TMR] = {alarm_timer_cb0,alarm_timer_cb1,alarm_timer_cb2,alarm_timer_cb3,alarm_timer_cb4,alarm_timer_cb5,alarm_timer_cb6};
+
+// Lua: delay( us )
+static int ICACHE_FLASH_ATTR tmr_delay( lua_State* L )
+{
+ s32 us;
+ us = luaL_checkinteger( L, 1 );
+ if ( us <= 0 )
+ return luaL_error( L, "wrong arg range" );
+ unsigned sec = (unsigned)us / 1000000;
+ unsigned remain = (unsigned)us % 1000000;
+ int i = 0;
+ for(i=0;i0)
+ os_delay_us( remain );
+ return 0;
+}
+
+// Lua: now() , return system timer in us
+static int ICACHE_FLASH_ATTR tmr_now( lua_State* L )
+{
+ unsigned now = 0x7FFFFFFF & system_get_time();
+ lua_pushinteger( L, now );
+ return 1;
+}
+
+// Lua: alarm( id, interval, repeat, function )
+static int ICACHE_FLASH_ATTR tmr_alarm( lua_State* L )
+{
+ s32 interval;
+ unsigned repeat = 0;
+ int stack = 1;
+
+ unsigned id = luaL_checkinteger( L, stack );
+ stack++;
+ MOD_CHECK_ID( tmr, id );
+
+ interval = luaL_checkinteger( L, stack );
+ stack++;
+ if ( interval <= 0 )
+ return luaL_error( L, "wrong arg range" );
+
+ if ( lua_isnumber(L, stack) ){
+ repeat = lua_tointeger(L, stack);
+ stack++;
+ if ( repeat != 1 && repeat != 0 )
+ return luaL_error( L, "wrong arg type" );
+ }
+
+ // luaL_checkanyfunction(L, stack);
+ if (lua_type(L, stack) == LUA_TFUNCTION || lua_type(L, stack) == LUA_TLIGHTFUNCTION){
+ lua_pushvalue(L, stack); // copy argument (func) to the top of stack
+ if(alarm_timer_cb_ref[id] != LUA_NOREF)
+ luaL_unref(L, LUA_REGISTRYINDEX, alarm_timer_cb_ref[id]);
+ alarm_timer_cb_ref[id] = luaL_ref(L, LUA_REGISTRYINDEX);
+ }
+
+ os_timer_disarm(&(alarm_timer[id]));
+ os_timer_setfn(&(alarm_timer[id]), (os_timer_func_t *)(alarm_timer_cb[id]), L);
+ os_timer_arm(&(alarm_timer[id]), interval, repeat);
+ return 0;
+}
+
+// Lua: stop( id )
+static int ICACHE_FLASH_ATTR tmr_stop( lua_State* L )
+{
+ unsigned id = luaL_checkinteger( L, 1 );
+ MOD_CHECK_ID( tmr, id );
+
+ os_timer_disarm(&(alarm_timer[id]));
+ return 0;
+}
+
+// extern void update_key_led();
+// Lua: wdclr()
+static int ICACHE_FLASH_ATTR tmr_wdclr( lua_State* L )
+{
+ WRITE_PERI_REG(0x60000914, 0x73);
+ // update_key_led();
+ return 0;
+}
+
+// Module function map
+#define MIN_OPT_LEVEL 2
+#include "lrodefs.h"
+const LUA_REG_TYPE tmr_map[] =
+{
+ { LSTRKEY( "delay" ), LFUNCVAL( tmr_delay ) },
+ { LSTRKEY( "now" ), LFUNCVAL( tmr_now ) },
+ { LSTRKEY( "alarm" ), LFUNCVAL( tmr_alarm ) },
+ { LSTRKEY( "stop" ), LFUNCVAL( tmr_stop ) },
+ { LSTRKEY( "wdclr" ), LFUNCVAL( tmr_wdclr ) },
+#if LUA_OPTIMIZE_MEMORY > 0
+
+#endif
+ { LNILKEY, LNILVAL }
+};
+
+LUALIB_API int ICACHE_FLASH_ATTR luaopen_tmr( lua_State *L )
+{
+ int i = 0;
+ for(i=0;i 0
+ return 0;
+#else // #if LUA_OPTIMIZE_MEMORY > 0
+ luaL_register( L, AUXLIB_TMR, tmr_map );
+ // Add constants
+
+ return 1;
+#endif // #if LUA_OPTIMIZE_MEMORY > 0
+}
diff --git a/app/modules/uart.c b/app/modules/uart.c
new file mode 100644
index 00000000..98d3c5dd
--- /dev/null
+++ b/app/modules/uart.c
@@ -0,0 +1,148 @@
+// Module for interfacing with serial
+
+//#include "lua.h"
+#include "lualib.h"
+#include "lauxlib.h"
+#include "platform.h"
+#include "auxmods.h"
+#include "lrotable.h"
+
+#include "c_types.h"
+#include "c_string.h"
+
+static lua_State *gL = NULL;
+static int uart_receive_rf = LUA_NOREF;
+static bool run_input = true;
+bool ICACHE_FLASH_ATTR uart_on_data_cb(const char *buf, size_t len){
+ if(!buf || len==0)
+ return false;
+ if(uart_receive_rf == LUA_NOREF)
+ return false;
+ if(!gL)
+ return false;
+ lua_rawgeti(gL, LUA_REGISTRYINDEX, uart_receive_rf);
+ lua_pushlstring(gL, buf, len);
+ lua_call(gL, 1, 0);
+ return !run_input;
+}
+
+// Lua: uart.on("method", function, [run_input])
+static int ICACHE_FLASH_ATTR uart_on( lua_State* L )
+{
+ size_t sl;
+ int32_t run = 1;
+ const char *method = luaL_checklstring( L, 1, &sl );
+ if (method == NULL)
+ return luaL_error( L, "wrong arg type" );
+
+ // luaL_checkanyfunction(L, 2);
+ if (lua_type(L, 2) == LUA_TFUNCTION || lua_type(L, 2) == LUA_TLIGHTFUNCTION){
+ if ( lua_isnumber(L, 3) ){
+ run = lua_tointeger(L, 3);
+ }
+ lua_pushvalue(L, 2); // copy argument (func) to the top of stack
+ } else {
+ lua_pushnil(L);
+ }
+ if(sl == 4 && c_strcmp(method, "data") == 0){
+ run_input = true;
+ if(uart_receive_rf != LUA_NOREF){
+ luaL_unref(L, LUA_REGISTRYINDEX, uart_receive_rf);
+ uart_receive_rf = LUA_NOREF;
+ }
+ if(!lua_isnil(L, -1)){
+ uart_receive_rf = luaL_ref(L, LUA_REGISTRYINDEX);
+ gL = L;
+ if(run==0)
+ run_input = false;
+ } else {
+ lua_pop(L, 1);
+ }
+ }else{
+ lua_pop(L, 1);
+ return luaL_error( L, "method not supported" );
+ }
+ return 0;
+}
+
+bool uart0_echo = true;
+// Lua: actualbaud = setup( id, baud, databits, parity, stopbits, echo )
+static int uart_setup( lua_State* L )
+{
+ unsigned id, databits, parity, stopbits, echo = 1;
+ u32 baud, res;
+
+ id = luaL_checkinteger( L, 1 );
+ MOD_CHECK_ID( uart, id );
+ baud = luaL_checkinteger( L, 2 );
+ databits = luaL_checkinteger( L, 3 );
+ parity = luaL_checkinteger( L, 4 );
+ stopbits = luaL_checkinteger( L, 5 );
+ if(lua_isnumber(L,6)){
+ echo = lua_tointeger(L,6);
+ if(echo!=0)
+ uart0_echo = true;
+ else
+ uart0_echo = false;
+ }
+
+ res = platform_uart_setup( id, baud, databits, parity, stopbits );
+ lua_pushinteger( L, res );
+ return 1;
+}
+
+// Lua: write( id, string1, [string2], ..., [stringn] )
+static int uart_write( lua_State* L )
+{
+ int id;
+ const char* buf;
+ size_t len, i;
+ int total = lua_gettop( L ), s;
+
+ id = luaL_checkinteger( L, 1 );
+ MOD_CHECK_ID( uart, id );
+ for( s = 2; s <= total; s ++ )
+ {
+ if( lua_type( L, s ) == LUA_TNUMBER )
+ {
+ len = lua_tointeger( L, s );
+ if( len > 255 )
+ return luaL_error( L, "invalid number" );
+ platform_uart_send( id, ( u8 )len );
+ }
+ else
+ {
+ luaL_checktype( L, s, LUA_TSTRING );
+ buf = lua_tolstring( L, s, &len );
+ for( i = 0; i < len; i ++ )
+ platform_uart_send( id, buf[ i ] );
+ }
+ }
+ return 0;
+}
+
+// Module function map
+#define MIN_OPT_LEVEL 2
+#include "lrodefs.h"
+const LUA_REG_TYPE uart_map[] =
+{
+ { LSTRKEY( "setup" ), LFUNCVAL( uart_setup ) },
+ { LSTRKEY( "write" ), LFUNCVAL( uart_write ) },
+ { LSTRKEY( "on" ), LFUNCVAL( uart_on ) },
+#if LUA_OPTIMIZE_MEMORY > 0
+
+#endif
+ { LNILKEY, LNILVAL }
+};
+
+LUALIB_API int ICACHE_FLASH_ATTR luaopen_uart( lua_State *L )
+{
+#if LUA_OPTIMIZE_MEMORY > 0
+ return 0;
+#else // #if LUA_OPTIMIZE_MEMORY > 0
+ luaL_register( L, AUXLIB_UART, uart_map );
+ // Add constants
+
+ return 1;
+#endif // #if LUA_OPTIMIZE_MEMORY > 0
+}
diff --git a/app/modules/wifi.c b/app/modules/wifi.c
new file mode 100644
index 00000000..3748cd3e
--- /dev/null
+++ b/app/modules/wifi.c
@@ -0,0 +1,431 @@
+// Module for interfacing with WIFI
+
+//#include "lua.h"
+#include "lualib.h"
+#include "lauxlib.h"
+#include "platform.h"
+#include "auxmods.h"
+#include "lrotable.h"
+
+#include "c_string.h"
+
+#include "c_types.h"
+#include "user_interface.h"
+#include "smart.h"
+
+static int wifi_smart_succeed = LUA_NOREF;
+
+static void ICACHE_FLASH_ATTR wifi_smart_succeed_cb(void *arg){
+ NODE_DBG("wifi_smart_succeed_cb is called.\n");
+ if( !arg )
+ return;
+ if(wifi_smart_succeed == LUA_NOREF)
+ return;
+ lua_State* L = (lua_State *)arg;
+ lua_rawgeti(L, LUA_REGISTRYINDEX, wifi_smart_succeed);
+ lua_call(L, 0, 0);
+}
+
+static int wifi_scan_succeed = LUA_NOREF;
+static lua_State* gL = NULL;
+/**
+ * @brief Wifi ap scan over callback to display.
+ * @param arg: contain the aps information
+ * @param status: scan over status
+ * @retval None
+ */
+static void ICACHE_FLASH_ATTR
+wifi_scan_done(void *arg, STATUS status)
+{
+ uint8 ssid[33];
+ char temp[128];
+ if(wifi_scan_succeed == LUA_NOREF)
+ return;
+ if(arg == NULL)
+ return;
+
+ lua_rawgeti(gL, LUA_REGISTRYINDEX, wifi_scan_succeed);
+
+ if (status == OK)
+ {
+ struct bss_info *bss_link = (struct bss_info *)arg;
+ bss_link = bss_link->next.stqe_next;//ignore first
+ lua_newtable( gL );
+
+ while (bss_link != NULL)
+ {
+ c_memset(ssid, 0, 33);
+ if (c_strlen(bss_link->ssid) <= 32)
+ {
+ c_memcpy(ssid, bss_link->ssid, c_strlen(bss_link->ssid));
+ }
+ else
+ {
+ c_memcpy(ssid, bss_link->ssid, 32);
+ }
+ c_sprintf(temp,"%d,%d,"MACSTR",%d", bss_link->authmode, bss_link->rssi,
+ MAC2STR(bss_link->bssid),bss_link->channel);
+
+ lua_pushstring(gL, temp);
+ lua_setfield( gL, -2, ssid );
+
+ // NODE_DBG(temp);
+
+ bss_link = bss_link->next.stqe_next;
+ }
+ }
+ else
+ {
+ lua_pushnil(gL);
+ }
+ lua_call(gL, 1, 0);
+}
+
+// Lua: smart(channel, function succeed_cb)
+static int ICACHE_FLASH_ATTR wifi_start_smart( lua_State* L )
+{
+ unsigned channel;
+ int stack = 1;
+
+ if ( lua_isnumber(L, stack) ){
+ channel = lua_tointeger(L, stack);
+ stack++;
+ } else {
+ channel = 6;
+ }
+
+ // luaL_checkanyfunction(L, stack);
+ if (lua_type(L, stack) == LUA_TFUNCTION || lua_type(L, stack) == LUA_TLIGHTFUNCTION){
+ lua_pushvalue(L, stack); // copy argument (func) to the top of stack
+ if(wifi_smart_succeed != LUA_NOREF)
+ luaL_unref(L, LUA_REGISTRYINDEX, wifi_smart_succeed);
+ wifi_smart_succeed = luaL_ref(L, LUA_REGISTRYINDEX);
+ }
+
+ if ( channel > 14 || channel < 1 )
+ return luaL_error( L, "wrong arg range" );
+
+ if(wifi_smart_succeed == LUA_NOREF){
+ smart_begin(channel, NULL, NULL);
+ }else{
+ smart_begin(channel, (smart_succeed )wifi_smart_succeed_cb, L);
+ }
+ return 0;
+}
+
+// Lua: exit_smart(channel)
+static int ICACHE_FLASH_ATTR wifi_exit_smart( lua_State* L )
+{
+ smart_end();
+ if(wifi_smart_succeed != LUA_NOREF)
+ luaL_unref(L, LUA_REGISTRYINDEX, wifi_smart_succeed);
+ wifi_smart_succeed = LUA_NOREF;
+ return 0;
+}
+
+// Lua: realmode = setmode(mode)
+static int ICACHE_FLASH_ATTR wifi_setmode( lua_State* L )
+{
+ unsigned mode;
+
+ mode = luaL_checkinteger( L, 1 );
+
+ if ( mode != STATION_MODE && mode != SOFTAP_MODE && mode != STATIONAP_MODE )
+ return luaL_error( L, "wrong arg type" );
+ wifi_set_opmode( (uint8_t)mode);
+ mode = (unsigned)wifi_get_opmode();
+ lua_pushinteger( L, mode );
+ return 1;
+}
+
+// Lua: realmode = getmode()
+static int ICACHE_FLASH_ATTR wifi_getmode( lua_State* L )
+{
+ unsigned mode;
+ mode = (unsigned)wifi_get_opmode();
+ lua_pushinteger( L, mode );
+ return 1;
+}
+
+
+// Lua: mac = wifi.xx.getmac()
+static int ICACHE_FLASH_ATTR wifi_getmac( lua_State* L, uint8_t mode )
+{
+ char temp[64];
+ uint8_t mac[6];
+ wifi_get_macaddr(mode, mac);
+ c_sprintf(temp, "%02X-%02X-%02X-%02X-%02X-%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5] );
+ lua_pushstring( L, temp );
+ return 1;
+}
+
+// Lua: ip = wifi.xx.getip()
+static int ICACHE_FLASH_ATTR wifi_getip( lua_State* L, uint8_t mode )
+{
+ struct ip_info pTempIp;
+ char temp[64];
+ wifi_get_ip_info(mode, &pTempIp);
+ if(pTempIp.ip.addr==0){
+ lua_pushnil(L);
+ } else {
+ c_sprintf(temp, "%d.%d.%d.%d", IP2STR(&pTempIp.ip) );
+ lua_pushstring( L, temp );
+ }
+ return 1;
+}
+
+// Lua: wifi.sta.getmac()
+static int ICACHE_FLASH_ATTR wifi_station_getmac( lua_State* L ){
+ return wifi_getmac(L, STATION_IF);
+}
+
+// Lua: wifi.sta.getip()
+static int ICACHE_FLASH_ATTR wifi_station_getip( lua_State* L ){
+ return wifi_getip(L, STATION_IF);
+}
+
+// Lua: wifi.sta.config(ssid, password)
+static int ICACHE_FLASH_ATTR wifi_station_config( lua_State* L )
+{
+ size_t sl, pl;
+ struct station_config sta_conf;
+ int i;
+ const char *ssid = luaL_checklstring( L, 1, &sl );
+ if (sl>32 || ssid == NULL)
+ return luaL_error( L, "ssid:<32" );
+ const char *password = luaL_checklstring( L, 2, &pl );
+ if (pl>64 || password == NULL)
+ return luaL_error( L, "pwd:<64" );
+
+ c_memset(sta_conf.ssid, 0, 32);
+ c_memset(sta_conf.password, 0, 64);
+ c_memset(sta_conf.bssid, 0, 6);
+ c_memcpy(sta_conf.ssid, ssid, sl);
+ c_memcpy(sta_conf.password, password, pl);
+ sta_conf.bssid_set = 0;
+
+ NODE_DBG(sta_conf.ssid);
+ NODE_DBG(" %d\n", sl);
+ NODE_DBG(sta_conf.password);
+ NODE_DBG(" %d\n", pl);
+
+ wifi_station_set_config(&sta_conf);
+ wifi_station_set_auto_connect(true);
+ wifi_station_disconnect();
+ wifi_station_connect();
+ // station_check_connect(0);
+ return 0;
+}
+
+// Lua: wifi.sta.connect()
+static int ICACHE_FLASH_ATTR wifi_station_connect4lua( lua_State* L )
+{
+ wifi_station_connect();
+ return 0;
+}
+
+// Lua: wifi.sta.disconnect()
+static int ICACHE_FLASH_ATTR wifi_station_disconnect4lua( lua_State* L )
+{
+ wifi_station_disconnect();
+ return 0;
+}
+
+// Lua: wifi.sta.auto(true/false)
+static int ICACHE_FLASH_ATTR wifi_station_setauto( lua_State* L )
+{
+ unsigned a;
+
+ a = luaL_checkinteger( L, 1 );
+
+ if ( a != 0 && a != 1 )
+ return luaL_error( L, "wrong arg type" );
+ wifi_station_set_auto_connect(a);
+ if(a){
+ // station_check_connect(0);
+ }
+ return 0;
+}
+
+static int ICACHE_FLASH_ATTR wifi_station_listap( lua_State* L )
+{
+ if(wifi_get_opmode() == SOFTAP_MODE)
+ {
+ return luaL_error( L, "Can't list ap in SOFTAP mode" );
+ }
+ gL = L;
+ // luaL_checkanyfunction(L, 1);
+ if (lua_type(L, 1) == LUA_TFUNCTION || lua_type(L, 1) == LUA_TLIGHTFUNCTION){
+ lua_pushvalue(L, 1); // copy argument (func) to the top of stack
+ if(wifi_scan_succeed != LUA_NOREF)
+ luaL_unref(L, LUA_REGISTRYINDEX, wifi_scan_succeed);
+ wifi_scan_succeed = luaL_ref(L, LUA_REGISTRYINDEX);
+ wifi_station_scan(NULL,wifi_scan_done);
+ } else {
+ if(wifi_scan_succeed != LUA_NOREF)
+ luaL_unref(L, LUA_REGISTRYINDEX, wifi_scan_succeed);
+ wifi_scan_succeed = LUA_NOREF;
+ }
+}
+
+// Lua: wifi.sta.status()
+static int ICACHE_FLASH_ATTR wifi_station_status( lua_State* L )
+{
+ uint8_t status = wifi_station_get_connect_status();
+ lua_pushinteger( L, status );
+ return 1;
+}
+
+// Lua: wifi.ap.getmac()
+static int ICACHE_FLASH_ATTR wifi_ap_getmac( lua_State* L ){
+ return wifi_getmac(L, SOFTAP_IF);
+}
+
+// Lua: wifi.ap.getip()
+static int ICACHE_FLASH_ATTR wifi_ap_getip( lua_State* L ){
+ return wifi_getip(L, SOFTAP_IF);
+}
+
+// Lua: wifi.ap.config(table)
+static int ICACHE_FLASH_ATTR wifi_ap_config( lua_State* L )
+{
+ struct softap_config config;
+ size_t len;
+ wifi_softap_get_config(&config);
+ if (!lua_istable(L, 1))
+ return luaL_error( L, "wrong arg type" );
+
+ lua_getfield(L, 1, "ssid");
+ if (!lua_isnil(L, -1)){ /* found? */
+ if( lua_isstring(L, -1) ) // deal with the ssid string
+ {
+ const char *ssid = luaL_checklstring( L, -1, &len );
+ if(len>32)
+ return luaL_error( L, "ssid:<32" );
+ c_memset(config.ssid, 0, 32);
+ c_memcpy(config.ssid, ssid, len);
+ config.ssid_len = len;
+ config.ssid_hidden = 0;
+ NODE_DBG(config.ssid);
+ NODE_DBG("\n");
+ }
+ else
+ return luaL_error( L, "wrong arg type" );
+ }
+ else
+ return luaL_error( L, "wrong arg type" );
+
+ lua_getfield(L, 1, "pwd");
+ if (!lua_isnil(L, -1)){ /* found? */
+ if( lua_isstring(L, -1) ) // deal with the password string
+ {
+ const char *pwd = luaL_checklstring( L, -1, &len );
+ if(len>64)
+ return luaL_error( L, "pwd:<64" );
+ c_memset(config.password, 0, 64);
+ c_memcpy(config.password, pwd, len);
+ config.authmode = AUTH_WPA_WPA2_PSK;
+ NODE_DBG(config.password);
+ NODE_DBG("\n");
+ }
+ else
+ return luaL_error( L, "wrong arg type" );
+ }
+ else{
+ config.authmode = AUTH_OPEN;
+ }
+
+ config.max_connection = 4;
+
+ wifi_softap_set_config(&config);
+ // system_restart();
+ return 0;
+}
+
+// Module function map
+#define MIN_OPT_LEVEL 2
+#include "lrodefs.h"
+static const LUA_REG_TYPE wifi_station_map[] =
+{
+ { LSTRKEY( "config" ), LFUNCVAL ( wifi_station_config ) },
+ { LSTRKEY( "connect" ), LFUNCVAL ( wifi_station_connect4lua ) },
+ { LSTRKEY( "disconnect" ), LFUNCVAL ( wifi_station_disconnect4lua ) },
+ { LSTRKEY( "autoconnect" ), LFUNCVAL ( wifi_station_setauto ) },
+ { LSTRKEY( "getip" ), LFUNCVAL ( wifi_station_getip ) },
+ { LSTRKEY( "getmac" ), LFUNCVAL ( wifi_station_getmac ) },
+ { LSTRKEY( "getap" ), LFUNCVAL ( wifi_station_listap ) },
+ { LSTRKEY( "status" ), LFUNCVAL ( wifi_station_status ) },
+ { LNILKEY, LNILVAL }
+};
+
+static const LUA_REG_TYPE wifi_ap_map[] =
+{
+ { LSTRKEY( "config" ), LFUNCVAL( wifi_ap_config ) },
+ { LSTRKEY( "getip" ), LFUNCVAL ( wifi_ap_getip ) },
+ { LSTRKEY( "getmac" ), LFUNCVAL ( wifi_ap_getmac ) },
+ { LNILKEY, LNILVAL }
+};
+
+const LUA_REG_TYPE wifi_map[] =
+{
+ { LSTRKEY( "setmode" ), LFUNCVAL( wifi_setmode ) },
+ { LSTRKEY( "getmode" ), LFUNCVAL( wifi_getmode ) },
+ { LSTRKEY( "startsmart" ), LFUNCVAL( wifi_start_smart ) },
+ { LSTRKEY( "stopsmart" ), LFUNCVAL( wifi_exit_smart ) },
+#if LUA_OPTIMIZE_MEMORY > 0
+ { LSTRKEY( "sta" ), LROVAL( wifi_station_map ) },
+ { LSTRKEY( "ap" ), LROVAL( wifi_ap_map ) },
+
+ // { LSTRKEY( "NULLMODE" ), LNUMVAL( NULL_MODE ) },
+ { LSTRKEY( "STATION" ), LNUMVAL( STATION_MODE ) },
+ { LSTRKEY( "SOFTAP" ), LNUMVAL( SOFTAP_MODE ) },
+ { LSTRKEY( "STATIONAP" ), LNUMVAL( STATIONAP_MODE ) },
+
+ // { LSTRKEY( "STA_IDLE" ), LNUMVAL( STATION_IDLE ) },
+ // { LSTRKEY( "STA_CONNECTING" ), LNUMVAL( STATION_CONNECTING ) },
+ // { LSTRKEY( "STA_WRONGPWD" ), LNUMVAL( STATION_WRONG_PASSWORD ) },
+ // { LSTRKEY( "STA_APNOTFOUND" ), LNUMVAL( STATION_NO_AP_FOUND ) },
+ // { LSTRKEY( "STA_FAIL" ), LNUMVAL( STATION_CONNECT_FAIL ) },
+ // { LSTRKEY( "STA_GOTIP" ), LNUMVAL( STATION_GOT_IP ) },
+
+ { LSTRKEY( "__metatable" ), LROVAL( wifi_map ) },
+#endif
+ { LNILKEY, LNILVAL }
+};
+
+LUALIB_API int ICACHE_FLASH_ATTR luaopen_wifi( lua_State *L )
+{
+#if LUA_OPTIMIZE_MEMORY > 0
+ return 0;
+#else // #if LUA_OPTIMIZE_MEMORY > 0
+ luaL_register( L, AUXLIB_WIFI, wifi_map );
+
+ // Set it as its own metatable
+ lua_pushvalue( L, -1 );
+ lua_setmetatable( L, -2 );
+
+ // Module constants
+ // MOD_REG_NUMBER( L, "NULLMODE", NULL_MODE );
+ MOD_REG_NUMBER( L, "STATION", STATION_MODE );
+ MOD_REG_NUMBER( L, "SOFTAP", SOFTAP_MODE );
+ MOD_REG_NUMBER( L, "STATIONAP", STATIONAP_MODE );
+
+ // MOD_REG_NUMBER( L, "STA_IDLE", STATION_IDLE );
+ // MOD_REG_NUMBER( L, "STA_CONNECTING", STATION_CONNECTING );
+ // MOD_REG_NUMBER( L, "STA_WRONGPWD", STATION_WRONG_PASSWORD );
+ // MOD_REG_NUMBER( L, "STA_APNOTFOUND", STATION_NO_AP_FOUND );
+ // MOD_REG_NUMBER( L, "STA_FAIL", STATION_CONNECT_FAIL );
+ // MOD_REG_NUMBER( L, "STA_GOTIP", STATION_GOT_IP );
+
+ // Setup the new tables (station and ap) inside wifi
+ lua_newtable( L );
+ luaL_register( L, NULL, wifi_station_map );
+ lua_setfield( L, -2, "sta" );
+
+ lua_newtable( L );
+ luaL_register( L, NULL, wifi_ap_map );
+ lua_setfield( L, -2, "ap" );
+
+ return 1;
+#endif // #if LUA_OPTIMIZE_MEMORY > 0
+}
diff --git a/app/platform/Makefile b/app/platform/Makefile
new file mode 100644
index 00000000..8a6dd55f
--- /dev/null
+++ b/app/platform/Makefile
@@ -0,0 +1,47 @@
+
+#############################################################
+# Required variables for each makefile
+# Discard this section from all parent makefiles
+# Expected variables (with automatic defaults):
+# CSRCS (all "C" files in the dir)
+# SUBDIRS (all subdirs with a Makefile)
+# GEN_LIBS - list of libs to be generated ()
+# GEN_IMAGES - list of images to be generated ()
+# COMPONENTS_xxx - a list of libs/objs in the form
+# subdir/lib to be extracted and rolled up into
+# a generated lib/image xxx.a ()
+#
+ifndef PDIR
+GEN_LIBS = libplatform.a
+endif
+
+#############################################################
+# Configuration i.e. compile options etc.
+# Target specific stuff (defines etc.) goes in here!
+# Generally values applying to a tree are captured in the
+# makefile at its root level - these are then overridden
+# for a subtree within the makefile rooted therein
+#
+#DEFINES +=
+
+#############################################################
+# Recursion Magic - Don't touch this!!
+#
+# Each subtree potentially has an include directory
+# corresponding to the common APIs applicable to modules
+# rooted at that subtree. Accordingly, the INCLUDE PATH
+# of a module can only contain the include directories up
+# its parent path, and not its siblings
+#
+# Required for each makefile to inherit from the parent
+#
+
+INCLUDES := $(INCLUDES) -I $(PDIR)include
+INCLUDES += -I ./
+INCLUDES += -I ../wofs
+INCLUDES += -I ../spiffs
+INCLUDES += -I ../libc
+INCLUDES += -I ../lua
+PDIR := ../$(PDIR)
+sinclude $(PDIR)Makefile
+
diff --git a/app/platform/common.c b/app/platform/common.c
new file mode 100644
index 00000000..ae6a4e9f
--- /dev/null
+++ b/app/platform/common.c
@@ -0,0 +1,260 @@
+// Common code for all backends
+
+#include "platform.h"
+#include "common.h"
+#include "c_string.h"
+#include "c_stdio.h"
+
+void ICACHE_FLASH_ATTR cmn_platform_init(void)
+{
+
+}
+
+// ****************************************************************************
+// GPIO functions
+
+int ICACHE_FLASH_ATTR platform_gpio_exists( unsigned pin )
+{
+ return pin < NUM_GPIO;
+}
+
+// ****************************************************************************
+// CAN functions
+
+int ICACHE_FLASH_ATTR platform_can_exists( unsigned id )
+{
+ return id < NUM_CAN;
+}
+
+// ****************************************************************************
+// SPI functions
+
+
+int ICACHE_FLASH_ATTR platform_spi_exists( unsigned id )
+{
+ return id < NUM_SPI;
+}
+
+// ****************************************************************************
+// PWM functions
+
+int ICACHE_FLASH_ATTR platform_pwm_exists( unsigned id )
+{
+ return ((id < NUM_PWM) && (id > 0));
+}
+
+// ****************************************************************************
+// ADC functions
+
+int ICACHE_FLASH_ATTR platform_adc_exists( unsigned id )
+{
+ return id < NUM_ADC;
+}
+
+// ****************************************************************************
+// UART functions
+
+int ICACHE_FLASH_ATTR platform_uart_exists( unsigned id )
+{
+ return id < NUM_UART;
+}
+
+// ****************************************************************************
+// OneWire functions
+
+int ICACHE_FLASH_ATTR platform_ow_exists( unsigned id )
+{
+ return ((id < NUM_OW) && (id > 0));
+}
+
+// ****************************************************************************
+// Timer functions
+
+int ICACHE_FLASH_ATTR platform_tmr_exists( unsigned id )
+{
+ return id < NUM_TMR;
+}
+
+// I2C support
+int ICACHE_FLASH_ATTR platform_i2c_exists( unsigned id )
+{
+#ifndef NUM_I2C
+ return 0;
+#else
+ return id < NUM_I2C;
+#endif
+}
+
+// ****************************************************************************
+// Internal flash support functions
+
+// This symbol must be exported by the linker command file and must reflect the
+// TOTAL size of flash used by the eLua image (not only the code and constants,
+// but also .data and whatever else ends up in the eLua image). FS will start
+// at the next usable (aligned to a flash sector boundary) address after
+// flash_used_size.
+
+// extern char flash_used_size[];
+extern char _flash_used_end[];
+
+// Helper function: find the flash sector in which an address resides
+// Return the sector number, as well as the start and end address of the sector
+static uint32_t ICACHE_FLASH_ATTR flashh_find_sector( uint32_t address, uint32_t *pstart, uint32_t *pend )
+{
+ address -= INTERNAL_FLASH_START_ADDRESS;
+#ifdef INTERNAL_FLASH_SECTOR_SIZE
+ // All the sectors in the flash have the same size, so just align the address
+ uint32_t sect_id = address / INTERNAL_FLASH_SECTOR_SIZE;
+
+ if( pstart )
+ *pstart = sect_id * INTERNAL_FLASH_SECTOR_SIZE + INTERNAL_FLASH_START_ADDRESS;
+ if( pend )
+ *pend = ( sect_id + 1 ) * INTERNAL_FLASH_SECTOR_SIZE + INTERNAL_FLASH_START_ADDRESS - 1;
+ return sect_id;
+#else // #ifdef INTERNAL_FLASH_SECTOR_SIZE
+ // The flash has blocks of different size
+ // Their size is decribed in the INTERNAL_FLASH_SECTOR_ARRAY macro
+ const uint32_t flash_sect_size[] = INTERNAL_FLASH_SECTOR_ARRAY;
+ uint32_t total = 0, i = 0;
+
+ while( ( total <= address ) && ( i < sizeof( flash_sect_size ) / sizeof( uint32_t ) ) )
+ total += flash_sect_size[ i ++ ];
+ if( pstart )
+ *pstart = ( total - flash_sect_size[ i - 1 ] ) + INTERNAL_FLASH_START_ADDRESS;
+ if( pend )
+ *pend = total + INTERNAL_FLASH_START_ADDRESS - 1;
+ return i - 1;
+#endif // #ifdef INTERNAL_FLASH_SECTOR_SIZE
+}
+
+uint32_t ICACHE_FLASH_ATTR platform_flash_get_sector_of_address( uint32_t addr )
+{
+ return flashh_find_sector( addr, NULL, NULL );
+}
+
+uint32_t ICACHE_FLASH_ATTR platform_flash_get_num_sectors(void)
+{
+#ifdef INTERNAL_FLASH_SECTOR_SIZE
+ return INTERNAL_FLASH_SIZE / INTERNAL_FLASH_SECTOR_SIZE;
+#else // #ifdef INTERNAL_FLASH_SECTOR_SIZE
+ const uint32_t flash_sect_size[] = INTERNAL_FLASH_SECTOR_ARRAY;
+
+ return sizeof( flash_sect_size ) / sizeof( uint32_t );
+#endif // #ifdef INTERNAL_FLASH_SECTOR_SIZE
+}
+
+uint32_t ICACHE_FLASH_ATTR platform_flash_get_first_free_block_address( uint32_t *psect )
+{
+ // Round the total used flash size to the closest flash block address
+ uint32_t start, end, sect;
+ NODE_DBG("_flash_used_end:%08x\n", (uint32_t)_flash_used_end);
+ if(_flash_used_end>0){ // find the used sector
+ // sect = flashh_find_sector( ( uint32_t )flash_used_size + INTERNAL_FLASH_START_ADDRESS - 1, NULL, &end );
+ sect = flashh_find_sector( ( uint32_t )_flash_used_end - 1, NULL, &end );
+ if( psect )
+ *psect = sect + 1;
+ return end + 1;
+ }else{
+ sect = flashh_find_sector( INTERNAL_FLASH_START_ADDRESS, &start, NULL ); // find the first free sector
+ if( psect )
+ *psect = sect;
+ return start;
+ }
+}
+
+uint32_t ICACHE_FLASH_ATTR platform_flash_write( const void *from, uint32_t toaddr, uint32_t size )
+{
+#ifndef INTERNAL_FLASH_WRITE_UNIT_SIZE
+ return platform_s_flash_write( from, toaddr, size );
+#else // #ifindef INTERNAL_FLASH_WRITE_UNIT_SIZE
+ uint32_t temp, rest, ssize = size;
+ unsigned i;
+ char tmpdata[ INTERNAL_FLASH_WRITE_UNIT_SIZE ];
+ const uint8_t *pfrom = ( const uint8_t* )from;
+ const uint32_t blksize = INTERNAL_FLASH_WRITE_UNIT_SIZE;
+ const uint32_t blkmask = INTERNAL_FLASH_WRITE_UNIT_SIZE - 1;
+
+ // Align the start
+ if( toaddr & blkmask )
+ {
+ rest = toaddr & blkmask;
+ temp = toaddr & ~blkmask; // this is the actual aligned address
+ // c_memcpy( tmpdata, ( const void* )temp, blksize );
+ platform_s_flash_read( tmpdata, temp, blksize );
+ for( i = rest; size && ( i < blksize ); i ++, size --, pfrom ++ )
+ tmpdata[ i ] = *pfrom;
+ platform_s_flash_write( tmpdata, temp, blksize );
+ if( size == 0 )
+ return ssize;
+ toaddr = temp + blksize;
+ }
+ // The start address is now a multiple of blksize
+ // Compute how many bytes we can write as multiples of blksize
+ rest = size & blkmask;
+ temp = size & ~blkmask;
+ // Program the blocks now
+ if( temp )
+ {
+ platform_s_flash_write( pfrom, toaddr, temp );
+ toaddr += temp;
+ pfrom += temp;
+ }
+ // And the final part of a block if needed
+ if( rest )
+ {
+ // c_memcpy( tmpdata, ( const void* )toaddr, blksize );
+ platform_s_flash_read( tmpdata, toaddr, blksize );
+ for( i = 0; size && ( i < rest ); i ++, size --, pfrom ++ )
+ tmpdata[ i ] = *pfrom;
+ platform_s_flash_write( tmpdata, toaddr, blksize );
+ }
+ return ssize;
+#endif // #ifndef INTERNAL_FLASH_WRITE_UNIT_SIZE
+}
+
+uint32_t ICACHE_FLASH_ATTR platform_flash_read( void *to, uint32_t fromaddr, uint32_t size )
+{
+#ifndef INTERNAL_FLASH_READ_UNIT_SIZE
+ return platform_s_flash_read( to, fromaddr, size );
+#else // #ifindef INTERNAL_FLASH_READ_UNIT_SIZE
+ uint32_t temp, rest, ssize = size;
+ unsigned i;
+ char tmpdata[ INTERNAL_FLASH_READ_UNIT_SIZE ];
+ uint8_t *pto = ( uint8_t* )to;
+ const uint32_t blksize = INTERNAL_FLASH_READ_UNIT_SIZE;
+ const uint32_t blkmask = INTERNAL_FLASH_READ_UNIT_SIZE - 1;
+
+ // Align the start
+ if( fromaddr & blkmask )
+ {
+ rest = fromaddr & blkmask;
+ temp = fromaddr & ~blkmask; // this is the actual aligned address
+ platform_s_flash_read( tmpdata, temp, blksize );
+ for( i = rest; size && ( i < blksize ); i ++, size --, pto ++ )
+ *pto = tmpdata[ i ];
+
+ if( size == 0 )
+ return ssize;
+ fromaddr = temp + blksize;
+ }
+ // The start address is now a multiple of blksize
+ // Compute how many bytes we can read as multiples of blksize
+ rest = size & blkmask;
+ temp = size & ~blkmask;
+ // Program the blocks now
+ if( temp )
+ {
+ platform_s_flash_read( pto, fromaddr, temp );
+ fromaddr += temp;
+ pto += temp;
+ }
+ // And the final part of a block if needed
+ if( rest )
+ {
+ platform_s_flash_read( tmpdata, fromaddr, blksize );
+ for( i = 0; size && ( i < rest ); i ++, size --, pto ++ )
+ *pto = tmpdata[ i ];
+ }
+ return ssize;
+#endif // #ifndef INTERNAL_FLASH_READ_UNIT_SIZE
+}
diff --git a/app/platform/common.h b/app/platform/common.h
new file mode 100644
index 00000000..7ea6f269
--- /dev/null
+++ b/app/platform/common.h
@@ -0,0 +1,18 @@
+// Common platform functions
+
+#ifndef __COMMON_H__
+#define __COMMON_H__
+
+#include "platform.h"
+
+// Functions exported by the common platform layer
+void cmn_platform_init(void);
+
+// Timer-specific functions
+
+// System timer generic implemenation
+
+// Filesystem-related functions
+
+#endif // #ifndef __COMMON_H__
+
diff --git a/app/platform/cpu_esp8266.h b/app/platform/cpu_esp8266.h
new file mode 100644
index 00000000..ecccee93
--- /dev/null
+++ b/app/platform/cpu_esp8266.h
@@ -0,0 +1,54 @@
+#ifndef __CPU_ESP8266_H__
+#define __CPU_ESP8266_H__
+
+#include "os_type.h"
+#include "spi_flash.h"
+#include "pin_map.h"
+#include "user_config.h"
+// Number of resources (0 if not available/not implemented)
+#define NUM_GPIO GPIO_PIN_NUM
+#define NUM_SPI 1
+#define NUM_UART 1
+#define NUM_PWM GPIO_PIN_NUM
+#define NUM_ADC 1
+#define NUM_CAN 0
+#define NUM_I2C 1
+#define NUM_OW GPIO_PIN_NUM
+#define NUM_TMR 7
+
+#if defined(FLASH_512K)
+#define FLASH_SEC_NUM 0x80 // 4MByte: 0x400, 2MByte: 0x200, 1MByte: 0x100, 512KByte: 0x80
+#elif defined(FLASH_1M)
+#define FLASH_SEC_NUM 0x100
+#elif defined(FLASH_2M)
+#define FLASH_SEC_NUM 0x200
+#elif defined(FLASH_4M)
+#define FLASH_SEC_NUM 0x400
+#else
+#define FLASH_SEC_NUM 0x80
+#endif
+#define SYS_PARAM_SEC_NUM 4
+#define SYS_PARAM_SEC_START (FLASH_SEC_NUM - SYS_PARAM_SEC_NUM)
+
+// #define WOFS_SEC_START 0x80
+// #define WOFS_SEC_START 0x60
+// #define WOFS_SEC_END (SYS_PARAM_SEC_START)
+// #define WOFS_SEC_NUM (WOFS_SEC_END - WOFS_SEC_START)
+// #define WOFS_SEC_NUM 0xc
+
+#define INTERNAL_FLASH_SECTOR_SIZE SPI_FLASH_SEC_SIZE
+// #define INTERNAL_FLASH_SECTOR_ARRAY { 0x4000, 0x4000, 0x4000, 0x4000, 0x10000, 0x20000, 0x20000, 0x20000, 0x20000, 0x20000 }
+#define INTERNAL_FLASH_WRITE_UNIT_SIZE 4
+#define INTERNAL_FLASH_READ_UNIT_SIZE 4
+
+#define INTERNAL_FLASH_SIZE ( (SYS_PARAM_SEC_START) * INTERNAL_FLASH_SECTOR_SIZE )
+#define INTERNAL_FLASH_START_ADDRESS 0x40200000
+
+// 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);
+#define flash_write spi_flash_write
+#define flash_erase spi_flash_erase_sector
+#define flash_read spi_flash_read
+
+#endif // #ifndef __CPU_ESP8266_H__
diff --git a/app/platform/flash_fs.c b/app/platform/flash_fs.c
new file mode 100644
index 00000000..b8f6373a
--- /dev/null
+++ b/app/platform/flash_fs.c
@@ -0,0 +1,32 @@
+#include "flash_fs.h"
+#include "c_string.h"
+
+#if defined( BUILD_WOFS )
+#include "romfs.h"
+#elif defined( BUILD_SPIFFS )
+#include "spiffs.h"
+#endif
+
+int ICACHE_FLASH_ATTR fs_mode2flag(const char *mode){
+ if(c_strlen(mode)==1){
+ if(c_strcmp(mode,"w")==0)
+ return FS_WRONLY|FS_CREAT|FS_TRUNC;
+ else if(c_strcmp(mode, "r")==0)
+ return FS_RDONLY;
+ else if(c_strcmp(mode, "a")==0)
+ return FS_WRONLY|FS_CREAT|FS_APPEND;
+ else
+ return FS_RDONLY;
+ } else if (c_strlen(mode)==2){
+ if(c_strcmp(mode,"r+")==0)
+ return FS_RDWR;
+ else if(c_strcmp(mode, "w+")==0)
+ return FS_RDWR|FS_CREAT|FS_TRUNC;
+ else if(c_strcmp(mode, "a+")==0)
+ return FS_RDWR|FS_CREAT|FS_APPEND;
+ else
+ return FS_RDONLY;
+ } else {
+ return FS_RDONLY;
+ }
+}
diff --git a/app/platform/flash_fs.h b/app/platform/flash_fs.h
new file mode 100644
index 00000000..3ab0421a
--- /dev/null
+++ b/app/platform/flash_fs.h
@@ -0,0 +1,78 @@
+
+#ifndef __FLASH_FS_H__
+#define __FLASH_FS_H__
+
+#include "user_config.h"
+
+#if defined( BUILD_WOFS )
+#include "romfs.h"
+
+#define FS_OPEN_OK 0
+
+#define FS_RDONLY O_RDONLY
+#define FS_WRONLY O_WRONLY
+#define FS_RDWR O_RDWR
+#define FS_APPEND O_APPEND
+#define FS_TRUNC O_TRUNC
+#define FS_CREAT O_CREAT
+#define FS_EXCL O_EXCL
+
+#define FS_SEEK_SET SEEK_SET
+#define FS_SEEK_CUR SEEK_CUR
+#define FS_SEEK_END SEEK_END
+
+#define fs_open wofs_open
+#define fs_close wofs_close
+#define fs_write wofs_write
+#define fs_read wofs_read
+#define fs_seek wofs_lseek
+#define fs_eof wofs_eof
+#define fs_getc wofs_getc
+#define fs_ungetc wofs_ungetc
+
+#define fs_format wofs_format
+#define fs_next wofs_next
+
+#define FS_NAME_MAX_LENGTH MAX_FNAME_LENGTH
+
+#elif defined( BUILD_SPIFFS )
+
+#include "spiffs.h"
+
+#define FS_OPEN_OK 1
+
+#define FS_RDONLY SPIFFS_RDONLY
+#define FS_WRONLY SPIFFS_WRONLY
+#define FS_RDWR SPIFFS_RDWR
+#define FS_APPEND SPIFFS_APPEND
+#define FS_TRUNC SPIFFS_TRUNC
+#define FS_CREAT SPIFFS_CREAT
+#define FS_EXCL SPIFFS_EXCL
+
+#define FS_SEEK_SET SPIFFS_SEEK_SET
+#define FS_SEEK_CUR SPIFFS_SEEK_CUR
+#define FS_SEEK_END SPIFFS_SEEK_END
+
+#define fs_open myspiffs_open
+#define fs_close myspiffs_close
+#define fs_write myspiffs_write
+#define fs_read myspiffs_read
+#define fs_seek myspiffs_lseek
+#define fs_eof myspiffs_eof
+#define fs_getc myspiffs_getc
+#define fs_ungetc myspiffs_ungetc
+#define fs_flush myspiffs_flush
+#define fs_error myspiffs_error
+#define fs_clearerr myspiffs_clearerr
+#define fs_tell myspiffs_tell
+
+#define fs_format myspiffs_format
+#define fs_check myspiffs_check
+
+#define FS_NAME_MAX_LENGTH SPIFFS_OBJ_NAME_LEN
+
+#endif
+
+int fs_mode2flag(const char *mode);
+
+#endif // #ifndef __FLASH_FS_H__
diff --git a/app/platform/pin_map.c b/app/platform/pin_map.c
new file mode 100644
index 00000000..a3f64859
--- /dev/null
+++ b/app/platform/pin_map.c
@@ -0,0 +1,45 @@
+#include "pin_map.h"
+#include "eagle_soc.h"
+#if 0
+uint32_t pin_mux[GPIO_PIN_NUM] = {PERIPHS_IO_MUX_MTDI_U, PERIPHS_IO_MUX_MTCK_U, PERIPHS_IO_MUX_MTMS_U, PERIPHS_IO_MUX_MTDO_U,
+ PERIPHS_IO_MUX_U0RXD_U, PERIPHS_IO_MUX_U0TXD_U, PERIPHS_IO_MUX_SD_DATA2_U, PERIPHS_IO_MUX_SD_DATA3_U,
+ PERIPHS_IO_MUX_GPIO0_U, PERIPHS_IO_MUX_GPIO2_U, PERIPHS_IO_MUX_GPIO4_U, PERIPHS_IO_MUX_GPIO5_U};
+
+uint8_t pin_num[GPIO_PIN_NUM] = {12, 13, 14, 15,
+ 3, 1, 9, 10,
+ 0, 2, 4, 5};
+
+uint8_t pin_func[GPIO_PIN_NUM] = {FUNC_GPIO12, FUNC_GPIO13, FUNC_GPIO14, FUNC_GPIO15,
+ FUNC_GPIO3, FUNC_GPIO1, FUNC_GPIO9, FUNC_GPIO10,
+ FUNC_GPIO0, FUNC_GPIO2, FUNC_GPIO4, FUNC_GPIO5};
+
+#ifdef GPIO_INTERRUPT_ENABLE
+GPIO_INT_TYPE pin_int_type[GPIO_PIN_NUM] = {
+ GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE,
+ GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE,
+ GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE};
+#endif
+#else
+uint32_t pin_mux[GPIO_PIN_NUM] = {PAD_XPD_DCDC_CONF, PERIPHS_IO_MUX_GPIO4_U, PERIPHS_IO_MUX_GPIO5_U, PERIPHS_IO_MUX_GPIO0_U,
+ PERIPHS_IO_MUX_GPIO2_U, PERIPHS_IO_MUX_MTMS_U, PERIPHS_IO_MUX_MTDI_U, PERIPHS_IO_MUX_MTCK_U,
+ PERIPHS_IO_MUX_MTDO_U, PERIPHS_IO_MUX_U0RXD_U, PERIPHS_IO_MUX_U0TXD_U, PERIPHS_IO_MUX_SD_DATA2_U,
+ PERIPHS_IO_MUX_SD_DATA3_U };
+
+uint8_t pin_num[GPIO_PIN_NUM] = {16, 4, 5, 0,
+ 2, 14, 12, 13,
+ 15, 3, 1, 9,
+ 10};
+
+uint8_t pin_func[GPIO_PIN_NUM] = {0, FUNC_GPIO4, FUNC_GPIO5, FUNC_GPIO0,
+ FUNC_GPIO2, FUNC_GPIO14, FUNC_GPIO12, FUNC_GPIO13,
+ FUNC_GPIO15, FUNC_GPIO3, FUNC_GPIO1, FUNC_GPIO9,
+ FUNC_GPIO10};
+
+#ifdef GPIO_INTERRUPT_ENABLE
+GPIO_INT_TYPE pin_int_type[GPIO_PIN_NUM] = {
+ GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE,
+ GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE,
+ GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE,
+ GPIO_PIN_INTR_DISABLE};
+#endif
+#endif
diff --git a/app/platform/pin_map.h b/app/platform/pin_map.h
new file mode 100644
index 00000000..5b4a68dd
--- /dev/null
+++ b/app/platform/pin_map.h
@@ -0,0 +1,17 @@
+
+#ifndef __PIN_MAP_H__
+#define __PIN_MAP_H__
+
+#include "c_types.h"
+#include "user_config.h"
+#include "gpio.h"
+
+#define GPIO_PIN_NUM 13
+
+extern uint8_t pin_num[GPIO_PIN_NUM];
+extern uint8_t pin_func[GPIO_PIN_NUM];
+extern uint32_t pin_mux[GPIO_PIN_NUM];
+#ifdef GPIO_INTERRUPT_ENABLE
+extern GPIO_INT_TYPE pin_int_type[GPIO_PIN_NUM];
+#endif
+#endif // #ifndef __PIN_MAP_H__
diff --git a/app/platform/platform.c b/app/platform/platform.c
new file mode 100644
index 00000000..b69599c2
--- /dev/null
+++ b/app/platform/platform.c
@@ -0,0 +1,483 @@
+// Platform-dependent functions
+
+#include "platform.h"
+#include "c_stdio.h"
+#include "c_string.h"
+#include "c_stdlib.h"
+#include "gpio.h"
+#include "user_interface.h"
+#include "driver/uart.h"
+// Platform specific includes
+
+static void pwms_init();
+
+int ICACHE_FLASH_ATTR platform_init()
+{
+ // Setup PWMs
+ pwms_init();
+
+ cmn_platform_init();
+ // All done
+ return PLATFORM_OK;
+}
+
+// ****************************************************************************
+// KEY_LED functions
+uint8_t ICACHE_FLASH_ATTR platform_key_led( uint8_t level){
+ uint8_t temp;
+ gpio16_output_set(1); // set to high first, for reading key low level
+ gpio16_input_conf();
+ temp = gpio16_input_get();
+ gpio16_output_conf();
+ gpio16_output_set(level);
+ return temp;
+}
+
+// ****************************************************************************
+// GPIO functions
+#ifdef GPIO_INTERRUPT_ENABLE
+extern void lua_gpio_unref(unsigned pin);
+#endif
+int ICACHE_FLASH_ATTR platform_gpio_mode( unsigned pin, unsigned mode, unsigned pull )
+{
+ // NODE_DBG("Function platform_gpio_mode() is called. pin_mux:%d, func:%d\n",pin_mux[pin],pin_func[pin]);
+ if (pin >= NUM_GPIO)
+ return -1;
+ if(pin == 0){
+ if(mode==PLATFORM_GPIO_INPUT)
+ gpio16_input_conf();
+ else
+ gpio16_output_conf();
+ return 1;
+ }
+
+ platform_pwm_close(pin); // closed from pwm module, if it is used in pwm
+
+ switch(pull){
+ case PLATFORM_GPIO_PULLUP:
+ PIN_PULLDWN_DIS(pin_mux[pin]);
+ PIN_PULLUP_EN(pin_mux[pin]);
+ break;
+ case PLATFORM_GPIO_PULLDOWN:
+ PIN_PULLUP_DIS(pin_mux[pin]);
+ PIN_PULLDWN_EN(pin_mux[pin]);
+ break;
+ case PLATFORM_GPIO_FLOAT:
+ PIN_PULLUP_DIS(pin_mux[pin]);
+ PIN_PULLDWN_DIS(pin_mux[pin]);
+ break;
+ default:
+ PIN_PULLUP_DIS(pin_mux[pin]);
+ PIN_PULLDWN_DIS(pin_mux[pin]);
+ break;
+ }
+
+ switch(mode){
+ case PLATFORM_GPIO_INPUT:
+#ifdef GPIO_INTERRUPT_ENABLE
+ lua_gpio_unref(pin); // unref the lua ref call back.
+#endif
+ GPIO_DIS_OUTPUT(pin_num[pin]);
+ case PLATFORM_GPIO_OUTPUT:
+ ETS_GPIO_INTR_DISABLE();
+#ifdef GPIO_INTERRUPT_ENABLE
+ pin_int_type[pin] = GPIO_PIN_INTR_DISABLE;
+#endif
+ PIN_FUNC_SELECT(pin_mux[pin], pin_func[pin]);
+ //disable interrupt
+ gpio_pin_intr_state_set(GPIO_ID_PIN(pin_num[pin]), GPIO_PIN_INTR_DISABLE);
+ //clear interrupt status
+ GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, BIT(pin_num[pin]));
+ GPIO_REG_WRITE(GPIO_PIN_ADDR(GPIO_ID_PIN(pin_num[pin])), GPIO_REG_READ(GPIO_PIN_ADDR(GPIO_ID_PIN(pin_num[pin]))) & (~ GPIO_PIN_PAD_DRIVER_SET(GPIO_PAD_DRIVER_ENABLE))); //disable open drain;
+ ETS_GPIO_INTR_ENABLE();
+ break;
+#ifdef GPIO_INTERRUPT_ENABLE
+ case PLATFORM_GPIO_INT:
+ ETS_GPIO_INTR_DISABLE();
+ PIN_FUNC_SELECT(pin_mux[pin], pin_func[pin]);
+ GPIO_DIS_OUTPUT(pin_num[pin]);
+ gpio_register_set(GPIO_PIN_ADDR(GPIO_ID_PIN(pin_num[pin])), GPIO_PIN_INT_TYPE_SET(GPIO_PIN_INTR_DISABLE)
+ | GPIO_PIN_PAD_DRIVER_SET(GPIO_PAD_DRIVER_DISABLE)
+ | GPIO_PIN_SOURCE_SET(GPIO_AS_PIN_SOURCE));
+ ETS_GPIO_INTR_ENABLE();
+ break;
+#endif
+ default:
+ break;
+ }
+ return 1;
+}
+
+int ICACHE_FLASH_ATTR platform_gpio_write( unsigned pin, unsigned level )
+{
+ // NODE_DBG("Function platform_gpio_write() is called. pin:%d, level:%d\n",GPIO_ID_PIN(pin_num[pin]),level);
+ if (pin >= NUM_GPIO)
+ return -1;
+ if(pin == 0){
+ gpio16_output_conf();
+ gpio16_output_set(level);
+ return 1;
+ }
+
+ GPIO_OUTPUT_SET(GPIO_ID_PIN(pin_num[pin]), level);
+}
+
+int ICACHE_FLASH_ATTR platform_gpio_read( unsigned pin )
+{
+ // NODE_DBG("Function platform_gpio_read() is called. pin:%d\n",GPIO_ID_PIN(pin_num[pin]));
+ if (pin >= NUM_GPIO)
+ return -1;
+
+ if(pin == 0){
+ gpio16_input_conf();
+ return 0x1 & gpio16_input_get();
+ }
+
+ GPIO_DIS_OUTPUT(pin_num[pin]);
+ return 0x1 & GPIO_INPUT_GET(GPIO_ID_PIN(pin_num[pin]));
+}
+
+#ifdef GPIO_INTERRUPT_ENABLE
+static void ICACHE_FLASH_ATTR platform_gpio_intr_dispatcher( platform_gpio_intr_handler_fn_t cb){
+ uint8 i, level;
+ uint32 gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS);
+ for (i = 0; i < GPIO_PIN_NUM; i++) {
+ if (pin_int_type[i] && (gpio_status & BIT(pin_num[i])) ) {
+ //disable interrupt
+ gpio_pin_intr_state_set(GPIO_ID_PIN(pin_num[i]), GPIO_PIN_INTR_DISABLE);
+ //clear interrupt status
+ GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status & BIT(pin_num[i]));
+ level = 0x1 & GPIO_INPUT_GET(GPIO_ID_PIN(pin_num[i]));
+ if(cb){
+ cb(i, level);
+ }
+ gpio_pin_intr_state_set(GPIO_ID_PIN(pin_num[i]), pin_int_type[i]);
+ }
+ }
+}
+
+void ICACHE_FLASH_ATTR platform_gpio_init( platform_gpio_intr_handler_fn_t cb )
+{
+ ETS_GPIO_INTR_ATTACH(platform_gpio_intr_dispatcher, cb);
+}
+
+int ICACHE_FLASH_ATTR platform_gpio_intr_init( unsigned pin, GPIO_INT_TYPE type )
+{
+ if (pin >= NUM_GPIO)
+ return -1;
+ ETS_GPIO_INTR_DISABLE();
+ //clear interrupt status
+ GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, BIT(pin_num[pin]));
+ pin_int_type[pin] = type;
+ //enable interrupt
+ gpio_pin_intr_state_set(GPIO_ID_PIN(pin_num[pin]), type);
+ ETS_GPIO_INTR_ENABLE();
+}
+#endif
+
+// ****************************************************************************
+// UART
+// TODO: Support timeouts.
+
+// UartDev is defined and initialized in rom code.
+extern UartDevice UartDev;
+uint32_t ICACHE_FLASH_ATTR platform_uart_setup( unsigned id, uint32_t baud, int databits, int parity, int stopbits )
+{
+ switch( baud )
+ {
+ case BIT_RATE_9600:
+ case BIT_RATE_19200:
+ case BIT_RATE_38400:
+ case BIT_RATE_57600:
+ case BIT_RATE_74880:
+ case BIT_RATE_115200:
+ case BIT_RATE_230400:
+ case BIT_RATE_460800:
+ case BIT_RATE_921600:
+ UartDev.baut_rate = baud;
+ break;
+ default:
+ UartDev.baut_rate = BIT_RATE_9600;
+ break;
+ }
+
+ switch( databits )
+ {
+ case 5:
+ UartDev.data_bits = FIVE_BITS;
+ break;
+ case 6:
+ UartDev.data_bits = SIX_BITS;
+ break;
+ case 7:
+ UartDev.data_bits = SEVEN_BITS;
+ break;
+ case 8:
+ UartDev.data_bits = EIGHT_BITS;
+ break;
+ default:
+ UartDev.data_bits = EIGHT_BITS;
+ break;
+ }
+
+ switch (stopbits)
+ {
+ case PLATFORM_UART_STOPBITS_1:
+ UartDev.stop_bits = ONE_STOP_BIT;
+ break;
+ case PLATFORM_UART_STOPBITS_2:
+ UartDev.stop_bits = TWO_STOP_BIT;
+ break;
+ default:
+ UartDev.stop_bits = ONE_STOP_BIT;
+ break;
+ }
+
+ switch (parity)
+ {
+ case PLATFORM_UART_PARITY_EVEN:
+ UartDev.parity = EVEN_BITS;
+ break;
+ case PLATFORM_UART_PARITY_ODD:
+ UartDev.parity = ODD_BITS;
+ break;
+ default:
+ UartDev.parity = NONE_BITS;
+ break;
+ }
+
+ uart_setup(id);
+
+ return baud;
+}
+
+// Send: version with and without mux
+void platform_uart_send( unsigned id, u8 data )
+{
+ uart_tx_one_char(id, data);
+}
+
+// ****************************************************************************
+// PWMs
+
+static uint16_t pwms_duty[NUM_PWM] = {0};
+
+static void ICACHE_FLASH_ATTR pwms_init()
+{
+ int i;
+ for(i=0;i= NUM_PWM)
+ return 0;
+ if(!pwm_exist(pin))
+ return 0;
+
+ return (uint32_t)pwm_get_freq(pin);
+}
+
+// Set the PWM clock
+uint32_t ICACHE_FLASH_ATTR platform_pwm_set_clock( unsigned pin, uint32_t clock )
+{
+ // NODE_DBG("Function platform_pwm_set_clock() is called.\n");
+ if( pin >= NUM_PWM)
+ return 0;
+ if(!pwm_exist(pin))
+ return 0;
+
+ pwm_set_freq((uint16_t)clock, pin);
+ pwm_start();
+ return (uint32_t)pwm_get_freq( pin );
+}
+
+uint32_t ICACHE_FLASH_ATTR platform_pwm_get_duty( unsigned pin )
+{
+ // NODE_DBG("Function platform_pwm_get_duty() is called.\n");
+ if( pin < NUM_PWM){
+ if(!pwm_exist(pin))
+ return 0;
+ // return NORMAL_DUTY(pwm_get_duty(pin));
+ return pwms_duty[pin];
+ }
+ return 0;
+}
+
+// Set the PWM duty
+uint32_t ICACHE_FLASH_ATTR platform_pwm_set_duty( unsigned pin, uint32_t duty )
+{
+ // NODE_DBG("Function platform_pwm_set_duty() is called.\n");
+ if ( pin < NUM_PWM)
+ {
+ if(!pwm_exist(pin))
+ return 0;
+ pwm_set_duty(DUTY(duty), pin);
+ } else {
+ return 0;
+ }
+ pwm_start();
+ pwms_duty[pin] = NORMAL_DUTY(pwm_get_duty(pin));
+ return pwms_duty[pin];
+}
+
+uint32_t ICACHE_FLASH_ATTR platform_pwm_setup( unsigned pin, uint32_t frequency, unsigned duty )
+{
+ uint32_t clock;
+ if ( pin < NUM_PWM)
+ {
+ platform_gpio_mode(pin, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT); // disable gpio interrupt first
+ if(!pwm_add(pin))
+ return 0;
+ // pwm_set_duty(DUTY(duty), pin);
+ pwm_set_duty(0, pin);
+ pwms_duty[pin] = duty;
+ pwm_set_freq((uint16_t)frequency, pin);
+ } else {
+ return 0;
+ }
+ clock = platform_pwm_get_clock( pin );
+ pwm_start();
+ return clock;
+}
+
+void ICACHE_FLASH_ATTR platform_pwm_close( unsigned pin )
+{
+ // NODE_DBG("Function platform_pwm_stop() is called.\n");
+ if ( pin < NUM_PWM)
+ {
+ pwm_delete(pin);
+ pwm_start();
+ }
+}
+
+void ICACHE_FLASH_ATTR platform_pwm_start( unsigned pin )
+{
+ // NODE_DBG("Function platform_pwm_start() is called.\n");
+ if ( pin < NUM_PWM)
+ {
+ if(!pwm_exist(pin))
+ return;
+ pwm_set_duty(DUTY(pwms_duty[pin]), pin);
+ pwm_start();
+ }
+}
+
+void ICACHE_FLASH_ATTR platform_pwm_stop( unsigned pin )
+{
+ // NODE_DBG("Function platform_pwm_stop() is called.\n");
+ if ( pin < NUM_PWM)
+ {
+ if(!pwm_exist(pin))
+ return;
+ pwm_set_duty(0, pin);
+ pwm_start();
+ }
+}
+
+// *****************************************************************************
+// I2C platform interface
+
+uint32_t ICACHE_FLASH_ATTR platform_i2c_setup( unsigned id, uint8_t sda, uint8_t scl, uint32_t speed ){
+ if (sda >= NUM_GPIO || scl >= NUM_GPIO)
+ return 0;
+
+ // platform_pwm_close(sda);
+ // platform_pwm_close(scl);
+
+ // disable gpio interrupt first
+ platform_gpio_mode(sda, PLATFORM_GPIO_INPUT, PLATFORM_GPIO_PULLUP); // inside this func call platform_pwm_close
+ platform_gpio_mode(scl, PLATFORM_GPIO_INPUT, PLATFORM_GPIO_PULLUP); // disable gpio interrupt first
+
+ i2c_master_gpio_init(sda, scl);
+ return PLATFORM_I2C_SPEED_SLOW;
+}
+
+void ICACHE_FLASH_ATTR platform_i2c_send_start( unsigned id ){
+ i2c_master_start();
+}
+
+void ICACHE_FLASH_ATTR platform_i2c_send_stop( unsigned id ){
+ i2c_master_stop();
+}
+
+int ICACHE_FLASH_ATTR platform_i2c_send_address( unsigned id, uint16_t address, int direction ){
+ // Convert enum codes to R/w bit value.
+ // If TX == 0 and RX == 1, this test will be removed by the compiler
+ if ( ! ( PLATFORM_I2C_DIRECTION_TRANSMITTER == 0 &&
+ PLATFORM_I2C_DIRECTION_RECEIVER == 1 ) ) {
+ direction = ( direction == PLATFORM_I2C_DIRECTION_TRANSMITTER ) ? 0 : 1;
+ }
+
+ i2c_master_writeByte( (uint8_t) ((address << 1) | direction ));
+ // Low-level returns nack (0=acked); we return ack (1=acked).
+ return ! i2c_master_getAck();
+}
+
+int ICACHE_FLASH_ATTR platform_i2c_send_byte( unsigned id, uint8_t data ){
+ i2c_master_writeByte(data);
+ // Low-level returns nack (0=acked); we return ack (1=acked).
+ return ! i2c_master_getAck();
+}
+
+int ICACHE_FLASH_ATTR platform_i2c_recv_byte( unsigned id, int ack ){
+ uint8_t r = i2c_master_readByte();
+ i2c_master_setAck( !ack );
+ return r;
+}
+
+// ****************************************************************************
+// Flash access functions
+
+uint32_t ICACHE_FLASH_ATTR platform_s_flash_write( const void *from, uint32_t toaddr, uint32_t size )
+{
+ toaddr -= INTERNAL_FLASH_START_ADDRESS;
+ SpiFlashOpResult r;
+ const uint32_t blkmask = INTERNAL_FLASH_WRITE_UNIT_SIZE - 1;
+ uint32_t *apbuf = NULL;
+ if( ((uint32_t)from) & blkmask ){
+ apbuf = (uint32_t *)c_malloc(size);
+ if(!apbuf)
+ return 0;
+ c_memcpy(apbuf, from, size);
+ }
+ WRITE_PERI_REG(0x60000914, 0x73);
+ r = flash_write(toaddr, apbuf?(uint32 *)apbuf:(uint32 *)from, size);
+ if(apbuf)
+ c_free(apbuf);
+ if(SPI_FLASH_RESULT_OK == r)
+ return size;
+ else{
+ NODE_ERR( "ERROR in flash_write: r=%d at %08X\n", ( int )r, ( unsigned )toaddr+INTERNAL_FLASH_START_ADDRESS );
+ return 0;
+ }
+}
+
+uint32_t ICACHE_FLASH_ATTR platform_s_flash_read( void *to, uint32_t fromaddr, uint32_t size )
+{
+ fromaddr -= INTERNAL_FLASH_START_ADDRESS;
+ SpiFlashOpResult r;
+ WRITE_PERI_REG(0x60000914, 0x73);
+ r = flash_read(fromaddr, (uint32 *)to, size);
+ if(SPI_FLASH_RESULT_OK == r)
+ return size;
+ else{
+ NODE_ERR( "ERROR in flash_read: r=%d at %08X\n", ( int )r, ( unsigned )fromaddr+INTERNAL_FLASH_START_ADDRESS );
+ return 0;
+ }
+}
+
+int ICACHE_FLASH_ATTR platform_flash_erase_sector( uint32_t sector_id )
+{
+ WRITE_PERI_REG(0x60000914, 0x73);
+ return flash_erase( sector_id ) == SPI_FLASH_RESULT_OK ? PLATFORM_OK : PLATFORM_ERR;
+}
diff --git a/app/platform/platform.h b/app/platform/platform.h
new file mode 100644
index 00000000..286dab2d
--- /dev/null
+++ b/app/platform/platform.h
@@ -0,0 +1,232 @@
+// Platform-specific functions
+
+#ifndef __PLATFORM_H__
+#define __PLATFORM_H__
+
+#include "cpu_esp8266.h"
+
+#include "c_types.h"
+#include "driver/pwm.h"
+// Error / status codes
+enum
+{
+ PLATFORM_ERR,
+ PLATFORM_OK,
+ PLATFORM_UNDERFLOW = -1
+};
+
+// Platform initialization
+int platform_init(void);
+void platform_int_init(void);
+
+// ****************************************************************************
+// KEY_LED functions
+uint8_t platform_key_led( uint8_t level);
+
+// *****************************************************************************
+// GPIO subsection
+#define PLATFORM_GPIO_FLOAT 0
+#define PLATFORM_GPIO_PULLUP 1
+#define PLATFORM_GPIO_PULLDOWN 2
+
+#define PLATFORM_GPIO_INT 2
+#define PLATFORM_GPIO_OUTPUT 1
+#define PLATFORM_GPIO_INPUT 0
+
+#define PLATFORM_GPIO_HIGH 1
+#define PLATFORM_GPIO_LOW 0
+
+/* GPIO interrupt handler */
+typedef void (* platform_gpio_intr_handler_fn_t)( unsigned pin, unsigned level );
+
+int platform_gpio_mode( unsigned pin, unsigned mode, unsigned pull );
+int platform_gpio_write( unsigned pin, unsigned level );
+int platform_gpio_read( unsigned pin );
+void platform_gpio_init( platform_gpio_intr_handler_fn_t cb );
+int platform_gpio_intr_init( unsigned pin, GPIO_INT_TYPE type );
+// *****************************************************************************
+// Timer subsection
+
+// Timer data type
+typedef uint32_t timer_data_type;
+
+// *****************************************************************************
+// CAN subsection
+
+// Maximum length for any CAN message
+#define PLATFORM_CAN_MAXLEN 8
+
+// eLua CAN ID types
+enum
+{
+ ELUA_CAN_ID_STD = 0,
+ ELUA_CAN_ID_EXT
+};
+
+int platform_can_exists( unsigned id );
+uint32_t platform_can_setup( unsigned id, uint32_t clock );
+int platform_can_send( unsigned id, uint32_t canid, uint8_t idtype, uint8_t len, const uint8_t *data );
+int platform_can_recv( unsigned id, uint32_t *canid, uint8_t *idtype, uint8_t *len, uint8_t *data );
+
+// *****************************************************************************
+// SPI subsection
+
+// There are 4 "virtual" SPI ports (SPI0...SPI3).
+#define PLATFORM_SPI_TOTAL 4
+// TODO: PLATFORM_SPI_TOTAL is not used - figure out purpose, or remove?
+
+// SPI mode
+#define PLATFORM_SPI_MASTER 1
+#define PLATFORM_SPI_SLAVE 0
+// SS values
+#define PLATFORM_SPI_SELECT_ON 1
+#define PLATFORM_SPI_SELECT_OFF 0
+// SPI enable/disable
+#define PLATFORM_SPI_ENABLE 1
+#define PLATFORM_SPI_DISABLE 0
+
+// Data types
+typedef uint32_t spi_data_type;
+
+// The platform SPI functions
+int platform_spi_exists( unsigned id );
+uint32_t platform_spi_setup( unsigned id, int mode, uint32_t clock, unsigned cpol, unsigned cpha, unsigned databits );
+spi_data_type platform_spi_send_recv( unsigned id, spi_data_type data );
+void platform_spi_select( unsigned id, int is_select );
+
+// *****************************************************************************
+// UART subsection
+
+// There are 3 "virtual" UART ports (UART0...UART2).
+#define PLATFORM_UART_TOTAL 3
+// TODO: PLATFORM_UART_TOTAL is not used - figure out purpose, or remove?
+// Note: Some CPUs (e.g. LM4F/TM4C) have more than 3 hardware UARTs
+
+// Parity
+enum
+{
+ PLATFORM_UART_PARITY_EVEN,
+ PLATFORM_UART_PARITY_ODD,
+ PLATFORM_UART_PARITY_NONE,
+ PLATFORM_UART_PARITY_MARK,
+ PLATFORM_UART_PARITY_SPACE
+};
+
+// Stop bits
+enum
+{
+ PLATFORM_UART_STOPBITS_1,
+ PLATFORM_UART_STOPBITS_1_5,
+ PLATFORM_UART_STOPBITS_2
+};
+
+// Flow control types (this is a bit mask, one can specify PLATFORM_UART_FLOW_RTS | PLATFORM_UART_FLOW_CTS )
+#define PLATFORM_UART_FLOW_NONE 0
+#define PLATFORM_UART_FLOW_RTS 1
+#define PLATFORM_UART_FLOW_CTS 2
+
+// The platform UART functions
+int platform_uart_exists( unsigned id );
+uint32_t platform_uart_setup( unsigned id, uint32_t baud, int databits, int parity, int stopbits );
+int platform_uart_set_buffer( unsigned id, unsigned size );
+void platform_uart_send( unsigned id, uint8_t data );
+void platform_s_uart_send( unsigned id, uint8_t data );
+int platform_uart_recv( unsigned id, unsigned timer_id, timer_data_type timeout );
+int platform_s_uart_recv( unsigned id, timer_data_type timeout );
+int platform_uart_set_flow_control( unsigned id, int type );
+int platform_s_uart_set_flow_control( unsigned id, int type );
+
+// *****************************************************************************
+// PWM subsection
+
+// There are 16 "virtual" PWM channels (PWM0...PWM15)
+#define PLATFORM_PWM_TOTAL 16
+// TODO: PLATFORM_PWM_TOTAL is not used - figure out purpose, or remove?
+
+#define NORMAL_PWM_DEPTH PWM_DEPTH
+#define NORMAL_DUTY(d) (((unsigned)(d)*NORMAL_PWM_DEPTH) / PWM_DEPTH)
+#define DUTY(d) ((uint16_t)( ((unsigned)(d)*PWM_DEPTH) / NORMAL_PWM_DEPTH) )
+
+// The platform PWM functions
+int platform_pwm_exists( unsigned id );
+uint32_t platform_pwm_setup( unsigned id, uint32_t frequency, unsigned duty );
+void platform_pwm_close( unsigned id );
+void platform_pwm_start( unsigned id );
+void platform_pwm_stop( unsigned id );
+uint32_t platform_pwm_set_clock( unsigned id, uint32_t data );
+uint32_t platform_pwm_get_clock( unsigned id );
+uint32_t platform_pwm_set_duty( unsigned id, uint32_t data );
+uint32_t platform_pwm_get_duty( unsigned id );
+
+
+// *****************************************************************************
+// The platform ADC functions
+
+// Functions requiring platform-specific implementation
+int platform_adc_update_sequence(void);
+int platform_adc_start_sequence(void);
+void platform_adc_stop( unsigned id );
+uint32_t platform_adc_set_clock( unsigned id, uint32_t frequency);
+int platform_adc_check_timer_id( unsigned id, unsigned timer_id );
+
+// ADC Common Functions
+int platform_adc_exists( unsigned id );
+uint32_t platform_adc_get_maxval( unsigned id );
+uint32_t platform_adc_set_smoothing( unsigned id, uint32_t length );
+void platform_adc_set_blocking( unsigned id, uint32_t mode );
+void platform_adc_set_freerunning( unsigned id, uint32_t mode );
+uint32_t platform_adc_is_done( unsigned id );
+void platform_adc_set_timer( unsigned id, uint32_t timer );
+
+// *****************************************************************************
+// I2C platform interface
+
+// I2C speed
+enum
+{
+ PLATFORM_I2C_SPEED_SLOW = 100000,
+ PLATFORM_I2C_SPEED_FAST = 400000
+};
+
+// I2C direction
+enum
+{
+ PLATFORM_I2C_DIRECTION_TRANSMITTER,
+ PLATFORM_I2C_DIRECTION_RECEIVER
+};
+
+int platform_i2c_exists( unsigned id );
+uint32_t platform_i2c_setup( unsigned id, uint8_t sda, uint8_t scl, uint32_t speed );
+void platform_i2c_send_start( unsigned id );
+void platform_i2c_send_stop( unsigned id );
+int platform_i2c_send_address( unsigned id, uint16_t address, int direction );
+int platform_i2c_send_byte( unsigned id, uint8_t data );
+int platform_i2c_recv_byte( unsigned id, int ack );
+
+// *****************************************************************************
+// Ethernet specific functions
+
+void platform_eth_send_packet( const void* src, uint32_t size );
+uint32_t platform_eth_get_packet_nb( void* buf, uint32_t maxlen );
+void platform_eth_force_interrupt(void);
+uint32_t platform_eth_get_elapsed_time(void);
+
+// *****************************************************************************
+// Internal flash erase/write functions
+
+uint32_t platform_flash_get_first_free_block_address( uint32_t *psect );
+uint32_t platform_flash_get_sector_of_address( uint32_t addr );
+uint32_t platform_flash_write( const void *from, uint32_t toaddr, uint32_t size );
+uint32_t platform_flash_read( void *to, uint32_t fromaddr, uint32_t size );
+uint32_t platform_s_flash_write( const void *from, uint32_t toaddr, uint32_t size );
+uint32_t platform_s_flash_read( void *to, uint32_t fromaddr, uint32_t size );
+uint32_t platform_flash_get_num_sectors(void);
+int platform_flash_erase_sector( uint32_t sector_id );
+
+// *****************************************************************************
+// Allocator support
+
+void* platform_get_first_free_ram( unsigned id );
+void* platform_get_last_free_ram( unsigned id );
+
+#endif
diff --git a/app/smart/Makefile b/app/smart/Makefile
new file mode 100644
index 00000000..79d73da1
--- /dev/null
+++ b/app/smart/Makefile
@@ -0,0 +1,44 @@
+
+#############################################################
+# Required variables for each makefile
+# Discard this section from all parent makefiles
+# Expected variables (with automatic defaults):
+# CSRCS (all "C" files in the dir)
+# SUBDIRS (all subdirs with a Makefile)
+# GEN_LIBS - list of libs to be generated ()
+# GEN_IMAGES - list of images to be generated ()
+# COMPONENTS_xxx - a list of libs/objs in the form
+# subdir/lib to be extracted and rolled up into
+# a generated lib/image xxx.a ()
+#
+ifndef PDIR
+GEN_LIBS = smart.a
+endif
+
+#############################################################
+# Configuration i.e. compile options etc.
+# Target specific stuff (defines etc.) goes in here!
+# Generally values applying to a tree are captured in the
+# makefile at its root level - these are then overridden
+# for a subtree within the makefile rooted therein
+#
+#DEFINES +=
+
+#############################################################
+# Recursion Magic - Don't touch this!!
+#
+# Each subtree potentially has an include directory
+# corresponding to the common APIs applicable to modules
+# rooted at that subtree. Accordingly, the INCLUDE PATH
+# of a module can only contain the include directories up
+# its parent path, and not its siblings
+#
+# Required for each makefile to inherit from the parent
+#
+
+INCLUDES := $(INCLUDES) -I $(PDIR)include
+INCLUDES += -I ./
+INCLUDES += -I ../libc
+PDIR := ../$(PDIR)
+sinclude $(PDIR)Makefile
+
diff --git a/app/smart/smart.c b/app/smart/smart.c
new file mode 100644
index 00000000..71808de5
--- /dev/null
+++ b/app/smart/smart.c
@@ -0,0 +1,713 @@
+#include "c_stdio.h"
+#include "c_stdlib.h"
+#include "c_string.h"
+#include "user_interface.h"
+#include "smart.h"
+
+#define ADDR_MAP_NUM 10
+
+static os_timer_t smart_timer;
+
+static smart_addr_map *am[ADDR_MAP_NUM];
+
+static smart_addr_map *matched = NULL;
+
+static struct station_config *sta_conf;
+
+static int cur_channel = 1;
+
+static uint8_t mode = STATION_MODE;
+
+static uint8_t alldone = 0;
+
+// 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000(LSB)
+// when the bit is set, means the ssid byte is got
+static uint8_t *got_ssid = NULL;
+static uint8_t *got_password = NULL;
+
+static uint8_t *ssid_nibble = NULL;
+static uint8_t *password_nibble = NULL;
+
+static smart_succeed succeed = NULL;
+static void *smart_succeed_arg = NULL;
+
+void smart_end();
+int ICACHE_FLASH_ATTR smart_check(uint8_t *nibble, uint16_t len, uint8_t *dst, uint8_t *got){
+ if(len == 0)
+ return 0;
+ uint16_t dst_len = len/NIBBLE_PER_BYTE;
+ uint16_t byte_num = 0, bit_num = 0;
+ int i = 0, res = 1; // assume ok.
+ c_memset(dst,0,dst_len);
+
+ if(NIBBLE_PER_BYTE==1){
+ for(i=0;inibble[i] || nibble[i]>=0x7F){ // not printable
+ NODE_DBG("Smart: got np byte %d:%02x\n", i, nibble[i]);
+ nibble[i] = 0;
+ got[byte_num] &= ~(0x1 << bit_num); // clear the bit
+ res = 0; // not ok
+ } else {
+ dst[i] = nibble[i];
+ }
+ }
+ return res;
+ }
+
+ // NIBBLE_PER_BYTE == 2
+ if((len%NIBBLE_PER_BYTE) != 0){
+ // this should not happen
+ NODE_DBG("Smart: smart_check got odd len\n");
+ return 0;
+ }
+
+ if(len == 2){
+ // only one byte
+ if(nibble[0]<=0xF && ((nibble[0]^0x1)&0xF == (nibble[1]>>4)) ){
+ dst[0] = ((nibble[0]&0xF)<<4) + (nibble[1]&0xF);
+ res = 1;
+ }else{
+ nibble[0] = 0;
+ nibble[1] = 0;
+ got[0] &= ~(0x3 << 0); // clear the 0 bit
+ res = 0; // not ok
+ }
+ return res;
+ }
+
+ res = 1; // assume ok.
+ for(i=len-2;i>0;i--){
+ bool forward = ( ((nibble[i]&0xF)^((i+1)&0xF)) == (nibble[i+1]>>4) );
+ bool back = ( ((nibble[i-1]&0xF)^(i&0xF)) == (nibble[i]>>4) );
+ if(!forward || !back){
+ // wrong forward, or wrong back, replace i-1, i and i+1, until get right back, forward
+ NODE_DBG("check: wf %d:%02x %02x %02x\n",i,nibble[i-1],nibble[i], nibble[i+1]);
+ byte_num = (i-1) / 8;
+ bit_num = (i-1) % 8;
+ nibble[i-1] = 0;
+ got[byte_num] &= ~(0x1 << bit_num); // clear the bit
+
+ byte_num = (i) / 8;
+ bit_num = (i) % 8;
+ nibble[i] = 0;
+ got[byte_num] &= ~(0x1 << bit_num); // clear the bit
+
+ byte_num = (i+1) / 8;
+ bit_num = (i+1) % 8;
+ nibble[i+1] = 0;
+ got[byte_num] &= ~(0x1 << bit_num); // clear the bit
+ res = 0;
+ return res; // once there is error,
+ }
+
+ if((i%NIBBLE_PER_BYTE) == 0) { // i == even
+ dst[i/NIBBLE_PER_BYTE] = ((nibble[i]&0xF)<<4) + (nibble[i+1]&0xF);
+ }
+ }
+
+ if(i==0){
+ dst[0] = ((nibble[0]&0xF)<<4) + (nibble[1]&0xF);
+ }
+
+ for(i=0;i%02x\n", i, nibble[i*NIBBLE_PER_BYTE], nibble[i*NIBBLE_PER_BYTE+1], dst[i]);
+ byte_num = (i*NIBBLE_PER_BYTE) / 8;
+ bit_num = (i*NIBBLE_PER_BYTE) % 8;
+ if(0x20>dst[i] || dst[i]>=0x7F){ // not printable
+ NODE_DBG("Smart: got np byte %d:%02x\n", i, dst[i]);
+ dst[i] = 0; // reset byte
+ nibble[i*NIBBLE_PER_BYTE] = 0; // reset hi-nibble
+ nibble[i*NIBBLE_PER_BYTE+1] = 0; // reset lo-nibble
+ got[byte_num] &= ~(0x3 << bit_num); // clear the bit
+ res = 0; // not ok
+ }
+ }
+ return res;
+}
+
+void ICACHE_FLASH_ATTR detect(uint8 *buf, uint16 len){
+ uint16_t seq;
+ int16_t seq_delta = 0;
+ uint16_t byte_num = 0, bit_num = 0;
+ int16_t c = 0;
+ if( ( (buf[0]) & TYPE_SUBTYPE_MASK) != TYPE_SUBTYPE_QOS_DATA){
+ return;
+ }
+ if( (buf[1] & DS_RETRY_MASK) != NO_RETRY )
+ return;
+ if( buf[SEQ_ADDR] & 0xF != 0 ) // Fragment Number should = 0
+ return;
+ // calculate current seq number
+ seq = buf[SEQ_ADDR+1];
+ seq = seq<<4;
+ seq += buf[SEQ_ADDR]>>4;
+
+ if(!matched){ // cur_base_seq is ref to flag[0] when finding the patern
+ int i;
+ for (i = 0; i < ADDR_MAP_NUM; i++) // for each source-dest adress pair in the map
+ {
+ if ( am[i]->flag_match_num == 0 ){ // not in the map yet
+ if ( len - am[i]->base_len == am[i]->flag[0]) // store new source-dest adress pair to the map until flag[0] is got
+ {
+ // BSSID, SA, DA, store the SA, DA
+ c_memcpy(am[i]->addr, &buf[ADDR_MATCH_START], ADDR_MATCH_LENGTH);
+ am[i]->flag_match_num++; // =1
+ am[i]->cur_base_seq = seq; // assume the first seq is found
+ am[i]->base_seq_valid = 1;
+ // NODE_DBG("Smart: new addr pair found\n");
+ }
+ break; // break any way for the next packet to come
+ }
+ else if(0 == c_memcmp(am[i]->addr, &buf[ADDR_MATCH_START], ADDR_MATCH_LENGTH)){ // source-dest adress pair match
+ if(am[i]->base_seq_valid == 0){
+ if ( len - am[i]->base_len == am[i]->flag[0]) { // found the new flag[0]
+ // here flag_match_num is already = 1
+ am[i]->cur_base_seq = seq;
+ am[i]->base_seq_valid = 1; // the seq number is valid now
+ // NODE_DBG("Smart: new base_seq found\n");
+ }
+ break; // break any way for the next packet to come
+ }
+
+ // base seq number is valid, cal the delta
+ if(seq >= am[i]->cur_base_seq){
+ seq_delta = seq - am[i]->cur_base_seq;
+ } else {
+ seq_delta = SEQ_MAX - am[i]->cur_base_seq + seq;
+ }
+
+ if(seq_delta < 0){ // this should never happen
+ am[i]->base_seq_valid = 0; // the seq number is not valid
+ break;
+ }
+
+ if(seq_delta == 0){ // base_seq is not valid any more
+ if ( len - am[i]->base_len != am[i]->flag[0]) { // lost the flag[0]
+ am[i]->base_seq_valid = 0; // the seq number is not valid
+ }
+ break; // break any way for the next packet to come
+ }
+
+ // delta is out of range, need to find the next flag[0] to start again
+ if (seq_delta>=FLAG_NUM){
+ am[i]->flag_match_num = 1; // reset to 1
+ if ( len - am[i]->base_len == am[i]->flag[0]) { // found the new flag[0]
+ // here flag_match_num is already = 1
+ am[i]->cur_base_seq = seq;
+ am[i]->base_seq_valid = 1; // the seq number is valid now
+ } else {
+ am[i]->base_seq_valid = 0;
+ }
+ break; // done for this packet
+ }
+
+ // NODE_DBG("Smart: match_num:%d seq_delta:%d len:%d\n",am[i]->flag_match_num,seq_delta,len-am[i]->base_len);
+ // seq_delta now from 1 to FLAG_NUM-1
+ // flag[] == 0 ,means skip this flag.
+ if ( (am[i]->flag_match_num==seq_delta) && \
+ ( (am[i]->flag[am[i]->flag_match_num]==len-am[i]->base_len) || (am[i]->flag[am[i]->flag_match_num]==0) ) ){
+ am[i]->flag_match_num++;
+ if(am[i]->flag_match_num == FLAG_MATCH_NUM){ //every thing is match.
+ NODE_ERR("Smart: got matched sender\n");
+ matched = am[i]; // got the matched source-dest adress pair who is sending the udp data
+ matched->base_seq_valid = 0; // set to 0, and start to reference to the SSID_FLAG from now on
+ os_timer_disarm(&smart_timer); // note: may start a longer timeout
+ }
+ break;
+ }
+
+ // non match, reset, need to find next flag[0] to start again
+ am[i]->flag_match_num = 1;
+ am[i]->base_seq_valid = 0;
+ break;
+ } // non-match source-dest adress pair, continue to next pair in the map.
+ } // for loop
+ // break out, or loop done.
+ goto end;
+ } else { // cur_base_seq is ref to SSID_FLAG when patern is alread found
+ if(0 != c_memcmp(matched->addr, &buf[ADDR_MATCH_START], ADDR_MATCH_LENGTH)){ // source-dest adress pair not match, ignore it
+ return;
+ }
+ if (matched->base_seq_valid == 0){ // SSID_FLAG seq invalid, need to find the next valid seq number
+ // base_seq not valid, find it
+ if (len - matched->base_len == SSID_FLAG){
+ matched->cur_base_seq = seq;
+ matched->base_seq_valid = 1;
+ }
+ goto end;
+ }
+
+ if(seq >= matched->cur_base_seq){
+ seq_delta = seq - matched->cur_base_seq;
+ } else {
+ seq_delta = SEQ_MAX - matched->cur_base_seq + seq;
+ }
+
+ if(seq_delta < 0){ // this should never happen
+ matched->base_seq_valid = 0; // the seq number is not valid
+ goto end;
+ }
+
+ if(seq_delta == 0){ // base_seq is not valid any more
+ if ( len - matched->base_len != SSID_FLAG) { // lost the SSID_FLAG
+ matched->base_seq_valid = 0; // the seq number is not valid
+ }
+ goto end; // exit for the next packet to come
+ }
+
+ if ( seq_delta > (SEP_NUM + 1)*(1+NIBBLE_PER_BYTE*matched->ssid_len) +\
+ 1 + (SEP_NUM + 1)*(1+NIBBLE_PER_BYTE*matched->pwd_len) ){
+ // delta out of the range
+ if (len - matched->base_len == SSID_FLAG){
+ matched->cur_base_seq = seq;
+ matched->base_seq_valid = 1;
+ } else {
+ matched->base_seq_valid = 0;
+ }
+ goto end;
+ }
+
+ // delta in the range
+ if (seq_delta==1){
+ int16_t ssid_len = len - matched->base_len - L_FLAG;
+ if ( matched->ssid_len == 0 ){ // update the ssid_len
+ if ( (ssid_len <=32) && (ssid_len >0) ){
+ matched->ssid_len = ssid_len;
+ NODE_DBG("Smart: found the ssid_len %d\n", matched->ssid_len);
+ }
+ goto end;
+ }
+ if (ssid_len != matched->ssid_len){ // ssid_len not match
+ matched->base_seq_valid = 0;
+ // note: not match, save the new one or old one? for now save the new one.
+ matched->ssid_len = ssid_len;
+ NODE_DBG("Smart: ssid_len not match\n");
+ }
+ goto end; // to the next packet
+ }
+
+ if( (SEP_NUM==2)&&(seq_delta==2 || seq_delta==3) ) {
+ if (len - matched->base_len != matched->flag[seq_delta-2+SEP_1_INDEX]){ // SEP not match
+ matched->base_seq_valid = 0;
+ NODE_DBG("Smart: SEP-L not match\n");
+ }
+ goto end; // to the next packet
+ }
+
+ if( seq_delta==(SEP_NUM + 1)*(1+NIBBLE_PER_BYTE*matched->ssid_len) + 1) {
+ if (len - matched->base_len != PWD_FLAG){ // PWD_FLAG not match
+ matched->base_seq_valid = 0;
+ NODE_DBG("Smart: PWD_FLAG not match\n");
+ }
+ goto end; // to the next packet
+ }
+
+ if (seq_delta==(SEP_NUM + 1)*(1+NIBBLE_PER_BYTE*matched->ssid_len) + 1 + 1){
+ int16_t pwd_len = len - matched->base_len - L_FLAG;
+ if ( matched->pwd_len == 0){
+ if ( (pwd_len <=64) && (pwd_len>0)){
+ matched->pwd_len = pwd_len;
+ NODE_DBG("Smart: found the pwd_len %d\n", matched->pwd_len);
+ }
+ goto end; // to the next packet
+ }
+ if (pwd_len != matched->pwd_len){ // pwd_len not match
+ matched->base_seq_valid = 0;
+ // note: not match, save the new one or old one? for now save the new one.
+ matched->pwd_len = pwd_len; // reset pwd_len to 0
+ NODE_DBG("Smart: pwd_len not match\n");
+ }
+ goto end;
+ }
+
+ if (seq_delta <= (SEP_NUM + 1)*(1+NIBBLE_PER_BYTE*matched->ssid_len) ){ // in the ssid zone
+ uint16_t it = (seq_delta-1-SEP_NUM-1) / (SEP_NUM + 1); // the number of ssid nibble: 0~31 or 0~63
+ uint16_t m = (seq_delta-1-SEP_NUM-1) % (SEP_NUM + 1); // 0~2
+ switch(m){
+ case 0: // the ssid hi/lo-nibble itself
+ c = (int16_t)(len - matched->base_len - C_FLAG);
+ if (c>255 || c<0){
+ matched->base_seq_valid = 0;
+ NODE_DBG("Smart: wrong ssid nibble\n");
+ goto end;
+ }
+ byte_num = it / 8; // 0~7
+ bit_num = it % 8; // 0~7
+ if( (got_ssid[byte_num] & (0x1 << bit_num)) == 0){
+ got_ssid[byte_num] |= 0x1 << bit_num; // set the bit
+ ssid_nibble[it] = c;
+ }
+ break;
+ case 1: // seperator 1
+ case 2: // seperator 2
+ if(len - matched->base_len != matched->flag[m-1+SEP_1_INDEX]){
+ NODE_DBG("Smart: SEP-S not match\n");
+ matched->base_seq_valid = 0;
+ goto end;
+ }
+ break;
+ default:
+ break;
+ }
+ } else { // in the pwd zone
+ uint16_t it = (seq_delta -1 -(SEP_NUM + 1)*(1+NIBBLE_PER_BYTE*matched->ssid_len) - 2 - SEP_NUM) / (SEP_NUM + 1); // the number of pwd byte
+ uint16_t m = (seq_delta -1 -(SEP_NUM + 1)*(1+NIBBLE_PER_BYTE*matched->ssid_len) - 2 - SEP_NUM) % (SEP_NUM + 1);
+ switch(m){
+ case 0: // the pwd hi/lo-nibble itself
+ c = (int16_t)(len - matched->base_len - C_FLAG);
+ if (c>255 || c<0){
+ matched->base_seq_valid = 0;
+ NODE_DBG("Smart: wrong password nibble\n");
+ goto end;
+ }
+ byte_num = it / 8; // 0~15 / 7
+ bit_num = it % 8; // 0~7
+ if( (got_password[byte_num] & (0x1 << bit_num)) == 0){
+ got_password[byte_num] |= 0x1 << bit_num; // set the bit
+ password_nibble[it] = c;
+ }
+ break;
+ case 1: // seperator 1
+ case 2: // seperator 2
+ if(len - matched->base_len != matched->flag[m-1+SEP_1_INDEX]){
+ NODE_DBG("Smart: SEP-P not match\n");
+ matched->base_seq_valid = 0;
+ goto end;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ // check if all done
+ // NODE_DBG("Smart: ssid got %02x %02x\n", got_ssid[0], got_ssid[1]);
+ // NODE_DBG("Smart: password got %02x %02x %02x\n", got_password[0], got_password[1], got_password[2]);
+ int i,j;
+ for(i=0;issid_len;i++){
+ byte_num = (i) / 8;
+ bit_num = (i) % 8;
+ if( (got_ssid[byte_num] & (0x1 << bit_num) ) != (0x1 << bit_num) ){ // check the bit == 1
+ break;
+ }
+ }
+ for(j=0;jpwd_len;j++){
+ byte_num = (j) / 8;
+ bit_num = (j) % 8;
+ if( (got_password[byte_num] & (0x1 << bit_num) ) != (0x1 << bit_num) ){ // check the 2 bit == 11
+ break;
+ }
+ }
+ if(matched->ssid_len > 0 && matched->pwd_len > 0 && i==NIBBLE_PER_BYTE*matched->ssid_len && j==NIBBLE_PER_BYTE*matched->pwd_len){ // get everything, check it.
+ if( smart_check(ssid_nibble, NIBBLE_PER_BYTE*matched->ssid_len, sta_conf->ssid, got_ssid) && \
+ smart_check(password_nibble, NIBBLE_PER_BYTE*matched->pwd_len, sta_conf->password, got_password) ){
+ // all done
+ alldone = 1;
+ NODE_ERR(sta_conf->ssid);
+ NODE_ERR(" %d\n", matched->ssid_len);
+ NODE_ERR(sta_conf->password);
+ NODE_ERR(" %d\n", matched->pwd_len);
+ smart_end();
+ // if(succeed){
+ // succeed(smart_succeed_arg);
+ // succeed = NULL; // reset to NULL when succeed
+ // smart_succeed_arg = NULL;
+ // }
+ return;
+ }
+ }
+ }
+
+end:
+#if 0
+ NODE_DBG("%d:\t0x%x 0x%x\t", len-BASE_LENGTH, buf[0],buf[1]);
+ NODE_DBG(MACSTR, MAC2STR(&(buf[BSSID_ADDR])));
+ NODE_DBG("\t");
+ NODE_DBG(MACSTR, MAC2STR(&(buf[SOURCE_ADDR])));
+ NODE_DBG("\t");
+ NODE_DBG(MACSTR, MAC2STR(&(buf[DEST_ADDR])));
+ uint16_t tseq = buf[SEQ_ADDR+1];
+ tseq = tseq<<4;
+ tseq += buf[SEQ_ADDR]>>4;
+ NODE_DBG("\t0x%04x\n", tseq);
+#endif
+ return;
+}
+
+void ICACHE_FLASH_ATTR reset_map(smart_addr_map **am, size_t num){
+ int i;
+ for (i = 0; i < num; ++i)
+ {
+ am[i]->flag_match_num = 0;
+ am[i]->addr_len = ADDR_MATCH_LENGTH;
+ am[i]->base_len = BASE_LENGTH;
+ am[i]->cur_base_seq = -1;
+ am[i]->base_seq_valid = 0;
+ am[i]->ssid_len = 0;
+ am[i]->pwd_len = 0;
+ c_memset(am[i]->addr, 0, ADDR_MATCH_LENGTH);
+ if(SEP_1_INDEX==0){
+ am[i]->flag[0] = SEP_1;
+ am[i]->flag[1] = SEP_2;
+ am[i]->flag[2] = SSID_FLAG;
+ }
+ if(SEP_1_INDEX==2){
+ am[i]->flag[0] = SSID_FLAG;
+ am[i]->flag[1] = 0; // skip this flag
+ am[i]->flag[2] = SEP_1;
+ am[i]->flag[3] = SEP_2;
+ }
+ }
+}
+
+void ICACHE_FLASH_ATTR smart_enable(void){
+ wifi_promiscuous_enable(1);
+}
+
+void ICACHE_FLASH_ATTR smart_disable(void){
+ wifi_promiscuous_enable(0);
+}
+
+void ICACHE_FLASH_ATTR smart_end(){
+ int i;
+ os_timer_disarm(&smart_timer);
+ smart_disable();
+ wifi_set_channel(cur_channel);
+
+ if(NULL_MODE != mode){
+ wifi_set_opmode(mode);
+ } else {
+ wifi_set_opmode(STATION_MODE);
+ }
+
+ mode = wifi_get_opmode();
+
+ if(sta_conf && alldone){
+ if( (STATION_MODE == mode) || (mode == STATIONAP_MODE) ){
+ wifi_station_set_config(sta_conf);
+ wifi_station_set_auto_connect(true);
+ wifi_station_disconnect();
+ wifi_station_connect();
+
+ os_timer_disarm(&smart_timer);
+ os_timer_setfn(&smart_timer, (os_timer_func_t *)station_check_connect, 1);
+ os_timer_arm(&smart_timer, STATION_CHECK_TIME, 0); // no repeat
+ }
+ }
+
+ for (i = 0; i < ADDR_MAP_NUM; ++i)
+ {
+ if(am[i]){
+ c_free(am[i]);
+ am[i] = NULL;
+ }
+ matched = NULL;
+ }
+
+ if(sta_conf){
+ c_free(sta_conf);
+ sta_conf = NULL;
+ }
+
+ if(got_password){
+ c_free(got_password);
+ got_password = NULL;
+ }
+
+ if(got_ssid){
+ c_free(got_ssid);
+ got_ssid = NULL;
+ }
+
+ if(password_nibble){
+ c_free(password_nibble);
+ password_nibble = NULL;
+ }
+
+ if(ssid_nibble){
+ c_free(ssid_nibble);
+ ssid_nibble = NULL;
+ }
+ // system_restart(); // restart to enable the mode
+}
+
+void ICACHE_FLASH_ATTR smart_next_channel(){
+ smart_disable();
+ switch(cur_channel){
+ case 1:
+ cur_channel = MAX_CHANNEL;
+ break;
+ case 2:
+ case 3:
+ case 4:
+ cur_channel++;
+ break;
+ case 5:
+ cur_channel = 7;
+ break;
+ case 6:
+ cur_channel = 1;
+ break;
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ cur_channel++;
+ break;
+ case 13:
+ cur_channel = 6;
+ break;
+ case MAX_CHANNEL:
+ cur_channel = 2;
+ break;
+ default:
+ cur_channel = 6;
+ break;
+ }
+
+ NODE_ERR("switch to channel %d\n", cur_channel);
+ wifi_set_channel(cur_channel);
+ reset_map(am, ADDR_MAP_NUM);
+ c_memset(sta_conf->ssid, 0, sizeof(sta_conf->ssid));
+ c_memset(sta_conf->password, 0, sizeof(sta_conf->password));
+
+ c_memset(got_ssid, 0, SSID_BIT_MAX);
+ c_memset(got_password, 0, PWD_BIT_MAX);
+
+ c_memset(ssid_nibble, 0, SSID_NIBBLE_MAX);
+ c_memset(password_nibble, 0, PWD_NIBBLE_MAX);
+
+ os_timer_disarm(&smart_timer);
+ os_timer_arm(&smart_timer, TIME_OUT_PER_CHANNEL, 0); // no repeat
+
+ smart_enable();
+}
+
+void ICACHE_FLASH_ATTR smart_begin(int chnl, smart_succeed s, void *arg){
+ int i;
+ alldone = 0;
+ for (i = 0; i < ADDR_MAP_NUM; ++i)
+ {
+ if(!am[i]){
+ am[i] = (smart_addr_map*)c_zalloc(sizeof(smart_addr_map));
+ if(!am[i]){
+ NODE_DBG("smart_begin map no memory\n");
+ smart_end();
+ return;
+ }
+ }
+ }
+ if(!sta_conf){
+ sta_conf = (struct station_config *)c_zalloc(sizeof(struct station_config));
+ if(!sta_conf){
+ NODE_DBG("smart_begin sta_conf no memory\n");
+ smart_end();
+ return;
+ }
+ }
+
+ if(!ssid_nibble){
+ ssid_nibble = (uint8_t *)c_zalloc(SSID_NIBBLE_MAX);
+ if(!ssid_nibble){
+ NODE_DBG("smart_begin sta_conf no memory\n");
+ smart_end();
+ return;
+ }
+ }
+
+ if(!password_nibble){
+ password_nibble = (uint8_t *)c_zalloc(PWD_NIBBLE_MAX);
+ if(!password_nibble){
+ NODE_DBG("smart_begin sta_conf no memory\n");
+ smart_end();
+ return;
+ }
+ }
+
+ if(!got_ssid){
+ got_ssid = (uint8_t *)c_zalloc(SSID_BIT_MAX);
+ if(!got_ssid){
+ NODE_DBG("smart_begin sta_conf no memory\n");
+ smart_end();
+ return;
+ }
+ }
+
+ if(!got_password){
+ got_password = (uint8_t *)c_zalloc(PWD_BIT_MAX);
+ if(!got_password){
+ NODE_DBG("smart_begin sta_conf no memory\n");
+ smart_end();
+ return;
+ }
+ }
+ reset_map(am, ADDR_MAP_NUM);
+ // c_memset(sta_conf->ssid, 0, sizeof(sta_conf->ssid));
+ // c_memset(sta_conf->password, 0, sizeof(sta_conf->password));
+
+ // c_memset(got_ssid, 0, SSID_BIT_MAX);
+ // c_memset(got_password, 0, PWD_BIT_MAX);
+
+ // c_memset(ssid_nibble, 0, SSID_NIBBLE_MAX);
+ // c_memset(password_nibble, 0, PWD_NIBBLE_MAX);
+ mode = wifi_get_opmode();
+ if( (STATION_MODE == mode) || (mode == STATIONAP_MODE) ){
+ wifi_station_set_auto_connect(false);
+ wifi_station_disconnect();
+ }
+ cur_channel = chnl;
+ NODE_ERR("set channel to %d\n", cur_channel);
+ wifi_set_channel(cur_channel);
+ wifi_set_promiscuous_rx_cb(detect);
+ os_timer_disarm(&smart_timer);
+ os_timer_setfn(&smart_timer, (os_timer_func_t *)smart_next_channel, NULL);
+ os_timer_arm(&smart_timer, TIME_OUT_PER_CHANNEL, 0); // no repeat
+
+ if(s){
+ succeed = s; // init the succeed call back
+ smart_succeed_arg = arg;
+ }
+
+ smart_enable();
+}
+
+void ICACHE_FLASH_ATTR station_check_connect(bool smart){
+ mode = wifi_get_opmode();
+ if( (STATION_MODE != mode) && (mode != STATIONAP_MODE) ){
+ return;
+ }
+ uint8_t status = wifi_station_get_connect_status();
+ switch(status){
+ case STATION_GOT_IP:
+ NODE_DBG("station_check_connect is called with status: GOT_IP\n");
+ if(succeed){
+ succeed(smart_succeed_arg);
+ succeed = NULL; // reset to NULL when succeed
+ smart_succeed_arg = NULL;
+ }
+ return;
+ case STATION_CONNECTING:
+ NODE_DBG("station_check_connect is called with status: CONNECTING\n");
+ break;
+ case STATION_IDLE:
+ wifi_station_set_auto_connect(true);
+ case STATION_CONNECT_FAIL:
+ case STATION_NO_AP_FOUND:
+ wifi_station_disconnect();
+ wifi_station_connect();
+ NODE_DBG("station_check_connect is called with smart: %d\n", smart);
+ break;
+ case STATION_WRONG_PASSWORD:
+ if(smart)
+ smart_begin(cur_channel, succeed, smart_succeed_arg);
+ return;
+ default:
+ break;
+ }
+ os_timer_disarm(&smart_timer);
+ os_timer_setfn(&smart_timer, (os_timer_func_t *)station_check_connect, smart);
+ os_timer_arm(&smart_timer, STATION_CHECK_TIME, 0); // no repeat
+}
diff --git a/app/smart/smart.h b/app/smart/smart.h
new file mode 100644
index 00000000..a702830a
--- /dev/null
+++ b/app/smart/smart.h
@@ -0,0 +1,87 @@
+#ifndef _SMART_H
+#define _SMART_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#define MAX_CHANNEL 14
+
+#define TYPE_SUBTYPE_QOS_DATA 0x88
+#define TYPE_SUBTYPE_MASK 0xFC
+
+#define NO_RETRY 0x41
+#define DS_RETRY_MASK 0x4B
+
+#define BSSID_ADDR 4
+#define SOURCE_ADDR 10
+#define DEST_ADDR 16
+#define ADDR_LENGTH 6
+#define ADDR_MATCH_START (SOURCE_ADDR)
+#define ADDR_MATCH_LENGTH (ADDR_LENGTH*2)
+
+#define SEQ_ADDR 22
+#define SEQ_LEN 2
+#define SEQ_MAX 0x1000
+
+#define BASE_LENGTH 82
+
+
+#define SSID_FLAG 1399
+#define PWD_FLAG 1459
+#define L_FLAG 28
+#define C_FLAG 593
+#define SEP_1 3
+#define SEP_2 23
+#define SEP_3 24
+#define SEP_4 25
+#define SEP_5 26
+#define SEP_NUM 2
+
+#define NIBBLE_PER_BYTE 2
+
+#define SEP_1_INDEX 2
+
+#if(SEP_1_INDEX==0)
+#define FLAG_NUM (SEP_NUM+1)
+#elif(SEP_1_INDEX==2)
+#define FLAG_NUM (SEP_NUM+2)
+#endif
+#define FLAG_MATCH_NUM (FLAG_NUM-1) // only need to match 2 or 3 byte to increase speed.
+
+// #define TI_SMARTCONFIG 1
+
+#define SSID_NIBBLE_MAX (32*NIBBLE_PER_BYTE)
+#define PWD_NIBBLE_MAX (64*NIBBLE_PER_BYTE)
+#define SSID_BIT_MAX (SSID_NIBBLE_MAX/8)
+#define PWD_BIT_MAX (PWD_NIBBLE_MAX/8)
+
+#define TIME_OUT_PER_CHANNEL (30*1000)
+
+#define STATION_CHECK_TIME (2*1000)
+
+struct _my_addr_map {
+ uint8 addr[ADDR_LENGTH*3];
+ uint8_t addr_len;
+ uint16_t base_len;
+ int16_t flag[FLAG_NUM];
+ // flag[0]: SEP_1, flag[1]: SEP_2, flag[1]: SSID_FLAG. SEP followed by SSID_FLAG formed flag[]
+ // if flag[i]==0, means skip this flag match, eg. SSID_FLAG, 0, SEP_1, SEP_2
+ uint8_t flag_match_num;
+ int16_t cur_base_seq;
+ int8_t base_seq_valid;
+ int8_t ssid_len;
+ int8_t pwd_len;
+};
+
+typedef struct _my_addr_map smart_addr_map;
+
+typedef void (* smart_succeed)(void *arg);
+
+void smart_begin(int chnl, smart_succeed s, void *arg);
+void station_check_connect(bool smart);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/app/spiffs/Makefile b/app/spiffs/Makefile
new file mode 100644
index 00000000..1c44e4ac
--- /dev/null
+++ b/app/spiffs/Makefile
@@ -0,0 +1,44 @@
+
+#############################################################
+# Required variables for each makefile
+# Discard this section from all parent makefiles
+# Expected variables (with automatic defaults):
+# CSRCS (all "C" files in the dir)
+# SUBDIRS (all subdirs with a Makefile)
+# GEN_LIBS - list of libs to be generated ()
+# GEN_IMAGES - list of images to be generated ()
+# COMPONENTS_xxx - a list of libs/objs in the form
+# subdir/lib to be extracted and rolled up into
+# a generated lib/image xxx.a ()
+#
+ifndef PDIR
+GEN_LIBS = spiffs.a
+endif
+
+#############################################################
+# Configuration i.e. compile options etc.
+# Target specific stuff (defines etc.) goes in here!
+# Generally values applying to a tree are captured in the
+# makefile at its root level - these are then overridden
+# for a subtree within the makefile rooted therein
+#
+#DEFINES +=
+
+#############################################################
+# Recursion Magic - Don't touch this!!
+#
+# Each subtree potentially has an include directory
+# corresponding to the common APIs applicable to modules
+# rooted at that subtree. Accordingly, the INCLUDE PATH
+# of a module can only contain the include directories up
+# its parent path, and not its siblings
+#
+# Required for each makefile to inherit from the parent
+#
+
+INCLUDES := $(INCLUDES) -I $(PDIR)include
+INCLUDES += -I ./
+INCLUDES += -I ../libc
+INCLUDES += -I ../platform
+PDIR := ../$(PDIR)
+sinclude $(PDIR)Makefile
diff --git a/app/spiffs/docs/IMPLEMENTING b/app/spiffs/docs/IMPLEMENTING
new file mode 100644
index 00000000..e69de29b
diff --git a/app/spiffs/docs/INTEGRATION b/app/spiffs/docs/INTEGRATION
new file mode 100644
index 00000000..703197e4
--- /dev/null
+++ b/app/spiffs/docs/INTEGRATION
@@ -0,0 +1,283 @@
+* QUICK AND DIRTY INTEGRATION EXAMPLE
+
+So, assume you're running a Cortex-M3 board with a 2 MB SPI flash on it. The
+SPI flash has 64kB blocks. Your project is built using gnumake, and now you
+want to try things out.
+
+First, you simply copy the files in src/ to your own source folder. Exclude
+all files in test folder. Then you point out these files in your make script
+for compilation.
+
+Also copy the spiffs_config.h over from the src/default/ folder.
+
+Try building. This fails, nagging about inclusions and u32_t and whatnot. Open
+the spiffs_config.h and delete the bad inclusions. Also, add following
+typedefs:
+
+ 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;
+
+Now it should build. Over to the mounting business. Assume you already
+implemented the read, write and erase functions to your SPI flash:
+
+ void my_spi_read(int addr, int size, char *buf)
+ void my_spi_write(int addr, int size, char *buf)
+ void my_spi_erase(int addr, int size)
+
+In your main.c or similar, include the spiffs.h and do that spiffs struct:
+
+ #include
+
+ static spiffs fs;
+
+Also, toss up some of the needed buffers:
+
+ #define LOG_PAGE_SIZE 256
+
+ static u8_t spiffs_work_buf[LOG_PAGE_SIZE*2];
+ static u8_t spiffs_fds[32*4];
+ static u8_t spiffs_cache[(LOG_PAGE_SIZE+32)*4];
+
+Now, write the my_spiffs_mount function:
+
+ void my_spiffs_mount() {
+ spiffs_config cfg;
+ cfg.phys_size = 2*1024*1024; // use all spi flash
+ cfg.phys_addr = 0; // start spiffs at start of spi flash
+ cfg.phys_erase_block = 65536; // according to datasheet
+ cfg.log_block_size = 65536; // let us not complicate things
+ cfg.log_block_size = LOG_PAGE_SIZE; // as we said
+
+ cfg.hal_read_f = my_spi_read;
+ cfg.hal_write_f = my_spi_write;
+ cfg.hal_erase_f = my_spi_erase;
+
+ int res = SPIFFS_mount(&fs,
+ &cfg,
+ spiffs_work_buf,
+ spiffs_fds,
+ sizeof(spiffs_fds),
+ spiffs_cache,
+ sizeof(spiffs_cache),
+ 0);
+ printf("mount res: %i\n", res);
+ }
+
+Now, build warns about the my_spi_read, write and erase functions. Wrong
+signatures, so go wrap them:
+
+ static s32_t my_spiffs_read(u32_t addr, u32_t size, u8_t *dst) {
+ my_spi_read(addr, size, dst);
+ return SPIFFS_OK;
+ }
+
+ static s32_t my_spiffs_write(u32_t addr, u32_t size, u8_t *src) {
+ my_spi_write(addr, size, dst);
+ return SPIFFS_OK;
+ }
+
+ static s32_t my_spiffs_erase(u32_t addr, u32_t size) {
+ my_spi_erase(addr, size);
+ return SPIFFS_OK;
+ }
+
+Redirect the config in my_spiffs_mount to the wrappers instead:
+
+ cfg.hal_read_f = my_spiffs_read;
+ cfg.hal_write_f = my_spiffs_write;
+ cfg.hal_erase_f = my_spiffs_erase;
+
+Ok, now you should be able to build and run. However, you get this output:
+
+ mount res: -1
+
+but you wanted
+
+ mount res: 0
+
+This is probably due to you having experimented with your SPI flash, so it
+contains rubbish from spiffs's point of view. Do a mass erase and run again.
+
+If all is ok now, you're good to go. Try creating a file and read it back:
+
+ static void test_spiffs() {
+ char buf[12];
+
+ // Surely, I've mounted spiffs before entering here
+
+ spiffs_file fd = SPIFFS_open(&fs, "my_file", SPIFFS_CREAT | SPIFFS_TRUNC | SPIFFS_RDWR, 0);
+ if (SPIFFS_write(&fs, fd, (u8_t *)"Hello world", 12) < 0) printf("errno %i\n", SPIFFS_errno(&fs));
+ SPIFFS_close(&fs, fd);
+
+ fd = SPIFFS_open(&fs, "my_file", SPIFFS_RDWR, 0);
+ if (SPIFFS_read(&fs, fd, (u8_t *)buf, 12) < 0) printf("errno %i\n", SPIFFS_errno(&fs));
+ SPIFFS_close(&fs, fd);
+
+ printf("--> %s <--\n", buf);
+ }
+
+Compile, run, cross fingers hard, and you'll get the output:
+
+ --> Hello world <--
+
+Got errors? Check spiffs.h for error definitions to get a clue what went voodoo.
+
+
+* INTEGRATING SPIFFS
+
+In order to integrate spiffs to your embedded target, you will basically need:
+ - A SPI flash device which your processor can communicate with
+ - An implementation for reading, writing and erasing the flash
+ - Memory (flash or ram) for the code
+ - Memory (ram) for the stack
+
+Other stuff may be needed, threaded systems might need mutexes and so on.
+
+** Logical structure
+
+First and foremost, one must decide how to divide up the SPI flash for spiffs.
+Having the datasheet for the actual SPI flash in hand will help. Spiffs can be
+defined to use all or only parts of the SPI flash.
+
+If following seems arcane, read the "HOW TO CONFIG" chapter first.
+
+ - Decide the logical size of blocks. This must be a multiple of the biggest
+ physical SPI flash block size. To go safe, use the physical block size -
+ which in many cases is 65536 bytes.
+ - Decide the logical size of pages. This must be a 2nd logarithm part of the
+ logical block size. To go safe, use 256 bytes to start with.
+ - Decide how much of the SPI flash memory to be used for spiffs. This must be
+ on logical block boundary. If unsafe, use 1 megabyte to start with.
+ - Decide where on the SPI flash memory the spiffs area should start. This must
+ be on physical block/sector boundary. If unsafe, use address 0.
+
+** SPI flash API
+
+The target must provide three functions to spiffs:
+
+ - s32_t (*spiffs_read)(u32_t addr, u32_t size, u8_t *dst)
+ - s32_t (*spiffs_write)(u32_t addr, u32_t size, u8_t *src)
+ - s32_t (*spiffs_erase)(u32_t addr, u32_t size)
+
+These functions define the only communication between the SPI flash and the
+spiffs stack.
+
+On success these must return 0 (or SPIFFS_OK). Anything else will be considered
+an error.
+
+The size for read and write requests will never exceed the logical page size,
+but it may be less.
+
+The address and size on erase requests will always be on physical block size
+boundaries.
+
+** Mount specification
+
+In spiffs.h, there is a SPIFFS_mount function defined, used to mount spiffs on
+the SPI flash.
+
+s32_t SPIFFS_mount(
+ spiffs *fs,
+ spiffs_config *config,
+ u8_t *work,
+ u8_t *fd_space,
+ u32_t fd_space_size,
+ void *cache,
+ u32_t cache_size,
+ spiffs_check_callback check_cb_f)
+
+ - fs Points to a spiffs struct. This may be totally uninitialized.
+ - config Points to a spiffs_config struct. This struct must be
+ initialized when mounting. See below.
+ - work A ram memory buffer being double the size of the logical page
+ size. This buffer is used excessively by the spiffs stack. If
+ logical page size is 256, this buffer must be 512 bytes.
+ - fd_space A ram memory buffer used for file descriptors.
+ - fd_space_size The size of the file descriptor buffer. A file descriptor
+ normally is around 32 bytes depending on the build config -
+ the bigger the buffer, the more file descriptors are
+ available.
+ - cache A ram memory buffer used for cache. Ignored if cache is
+ disabled in build config.
+ - cache_size The size of the cache buffer. Ignored if cache is disabled in
+ build config. One cache page will be slightly larger than the
+ logical page size. The more ram, the more cache pages, the
+ quicker the system.
+ - check_cb_f Callback function for monitoring spiffs consistency checks and
+ mending operations. May be null.
+
+The config struct must be initialized prior to mounting. One must always
+define the SPI flash access functions:
+
+ spiffs_config.hal_read_f - pointing to the function reading the SPI flash
+
+ spiffs_config.hal_write_f - pointing to the function writing the SPI flash
+
+ spiffs_config.hal_erase_f - pointing to the function erasing the SPI flash
+
+Depending on the build config - if SPIFFS_SINGLETON is set to zero - following
+parameters must be defined:
+
+ spiffs_config.phys_size - the physical number of bytes accounted for
+ spiffs on the SPI flash
+
+ spiffs_config.phys_addr - the physical starting address on the SPI flash
+
+ spiffs_config.phys_erase_block - the physical size of the largest block/sector
+ on the SPI flash found within the spiffs
+ usage address space
+
+ spiffs_config.log_block_size - the logical size of a spiffs block
+
+ spiffs_config.log_page_size - the logical size of a spiffs page
+
+If SPIFFS_SINGLETON is set to one, above parameters must be set ny defines in
+the config header file, spiffs_config.h.
+
+
+** Build config
+
+makefile: The files needed to be compiled to your target resides in files.mk to
+be included in your makefile, either by cut and paste or by inclusion.
+
+Types: spiffs uses the types u8_t, s8_t, u16_t, s16_t, u32_t, s32_t; these must
+be typedeffed.
+
+spiffs_config.h: you also need to define a spiffs_config.h header. Example of
+this is found in the default/ directory.
+
+
+** RAM
+
+Spiffs needs ram. It needs a working buffer being double the size of the
+logical page size. It also needs at least one file descriptor. If cache is
+enabled (highly recommended), it will also need a bunch of cache pages.
+
+Say you have a logical page size of 256 bytes. You want to be able to have four
+files open simultaneously, and you can give spiffs four cache pages. This
+roughly sums up to:
+
+256*2 (work buffer) +
+32*4 (file descriptors) +
+(256+32)*4 (cache pages) + 40 (cache metadata)
+
+i.e. 1832 bytes.
+
+This is apart from call stack usage.
+
+To get the exact amount of bytes needed on your specific target, enable
+SPIFFS_BUFFER_HELP in spiffs_config.h, rebuild and call:
+
+ SPIFFS_buffer_bytes_for_filedescs
+ SPIFFS_buffer_bytes_for_cache
+
+Having these figures you can disable SPIFFS_BUFFER_HELP again to save flash.
+
+
+* HOW TO CONFIG
+
+TODO
\ No newline at end of file
diff --git a/app/spiffs/docs/TECH_SPEC b/app/spiffs/docs/TECH_SPEC
new file mode 100644
index 00000000..b4755a6d
--- /dev/null
+++ b/app/spiffs/docs/TECH_SPEC
@@ -0,0 +1,239 @@
+* USING SPIFFS
+
+TODO
+
+
+* SPIFFS DESIGN
+
+Spiffs is inspired by YAFFS. However, YAFFS is designed for NAND flashes, and
+for bigger targets with much more ram. Nevertheless, many wise thoughts have
+been borrowed from YAFFS when writing spiffs. Kudos!
+
+The main complication writing spiffs was that it cannot be assumed the target
+has a heap. Spiffs must go along only with the work ram buffer given to it.
+This forces extra implementation on many areas of spiffs.
+
+
+** SPI flash devices using NOR technology
+
+Below is a small description of how SPI flashes work internally. This is to
+give an understanding of the design choices made in spiffs.
+
+SPI flash devices are physically divided in blocks. On some SPI flash devices,
+blocks are further divided into sectors. Datasheets sometimes name blocks as
+sectors and vice versa.
+
+Common memory capacaties for SPI flashes are 512kB up to 8MB of data, where
+blocks may be 64kB. Sectors can be e.g. 4kB, if supported. Many SPI flashes
+have uniform block sizes, whereas others have non-uniform - the latter meaning
+that e.g. the first 16 blocks are 4kB big, and the rest are 64kB.
+
+The entire memory is linear and can be read and written in random access.
+Erasing can only be done block- or sectorwise; or by mass erase.
+
+SPI flashes can normally be erased from 100.000 up to 1.000.000 cycles before
+they fail.
+
+A clean SPI flash from factory have all bits in entire memory set to one. A
+mass erase will reset the device to this state. Block or sector erasing will
+put the all bits in the area given by the sector or block to ones. Writing to a
+NOR flash pulls ones to zeroes. Writing 0xFF to an address is simply a no-op.
+
+Writing 0b10101010 to a flash address holding 0b00001111 will yield 0b00001010.
+
+This way of "write by nand" is used considerably in spiffs.
+
+Common characteristics of NOR flashes are quick reads, but slow writes.
+
+And finally, unlike NAND flashes, NOR flashes seem to not need any error
+correction. They always write correctly I gather.
+
+
+** Spiffs logical structure
+
+Some terminology before proceeding. Physical blocks/sectors means sizes stated
+in the datasheet. Logical blocks and pages is something the integrator choose.
+
+
+** Blocks and pages
+
+Spiffs is allocated to a part or all of the memory of the SPI flash device.
+This area is divided into logical blocks, which in turn are divided into
+logical pages. The boundary of a logical block must coincide with one or more
+physical blocks. The sizes for logical blocks and logical pages always remain
+the same, they are uniform.
+
+Example: non-uniform flash mapped to spiffs with 128kB logical blocks
+
+PHYSICAL FLASH BLOCKS SPIFFS LOGICAL BLOCKS: 128kB
+
++-----------------------+ - - - +-----------------------+
+| Block 1 : 16kB | | Block 1 : 128kB |
++-----------------------+ | |
+| Block 2 : 16kB | | |
++-----------------------+ | |
+| Block 3 : 16kB | | |
++-----------------------+ | |
+| Block 4 : 16kB | | |
++-----------------------+ | |
+| Block 5 : 64kB | | |
++-----------------------+ - - - +-----------------------+
+| Block 6 : 64kB | | Block 2 : 128kB |
++-----------------------+ | |
+| Block 7 : 64kB | | |
++-----------------------+ - - - +-----------------------+
+| Block 8 : 64kB | | Block 3 : 128kB |
++-----------------------+ | |
+| Block 9 : 64kB | | |
++-----------------------+ - - - +-----------------------+
+| ... | | ... |
+
+A logical block is divided further into a number of logical pages. A page
+defines the smallest data holding element known to spiffs. Hence, if a file
+is created being one byte big, it will occupy one page for index and one page
+for data - it will occupy 2 x size of a logical page on flash.
+So it seems it is good to select a small page size.
+
+Each page has a metadata header being normally 5 to 9 bytes. This said, a very
+small page size will make metadata occupy a lot of the memory on the flash. A
+page size of 64 bytes will waste 8-14% on metadata, while 256 bytes 2-4%.
+So it seems it is good to select a big page size.
+
+Also, spiffs uses a ram buffer being two times the page size. This ram buffer
+is used for loading and manipulating pages, but it is also used for algorithms
+to find free file ids, scanning the file system, etc. Having too small a page
+size means less work buffer for spiffs, ending up in more reads operations and
+eventually gives a slower file system.
+
+Choosing the page size for the system involves many factors:
+ - How big is the logical block size
+ - What is the normal size of most files
+ - How much ram can be spent
+ - How much data (vs metadata) must be crammed into the file system
+ - How fast must spiffs be
+ - Other things impossible to find out
+
+So, chosing the Optimal Page Size (tm) seems tricky, to say the least. Don't
+fret - there is no optimal page size. This varies from how the target will use
+spiffs. Use the golden rule:
+
+ ~~~ Logical Page Size = Logical Block Size / 256 ~~~
+
+This is a good starting point. The final page size can then be derived through
+heuristical experimenting for us non-analytical minds.
+
+
+** Objects, indices and look-ups
+
+A file, or an object as called in spiffs, is identified by an object id.
+Another YAFFS rip-off. This object id is a part of the page header. So, all
+pages know to which object/file they belong - not counting the free pages.
+
+An object is made up of two types of pages: object index pages and data pages.
+Data pages contain the data written by user. Index pages contain metadata about
+the object, more specifically what data pages are part of the object.
+
+The page header also includes something called a span index. Let's say a file
+is written covering three data pages. The first data page will then have span
+index 0, the second span index 1, and the last data page will have span index
+2. Simple as that.
+
+Finally, each page header contain flags, telling if the page is used,
+deleted, finalized, holds index or data, and more.
+
+Object indices also have span indices, where an object index with span index 0
+is referred to as the object index header. This page does not only contain
+references to data pages, but also extra info such as object name, object size
+in bytes, flags for file or directory, etc.
+
+If one were to create a file covering three data pages, named e.g.
+"spandex-joke.txt", given object id 12, it could look like this:
+
+PAGE 0
+
+PAGE 1 page header: [obj_id:12 span_ix:0 flags:USED|DATA]
+
+
+PAGE 2 page header: [obj_id:12 span_ix:1 flags:USED|DATA]
+
+
+PAGE 3 page header: [obj_id:545 span_ix:13 flags:USED|DATA]
+
+
+PAGE 4 page header: [obj_id:12 span_ix:2 flags:USED|DATA]
+
+
+PAGE 5 page header: [obj_id:12 span_ix:0 flags:USED|INDEX]
+ obj ix header: [name:spandex-joke.txt size:600 bytes flags:FILE]
+ obj ix: [1 2 4]
+
+Looking in detail at page 5, the object index header page, the object index
+array refers to each data page in order, as mentioned before. The index of the
+object index array correlates with the data page span index.
+
+ entry ix: 0 1 2
+ obj ix: [1 2 4]
+ | | |
+ PAGE 1, DATA, SPAN_IX 0 --------/ | |
+ PAGE 2, DATA, SPAN_IX 1 --------/ |
+ PAGE 4, DATA, SPAN_IX 2 --------/
+
+Things to be unveiled in page 0 - well.. Spiffs is designed for systems low on
+ram. We cannot keep a dynamic list on the whereabouts of each object index
+header so we can find a file fast. There might not even be a heap! But, we do
+not want to scan all page headers on the flash to find the object index header.
+
+The first page(s) of each block contains the so called object look-up. These
+are not normal pages, they do not have a header. Instead, they are arrays
+pointing out what object-id the rest of all pages in the block belongs to.
+
+By this look-up, only the first page(s) in each block must to scanned to find
+the actual page which contains the object index header of the desired object.
+
+The object lookup is redundant metadata. The assumption is that it presents
+less overhead reading a full page of data to memory from each block and search
+that, instead of reading a small amount of data from each page (i.e. the page
+header) in all blocks. Each read operation from SPI flash normally contains
+extra data as the read command itself and the flash address. Also, depending on
+the underlying implementation, other criterions may need to be passed for each
+read transaction, like mutexes and such.
+
+The veiled example unveiled would look like this, with some extra pages:
+
+PAGE 0 [ 12 12 545 12 12 34 34 4 0 0 0 0 ...]
+PAGE 1 page header: [obj_id:12 span_ix:0 flags:USED|DATA] ...
+PAGE 2 page header: [obj_id:12 span_ix:1 flags:USED|DATA] ...
+PAGE 3 page header: [obj_id:545 span_ix:13 flags:USED|DATA] ...
+PAGE 4 page header: [obj_id:12 span_ix:2 flags:USED|DATA] ...
+PAGE 5 page header: [obj_id:12 span_ix:0 flags:USED|INDEX] ...
+PAGE 6 page header: [obj_id:34 span_ix:0 flags:USED|DATA] ...
+PAGE 7 page header: [obj_id:34 span_ix:1 flags:USED|DATA] ...
+PAGE 8 page header: [obj_id:4 span_ix:1 flags:USED|INDEX] ...
+PAGE 9 page header: [obj_id:23 span_ix:0 flags:DELETED|INDEX] ...
+PAGE 10 page header: [obj_id:23 span_ix:0 flags:DELETED|DATA] ...
+PAGE 11 page header: [obj_id:23 span_ix:1 flags:DELETED|DATA] ...
+PAGE 12 page header: [obj_id:23 span_ix:2 flags:DELETED|DATA] ...
+...
+
+Ok, so why are page 9 to 12 marked as 0 when they belong to object id 23? These
+pages are deleted, so this is marked both in page header flags and in the look
+up. This is an example where spiffs uses NOR flashes "nand-way" of writing.
+
+As a matter of fact, there are two object id's which are special:
+
+obj id 0 (all bits zeroes) - indicates a deleted page in object look up
+obj id 0xff.. (all bits ones) - indicates a free page in object look up
+
+Actually, the object id's have another quirk: if the most significant bit is
+set, this indicates an object index page. If the most significant bit is zero,
+this indicates a data page. So to be fully correct, page 0 in above example
+would look like this:
+
+PAGE 0 [ 12 12 545 12 *12 34 34 *4 0 0 0 0 ...]
+
+where the asterisk means the msb of the object id is set.
+
+This is another way to speed up the searches when looking for object indices.
+By looking on the object id's msb in the object lookup, it is also possible
+to find out whether the page is an object index page or a data page.
+
diff --git a/app/spiffs/docs/TODO b/app/spiffs/docs/TODO
new file mode 100644
index 00000000..88709f02
--- /dev/null
+++ b/app/spiffs/docs/TODO
@@ -0,0 +1 @@
+* When mending lost pages, also see if they fit into length specified in object index header
\ No newline at end of file
diff --git a/app/spiffs/params_test.h b/app/spiffs/params_test.h
new file mode 100644
index 00000000..38809167
--- /dev/null
+++ b/app/spiffs/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/spiffs.c b/app/spiffs/spiffs.c
new file mode 100644
index 00000000..c4fa2b14
--- /dev/null
+++ b/app/spiffs/spiffs.c
@@ -0,0 +1,159 @@
+#include "c_stdio.h"
+#include "platform.h"
+#include "spiffs.h"
+
+spiffs fs;
+
+#define LOG_PAGE_SIZE 256
+
+static u8_t spiffs_work_buf[LOG_PAGE_SIZE*2];
+static u8_t spiffs_fds[32*4];
+static u8_t spiffs_cache[(LOG_PAGE_SIZE+32)*4];
+
+static s32_t ICACHE_FLASH_ATTR my_spiffs_read(u32_t addr, u32_t size, u8_t *dst) {
+ platform_flash_read(dst, addr, size);
+ return SPIFFS_OK;
+}
+
+static s32_t ICACHE_FLASH_ATTR my_spiffs_write(u32_t addr, u32_t size, u8_t *src) {
+ platform_flash_write(src, addr, size);
+ return SPIFFS_OK;
+}
+
+static s32_t ICACHE_FLASH_ATTR my_spiffs_erase(u32_t addr, u32_t size) {
+ u32_t sect_first = platform_flash_get_sector_of_address(addr);
+ u32_t sect_last = sect_first;
+ while( sect_first <= sect_last )
+ if( platform_flash_erase_sector( sect_first ++ ) == PLATFORM_ERR )
+ return SPIFFS_ERR_INTERNAL;
+ return SPIFFS_OK;
+}
+
+void myspiffs_check_callback(spiffs_check_type type, spiffs_check_report report, u32_t arg1, u32_t arg2){
+ // if(SPIFFS_CHECK_PROGRESS == report) return;
+ // NODE_ERR("type: %d, report: %d, arg1: %d, arg2: %d\n", type, report, arg1, arg2);
+}
+
+/*******************
+The W25Q32BV array is organized into 16,384 programmable pages of 256-bytes each. Up to 256 bytes can be programmed at a time.
+Pages can be erased in groups of 16 (4KB sector erase), groups of 128 (32KB block erase), groups of 256 (64KB block erase) or
+the entire chip (chip erase). The W25Q32BV has 1,024 erasable sectors and 64 erasable blocks respectively.
+The small 4KB sectors allow for greater flexibility in applications that require data and parameter storage.
+
+********************/
+
+void ICACHE_FLASH_ATTR spiffs_mount() {
+ spiffs_config cfg;
+ cfg.phys_addr = ( u32_t )platform_flash_get_first_free_block_address( NULL );
+ cfg.phys_size = INTERNAL_FLASH_SIZE - ( ( u32_t )cfg.phys_addr - INTERNAL_FLASH_START_ADDRESS );
+ cfg.phys_erase_block = INTERNAL_FLASH_SECTOR_SIZE; // according to datasheet
+ cfg.log_block_size = INTERNAL_FLASH_SECTOR_SIZE; // let us not complicate things
+ cfg.log_page_size = LOG_PAGE_SIZE; // as we said
+ NODE_DBG("fs.start:%x,max:%x\n",cfg.phys_addr,cfg.phys_size);
+
+ cfg.hal_read_f = my_spiffs_read;
+ cfg.hal_write_f = my_spiffs_write;
+ cfg.hal_erase_f = my_spiffs_erase;
+
+ int res = SPIFFS_mount(&fs,
+ &cfg,
+ spiffs_work_buf,
+ spiffs_fds,
+ sizeof(spiffs_fds),
+ spiffs_cache,
+ sizeof(spiffs_cache),
+ // myspiffs_check_callback);
+ 0);
+ NODE_DBG("mount res: %i\n", res);
+}
+
+// FS formatting function
+// Returns 1 if OK, 0 for error
+int ICACHE_FLASH_ATTR myspiffs_format( void )
+{
+ SPIFFS_unmount(&fs);
+ u32_t sect_first, sect_last;
+ sect_first = ( u32_t )platform_flash_get_first_free_block_address( NULL );
+ sect_first = platform_flash_get_sector_of_address(sect_first);
+ sect_last = INTERNAL_FLASH_SIZE + INTERNAL_FLASH_START_ADDRESS - 4;
+ sect_last = platform_flash_get_sector_of_address(sect_last);
+ NODE_DBG("sect_first: %x, sect_last: %x\n", sect_first, sect_last);
+ while( sect_first <= sect_last )
+ if( platform_flash_erase_sector( sect_first ++ ) == PLATFORM_ERR )
+ return 0;
+ return 1;
+}
+
+int ICACHE_FLASH_ATTR myspiffs_check( void )
+{
+ // ets_wdt_disable();
+ // int res = (int)SPIFFS_check(&fs);
+ // ets_wdt_enable();
+ // return res;
+}
+
+int ICACHE_FLASH_ATTR myspiffs_open(const char *name, int flags){
+ return (int)SPIFFS_open(&fs, name, (spiffs_flags)flags, 0);
+}
+
+int ICACHE_FLASH_ATTR myspiffs_close( int fd ){
+ SPIFFS_close(&fs, (spiffs_file)fd);
+ return 0;
+}
+size_t ICACHE_FLASH_ATTR myspiffs_write( int fd, const void* ptr, size_t len ){
+#if 0
+ if(fd==c_stdout || fd==c_stderr){
+ uart0_tx_buffer((u8_t*)ptr, len);
+ return len;
+ }
+#endif
+ return SPIFFS_write(&fs, (spiffs_file)fd, (void *)ptr, len);
+}
+size_t ICACHE_FLASH_ATTR myspiffs_read( int fd, void* ptr, size_t len){
+ return SPIFFS_read(&fs, (spiffs_file)fd, ptr, len);
+}
+int ICACHE_FLASH_ATTR myspiffs_lseek( int fd, int off, int whence ){
+ return SPIFFS_lseek(&fs, (spiffs_file)fd, off, whence);
+}
+int ICACHE_FLASH_ATTR myspiffs_eof( int fd ){
+ return SPIFFS_eof(&fs, (spiffs_file)fd);
+}
+int ICACHE_FLASH_ATTR myspiffs_tell( int fd ){
+ return SPIFFS_tell(&fs, (spiffs_file)fd);
+}
+int ICACHE_FLASH_ATTR myspiffs_getc( int fd ){
+ char c = EOF;
+ if(!myspiffs_eof(fd)){
+ SPIFFS_read(&fs, (spiffs_file)fd, &c, 1);
+ }
+ return (int)c;
+}
+int ICACHE_FLASH_ATTR myspiffs_ungetc( int c, int fd ){
+ return SPIFFS_lseek(&fs, (spiffs_file)fd, -1, SEEK_CUR);
+}
+int ICACHE_FLASH_ATTR myspiffs_flush( int fd ){
+ return SPIFFS_fflush(&fs, (spiffs_file)fd);
+}
+int ICACHE_FLASH_ATTR myspiffs_error( int fd ){
+ return SPIFFS_errno(&fs);
+}
+void ICACHE_FLASH_ATTR myspiffs_clearerr( int fd ){
+ fs.errno = SPIFFS_OK;
+}
+#if 0
+void ICACHE_FLASH_ATTR test_spiffs() {
+ char buf[12];
+
+ // Surely, I've mounted spiffs before entering here
+
+ spiffs_file fd = SPIFFS_open(&fs, "my_file", SPIFFS_CREAT | SPIFFS_TRUNC | SPIFFS_RDWR, 0);
+ if (SPIFFS_write(&fs, fd, (u8_t *)"Hello world", 12) < 0) NODE_DBG("errno %i\n", SPIFFS_errno(&fs));
+ SPIFFS_close(&fs, fd);
+
+ fd = SPIFFS_open(&fs, "my_file", SPIFFS_RDWR, 0);
+ if (SPIFFS_read(&fs, fd, (u8_t *)buf, 12) < 0) NODE_DBG("errno %i\n", SPIFFS_errno(&fs));
+ SPIFFS_close(&fs, fd);
+
+ NODE_DBG("--> %s <--\n", buf);
+}
+#endif
diff --git a/app/spiffs/spiffs.h b/app/spiffs/spiffs.h
new file mode 100644
index 00000000..9875590c
--- /dev/null
+++ b/app/spiffs/spiffs.h
@@ -0,0 +1,442 @@
+/*
+ * spiffs.h
+ *
+ * Created on: May 26, 2013
+ * Author: petera
+ */
+
+
+
+#ifndef SPIFFS_H_
+#define SPIFFS_H_
+#include "c_stdio.h"
+#include "spiffs_config.h"
+
+#define SPIFFS_OK 0
+#define SPIFFS_ERR_NOT_MOUNTED -10000
+#define SPIFFS_ERR_FULL -10001
+#define SPIFFS_ERR_NOT_FOUND -10002
+#define SPIFFS_ERR_END_OF_OBJECT -10003
+#define SPIFFS_ERR_DELETED -10004
+#define SPIFFS_ERR_NOT_FINALIZED -10005
+#define SPIFFS_ERR_NOT_INDEX -10006
+#define SPIFFS_ERR_OUT_OF_FILE_DESCS -10007
+#define SPIFFS_ERR_FILE_CLOSED -10008
+#define SPIFFS_ERR_FILE_DELETED -10009
+#define SPIFFS_ERR_BAD_DESCRIPTOR -10010
+#define SPIFFS_ERR_IS_INDEX -10011
+#define SPIFFS_ERR_IS_FREE -10012
+#define SPIFFS_ERR_INDEX_SPAN_MISMATCH -10013
+#define SPIFFS_ERR_DATA_SPAN_MISMATCH -10014
+#define SPIFFS_ERR_INDEX_REF_FREE -10015
+#define SPIFFS_ERR_INDEX_REF_LU -10016
+#define SPIFFS_ERR_INDEX_REF_INVALID -10017
+#define SPIFFS_ERR_INDEX_FREE -10018
+#define SPIFFS_ERR_INDEX_LU -10019
+#define SPIFFS_ERR_INDEX_INVALID -10020
+#define SPIFFS_ERR_NOT_WRITABLE -10021
+#define SPIFFS_ERR_NOT_READABLE -10022
+
+#define SPIFFS_ERR_INTERNAL -10050
+
+#define SPIFFS_ERR_TEST -10100
+
+
+// spiffs file descriptor index type. must be signed
+typedef s16_t spiffs_file;
+// spiffs file descriptor flags
+typedef u16_t spiffs_flags;
+// spiffs file mode
+typedef u16_t spiffs_mode;
+// object type
+typedef u8_t spiffs_obj_type;
+
+/* spi read call function type */
+typedef s32_t (*spiffs_read)(u32_t addr, u32_t size, u8_t *dst);
+/* spi write call function type */
+typedef s32_t (*spiffs_write)(u32_t addr, u32_t size, u8_t *src);
+/* spi erase call function type */
+typedef s32_t (*spiffs_erase)(u32_t addr, u32_t size);
+
+/* file system check callback report operation */
+typedef enum {
+ SPIFFS_CHECK_LOOKUP = 0,
+ SPIFFS_CHECK_INDEX,
+ SPIFFS_CHECK_PAGE
+} spiffs_check_type;
+
+/* file system check callback report type */
+typedef enum {
+ SPIFFS_CHECK_PROGRESS = 0,
+ SPIFFS_CHECK_ERROR,
+ SPIFFS_CHECK_FIX_INDEX,
+ SPIFFS_CHECK_FIX_LOOKUP,
+ SPIFFS_CHECK_DELETE_ORPHANED_INDEX,
+ SPIFFS_CHECK_DELETE_PAGE,
+ SPIFFS_CHECK_DELETE_BAD_FILE,
+} spiffs_check_report;
+
+/* file system check callback function */
+typedef void (*spiffs_check_callback)(spiffs_check_type type, spiffs_check_report report,
+ u32_t arg1, u32_t arg2);
+
+#ifndef SPIFFS_DBG
+#define SPIFFS_DBG(...) \
+ print(__VA_ARGS__)
+#endif
+#ifndef SPIFFS_GC_DBG
+#define SPIFFS_GC_DBG(...) printf(__VA_ARGS__)
+#endif
+#ifndef SPIFFS_CACHE_DBG
+#define SPIFFS_CACHE_DBG(...) printf(__VA_ARGS__)
+#endif
+#ifndef SPIFFS_CHECK_DBG
+#define SPIFFS_CHECK_DBG(...) printf(__VA_ARGS__)
+#endif
+
+/* Any write to the filehandle is appended to end of the file */
+#define SPIFFS_APPEND (1<<0)
+/* If the opened file exists, it will be truncated to zero length before opened */
+#define SPIFFS_TRUNC (1<<1)
+/* If the opened file does not exist, it will be created before opened */
+#define SPIFFS_CREAT (1<<2)
+/* The opened file may only be read */
+#define SPIFFS_RDONLY (1<<3)
+/* The opened file may only be writted */
+#define SPIFFS_WRONLY (1<<4)
+/* The opened file may be both read and writted */
+#define SPIFFS_RDWR (SPIFFS_RDONLY | SPIFFS_WRONLY)
+/* Any writes to the filehandle will never be cached */
+#define SPIFFS_DIRECT (1<<5)
+
+#define SPIFFS_SEEK_SET (0)
+#define SPIFFS_SEEK_CUR (1)
+#define SPIFFS_SEEK_END (2)
+
+#define SPIFFS_TYPE_FILE (1)
+#define SPIFFS_TYPE_DIR (2)
+#define SPIFFS_TYPE_HARD_LINK (3)
+#define SPIFFS_TYPE_SOFT_LINK (4)
+
+#ifndef SPIFFS_LOCK
+#define SPIFFS_LOCK(fs)
+#endif
+
+#ifndef SPIFFS_UNLOCK
+#define SPIFFS_UNLOCK(fs)
+#endif
+
+// phys structs
+
+// spiffs spi configuration struct
+typedef struct {
+ // physical read function
+ spiffs_read hal_read_f;
+ // physical write function
+ spiffs_write hal_write_f;
+ // physical erase function
+ spiffs_erase hal_erase_f;
+#if SPIFFS_SINGLETON == 0
+ // physical size of the spi flash
+ u32_t phys_size;
+ // physical offset in spi flash used for spiffs,
+ // must be on block boundary
+ u32_t phys_addr;
+ // physical size when erasing a block
+ u32_t phys_erase_block;
+
+ // logical size of a block, must be on physical
+ // block size boundary and must never be less than
+ // a physical block
+ u32_t log_block_size;
+ // logical size of a page, must be at least
+ // log_block_size / 8
+ u32_t log_page_size;
+#endif
+} spiffs_config;
+
+typedef struct {
+ // file system configuration
+ spiffs_config cfg;
+ // number of logical blocks
+ u32_t block_count;
+
+ // cursor for free blocks, block index
+ spiffs_block_ix free_cursor_block_ix;
+ // cursor for free blocks, entry index
+ int free_cursor_obj_lu_entry;
+ // cursor when searching, block index
+ spiffs_block_ix cursor_block_ix;
+ // cursor when searching, entry index
+ int cursor_obj_lu_entry;
+
+ // primary work buffer, size of a logical page
+ u8_t *lu_work;
+ // secondary work buffer, size of a logical page
+ u8_t *work;
+ // file descriptor memory area
+ u8_t *fd_space;
+ // available file descriptors
+ u32_t fd_count;
+
+ // last error
+ s32_t errno;
+
+ // current number of free blocks
+ u32_t free_blocks;
+ // current number of busy pages
+ u32_t stats_p_allocated;
+ // current number of deleted pages
+ u32_t stats_p_deleted;
+ // flag indicating that garbage collector is cleaning
+ u8_t cleaning;
+ // max erase count amongst all blocks
+ spiffs_obj_id max_erase_count;
+
+#if SPIFFS_GC_STATS
+ u32_t stats_gc_runs;
+#endif
+
+#if SPIFFS_CACHE
+ // cache memory
+ void *cache;
+ // cache size
+ u32_t cache_size;
+#if SPIFFS_CACHE_STATS
+ u32_t cache_hits;
+ u32_t cache_misses;
+#endif
+#endif
+
+ // check callback function
+ spiffs_check_callback check_cb_f;
+} spiffs;
+
+/* spiffs file status struct */
+typedef struct {
+ spiffs_obj_id obj_id;
+ u32_t size;
+ spiffs_obj_type type;
+ u8_t name[SPIFFS_OBJ_NAME_LEN];
+} spiffs_stat;
+
+struct spiffs_dirent {
+ spiffs_obj_id obj_id;
+ u8_t name[SPIFFS_OBJ_NAME_LEN];
+ spiffs_obj_type type;
+ u32_t size;
+};
+
+typedef struct {
+ spiffs *fs;
+ spiffs_block_ix block;
+ int entry;
+} spiffs_DIR;
+
+// functions
+
+/**
+ * Initializes the file system dynamic parameters and mounts the filesystem
+ * @param fs the file system struct
+ * @param config the physical and logical configuration of the file system
+ * @param work a memory work buffer comprising 2*config->log_page_size
+ * bytes used throughout all file system operations
+ * @param fd_space memory for file descriptors
+ * @param fd_space_size memory size of file descriptors
+ * @param cache memory for cache, may be null
+ * @param cache_size memory size of cache
+ * @param check_cb_f callback function for reporting during consistency checks
+ */
+s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work,
+ u8_t *fd_space, u32_t fd_space_size,
+ void *cache, u32_t cache_size,
+ spiffs_check_callback check_cb_f);
+
+/**
+ * Unmounts the file system. All file handles will be flushed of any
+ * cached writes and closed.
+ * @param fs the file system struct
+ */
+void SPIFFS_unmount(spiffs *fs);
+
+/**
+ * Creates a new file.
+ * @param fs the file system struct
+ * @param path the path of the new file
+ * @param mode ignored, for posix compliance
+ */
+s32_t SPIFFS_creat(spiffs *fs, const char *path, spiffs_mode mode);
+
+/**
+ * Opens/creates a file.
+ * @param fs the file system struct
+ * @param path the path of the new file
+ * @param flags the flags for the open command, can be combinations of
+ * SPIFFS_APPEND, SPIFFS_TRUNC, SPIFFS_CREAT, SPIFFS_RD_ONLY,
+ * SPIFFS_WR_ONLY, SPIFFS_RDWR, SPIFFS_DIRECT
+ * @param mode ignored, for posix compliance
+ */
+spiffs_file SPIFFS_open(spiffs *fs, const char *path, spiffs_flags flags, spiffs_mode mode);
+
+/**
+ * Reads from given filehandle.
+ * @param fs the file system struct
+ * @param fh the filehandle
+ * @param buf where to put read data
+ * @param len how much to read
+ * @returns number of bytes read, or -1 if error
+ */
+s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, u32_t len);
+
+/**
+ * Writes to given filehandle.
+ * @param fs the file system struct
+ * @param fh the filehandle
+ * @param buf the data to write
+ * @param len how much to write
+ * @returns number of bytes written, or -1 if error
+ */
+s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, u32_t len);
+
+/**
+ * Moves the read/write file offset
+ * @param fs the file system struct
+ * @param fh the filehandle
+ * @param offs how much/where to move the offset
+ * @param whence if SPIFFS_SEEK_SET, the file offset shall be set to offset bytes
+ * if SPIFFS_SEEK_CUR, the file offset shall be set to its current location plus offset
+ * if SPIFFS_SEEK_END, the file offset shall be set to the size of the file plus offset
+ */
+s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence);
+
+/**
+ * Removes a file by path
+ * @param fs the file system struct
+ * @param path the path of the file to remove
+ */
+s32_t SPIFFS_remove(spiffs *fs, const char *path);
+
+/**
+ * Removes a file by filehandle
+ * @param fs the file system struct
+ * @param fh the filehandle of the file to remove
+ */
+s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh);
+
+/**
+ * Gets file status by path
+ * @param fs the file system struct
+ * @param path the path of the file to stat
+ * @param s the stat struct to populate
+ */
+s32_t SPIFFS_stat(spiffs *fs, const char *path, spiffs_stat *s);
+
+/**
+ * Gets file status by filehandle
+ * @param fs the file system struct
+ * @param fh the filehandle of the file to stat
+ * @param s the stat struct to populate
+ */
+s32_t SPIFFS_fstat(spiffs *fs, spiffs_file fh, spiffs_stat *s);
+
+/**
+ * Flushes all pending write operations from cache for given file
+ * @param fs the file system struct
+ * @param fh the filehandle of the file to flush
+ */
+s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh);
+
+/**
+ * Closes a filehandle. If there are pending write operations, these are finalized before closing.
+ * @param fs the file system struct
+ * @param fh the filehandle of the file to close
+ */
+void SPIFFS_close(spiffs *fs, spiffs_file fh);
+
+/**
+ * Returns last error of last file operation.
+ * @param fs the file system struct
+ */
+s32_t SPIFFS_errno(spiffs *fs);
+
+/**
+ * Opens a directory stream corresponding to the given name.
+ * The stream is positioned at the first entry in the directory.
+ * On hydrogen builds the name argument is ignored as hydrogen builds always correspond
+ * to a flat file structure - no directories.
+ * @param fs the file system struct
+ * @param name the name of the directory
+ * @param d pointer the directory stream to be populated
+ */
+spiffs_DIR *SPIFFS_opendir(spiffs *fs, const char *name, spiffs_DIR *d);
+
+/**
+ * Closes a directory stream
+ * @param d the directory stream to close
+ */
+s32_t SPIFFS_closedir(spiffs_DIR *d);
+
+/**
+ * Reads a directory into given spifs_dirent struct.
+ * @param d pointer to the directory stream
+ * @param e the dirent struct to be populated
+ * @returns null if error or end of stream, else given dirent is returned
+ */
+struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e);
+
+/**
+ * Runs a consistency check on given filesystem.
+ * @param fs the file system struct
+ */
+s32_t SPIFFS_check(spiffs *fs);
+
+/**
+ * Check if EOF reached.
+ * @param fs the file system struct
+ * @param fh the filehandle of the file to check
+ */
+s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh);
+s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh);
+
+#if SPIFFS_TEST_VISUALISATION
+/**
+ * Prints out a visualization of the filesystem.
+ * @param fs the file system struct
+ */
+s32_t SPIFFS_vis(spiffs *fs);
+#endif
+
+#if SPIFFS_BUFFER_HELP
+/**
+ * Returns number of bytes needed for the filedescriptor buffer given
+ * amount of file descriptors.
+ */
+u32_t SPIFFS_buffer_bytes_for_filedescs(spiffs *fs, u32_t num_descs);
+
+#if SPIFFS_CACHE
+/**
+ * Returns number of bytes needed for the cache buffer given
+ * amount of cache pages.
+ */
+u32_t SPIFFS_buffer_bytes_for_cache(spiffs *fs, u32_t num_pages);
+#endif
+#endif
+
+#if SPIFFS_CACHE
+#endif
+
+int myspiffs_open(const char *name, int flags);
+int myspiffs_close( int fd );
+size_t myspiffs_write( int fd, const void* ptr, size_t len );
+size_t myspiffs_read( int fd, void* ptr, size_t len);
+int myspiffs_lseek( int fd, int off, int whence );
+int myspiffs_eof( int fd );
+int myspiffs_tell( int fd );
+int myspiffs_getc( int fd );
+int myspiffs_ungetc( int c, int fd );
+int myspiffs_flush( int fd );
+int myspiffs_error( int fd );
+void myspiffs_clearerr( int fd );
+int myspiffs_check( void );
+
+#endif /* SPIFFS_H_ */
diff --git a/app/spiffs/spiffs_cache.c b/app/spiffs/spiffs_cache.c
new file mode 100644
index 00000000..d56823cc
--- /dev/null
+++ b/app/spiffs/spiffs_cache.c
@@ -0,0 +1,301 @@
+/*
+ * spiffs_cache.c
+ *
+ * Created on: Jun 23, 2013
+ * Author: petera
+ */
+
+#include "spiffs.h"
+#include "spiffs_nucleus.h"
+
+#if SPIFFS_CACHE
+
+// returns cached page for give page index, or null if no such cached page
+static spiffs_cache_page *ICACHE_FLASH_ATTR spiffs_cache_page_get(spiffs *fs, spiffs_page_ix pix) {
+ spiffs_cache *cache = spiffs_get_cache(fs);
+ if ((cache->cpage_use_map & cache->cpage_use_mask) == 0) return 0;
+ int i;
+ for (i = 0; i < cache->cpage_count; i++) {
+ spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i);
+ if ((cache->cpage_use_map & (1<flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 &&
+ cp->pix == pix ) {
+ SPIFFS_CACHE_DBG("CACHE_GET: have cache page %i for %04x\n", i, pix);
+ cp->last_access = cache->last_access;
+ return cp;
+ }
+ }
+ //SPIFFS_CACHE_DBG("CACHE_GET: no cache for %04x\n", pix);
+ return 0;
+}
+
+// frees cached page
+static s32_t ICACHE_FLASH_ATTR spiffs_cache_page_free(spiffs *fs, int ix, u8_t write_back) {
+ s32_t res = SPIFFS_OK;
+ spiffs_cache *cache = spiffs_get_cache(fs);
+ spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, ix);
+ if (cache->cpage_use_map & (1<flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 &&
+ (cp->flags & SPIFFS_CACHE_FLAG_DIRTY)) {
+ u8_t *mem = spiffs_get_cache_page(fs, cache, ix);
+ res = fs->cfg.hal_write_f(SPIFFS_PAGE_TO_PADDR(fs, cp->pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), mem);
+ }
+
+ cp->flags = 0;
+ cache->cpage_use_map &= ~(1 << ix);
+
+ if (cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) {
+ SPIFFS_CACHE_DBG("CACHE_FREE: free cache page %i objid %04x\n", ix, cp->obj_id);
+ } else {
+ SPIFFS_CACHE_DBG("CACHE_FREE: free cache page %i pix %04x\n", ix, cp->pix);
+ }
+ }
+
+ return res;
+}
+
+// removes the oldest accessed cached page
+static s32_t ICACHE_FLASH_ATTR spiffs_cache_page_remove_oldest(spiffs *fs, u8_t flag_mask, u8_t flags) {
+ s32_t res = SPIFFS_OK;
+ spiffs_cache *cache = spiffs_get_cache(fs);
+
+ if ((cache->cpage_use_map & cache->cpage_use_mask) != cache->cpage_use_mask) {
+ // at least one free cpage
+ return SPIFFS_OK;
+ }
+
+ // all busy, scan thru all to find the cpage which has oldest access
+ int i;
+ int cand_ix = -1;
+ u32_t oldest_val = 0;
+ for (i = 0; i < cache->cpage_count; i++) {
+ spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i);
+ if ((cache->last_access - cp->last_access) > oldest_val &&
+ (cp->flags & flag_mask) == flags) {
+ oldest_val = cache->last_access - cp->last_access;
+ cand_ix = i;
+ }
+ }
+
+ if (cand_ix >= 0) {
+ res = spiffs_cache_page_free(fs, cand_ix, 1);
+ }
+
+ return res;
+}
+
+// allocates a new cached page and returns it, or null if all cache pages are busy
+static spiffs_cache_page *ICACHE_FLASH_ATTR spiffs_cache_page_allocate(spiffs *fs) {
+ spiffs_cache *cache = spiffs_get_cache(fs);
+ if (cache->cpage_use_map == 0xffffffff) {
+ // out of cache memory
+ return 0;
+ }
+ int i;
+ for (i = 0; i < cache->cpage_count; i++) {
+ if ((cache->cpage_use_map & (1<cpage_use_map |= (1<