Commit cdd13b1a authored by funshine's avatar funshine
Browse files

source file first commit, folder structure refact

parent 13c69796
/*
** $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; i<argc; i++)
{
if (*argv[i]!='-') /* end of options; keep it */
break;
else if (IS("--")) /* end of options; skip it */
{
++i;
if (version) ++version;
break;
}
else if (IS("-")) /* end of options; use stdin */
break;
else if (IS("-l")) /* list */
++listing;
else if (IS("-o")) /* output file */
{
output=argv[++i];
if (output==NULL || *output==0) usage(LUA_QL("-o") " needs argument");
if (IS("-")) output=NULL;
}
else if (IS("-p")) /* parse only */
dumping=0;
else if (IS("-s")) /* strip debug information */
stripping=1;
else if (IS("-v")) /* show version */
++version;
else if (IS("-cci")) /* target integer size */
{
int s = target.sizeof_int = atoi(argv[++i])/8;
if (!(s==1 || s==2 || s==4)) fatal(LUA_QL("-cci") " must be 8, 16 or 32");
}
else if (IS("-ccn")) /* target lua_Number type and size */
{
const char *type=argv[++i];
if (strcmp(type,"int")==0) target.lua_Number_integral=1;
else if (strcmp(type,"float")==0) target.lua_Number_integral=0;
else if (strcmp(type,"float_arm")==0)
{
target.lua_Number_integral=0;
target.is_arm_fpa=1;
}
else fatal(LUA_QL("-ccn") " type must be " LUA_QL("int") " or " LUA_QL("float") " or " LUA_QL("float_arm"));
int s = target.sizeof_lua_Number = atoi(argv[++i])/8;
if (target.lua_Number_integral && !(s==1 || s==2 || s==4)) fatal(LUA_QL("-ccn") " size must be 8, 16, or 32 for int");
if (!target.lua_Number_integral && !(s==4 || s==8)) fatal(LUA_QL("-ccn") " size must be 32 or 64 for float");
}
else if (IS("-cce")) /* target endianness */
{
const char *val=argv[++i];
if (strcmp(val,"big")==0) target.little_endian=0;
else if (strcmp(val,"little")==0) target.little_endian=1;
else fatal(LUA_QL("-cce") " must be " LUA_QL("big") " or " LUA_QL("little"));
}
else /* unknown option */
usage(argv[i]);
}
if (i==argc && (listing || !dumping))
{
dumping=0;
argv[--i]=Output;
}
if (version)
{
printf("%s %s\n",LUA_RELEASE,LUA_COPYRIGHT);
if (version==argc-1) exit(EXIT_SUCCESS);
}
return i;
}
#define toproto(L,i) (clvalue(L->top+(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; i<n; i++)
{
f->p[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; i<argc; i++)
{
const char* filename=IS("-") ? NULL : argv[i];
if (luaL_loadfile(L,filename)!=0) fatal(lua_tostring(L,-1));
}
f=combine(L,argc);
if (listing) luaU_print(f,listing>1);
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;
}
/*
** $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; i<n; i++)
{
int c=s[i];
switch (c)
{
case '"': printf("\\\""); break;
case '\\': printf("\\\\"); break;
case '\a': printf("\\a"); break;
case '\b': printf("\\b"); break;
case '\f': printf("\\f"); break;
case '\n': printf("\\n"); break;
case '\r': printf("\\r"); break;
case '\t': printf("\\t"); break;
case '\v': printf("\\v"); break;
default: if (isprint((unsigned char)c))
putchar(c);
else
printf("\\%03u",(unsigned char)c);
}
}
putchar('"');
}
static void PrintConstant(const Proto* f, int i)
{
const TValue* o=&f->k[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; pc<n; pc++)
{
Instruction i=code[pc];
OpCode o=GET_OPCODE(i);
int a=GETARG_A(i);
int b=GETARG_B(i);
int c=GETARG_C(i);
int bx=GETARG_Bx(i);
int sbx=GETARG_sBx(i);
int line=getline(f,pc);
printf("\t%d\t",pc+1);
if (line>0) 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; i<n; i++)
{
printf("\t%d\t",i+1);
PrintConstant(f,i);
printf("\n");
}
}
static void PrintLocals(const Proto* f)
{
int i,n=f->sizelocvars;
printf("locals (%d) for %p:\n",n,VOID(f));
for (i=0; i<n; i++)
{
printf("\t%d\t%s\t%d\t%d\n",
i,getstr(f->locvars[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; i<n; i++)
{
printf("\t%d\t%s\n",i,getstr(f->upvalues[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; i<n; i++) PrintFunction(f->p[i],full);
}
/*
** $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);
}
#############################################################
# 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
#############################################################
# 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
/**
* @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 <adam@sics.se>
*
*/
/* 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 <string.h>
/**
* 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 */
/**
* @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 <adam@sics.se>
*
*/
#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 <string.h>
#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 */
/**
* @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 <adam@sics.se>
*
*/
#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 */
/**
* @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 <adam@sics.se>
*
*/
#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 <string.h>
/**
* 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 */
/**
* @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 <string.h>
#include <stdlib.h>
/** 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 */
/**
* @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 */
/**
* @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 <adam@sics.se>
*
* Improved by Marc Boucher <marc@mbsi.ca> and David Haas <dhaas@alum.rpi.edu>
*
*/
#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 <string.h>
#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 */
/**
* @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 <adam@sics.se>
*
*/
#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 */
#############################################################
# 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
#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; i<q->len; 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; i<q->len; 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; i<q->len; 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; i<p->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
}
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; i<p->next->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;
}
}
}
/******************************************************************************
* 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);
}
/******************************************************************************
* 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;
}
}
/******************************************************************************
* 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;
}
/**
* @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 */
/**
* @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 */
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment